Skip to content

Commit 59d3614

Browse files
committed
Add ability to attach/detach devices to tickets.
Add attach arguments for hardware and virtual to ticket create CLI. Add new ticket attach and detach CLI commands. Add methods to ticket manager to attach and detach devices.
1 parent c1779c2 commit 59d3614

8 files changed

Lines changed: 327 additions & 10 deletions

File tree

SoftLayer/CLI/routes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@
188188
('ticket:update', 'SoftLayer.CLI.ticket.update:cli'),
189189
('ticket:subjects', 'SoftLayer.CLI.ticket.subjects:cli'),
190190
('ticket:summary', 'SoftLayer.CLI.ticket.summary:cli'),
191+
('ticket:attach', 'SoftLayer.CLI.ticket.attach:cli'),
192+
('ticket:detach', 'SoftLayer.CLI.ticket.detach:cli'),
191193

192194
('vlan', 'SoftLayer.CLI.vlan'),
193195
('vlan:detail', 'SoftLayer.CLI.vlan.detail:cli'),

SoftLayer/CLI/ticket/attach.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""Attach devices to a ticket."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
6+
import SoftLayer
7+
from SoftLayer.CLI import environment
8+
from SoftLayer.CLI import exceptions
9+
from SoftLayer.CLI import helpers
10+
11+
12+
@click.command()
13+
@click.argument('identifier', type=int)
14+
@click.option('--hardware',
15+
'hardware_identifier',
16+
help="The identifier for hardware to attach")
17+
@click.option('--virtual',
18+
'virtual_identifier',
19+
help="The identifier for a virtual server to attach")
20+
@environment.pass_env
21+
def cli(env, identifier, hardware_identifier, virtual_identifier):
22+
"""Attach devices to a ticket."""
23+
ticket_mgr = SoftLayer.TicketManager(env.client)
24+
25+
if hardware_identifier and virtual_identifier:
26+
raise exceptions.ArgumentError(
27+
"Cannot attach hardware and a virtual server at the same time")
28+
elif hardware_identifier:
29+
hardware_mgr = SoftLayer.HardwareManager(env.client)
30+
hardware_id = helpers.resolve_id(hardware_mgr.resolve_ids,
31+
hardware_identifier,
32+
'hardware')
33+
ticket_mgr.attach_hardware(identifier, hardware_id)
34+
elif virtual_identifier:
35+
vs_mgr = SoftLayer.VSManager(env.client)
36+
vs_id = helpers.resolve_id(vs_mgr.resolve_ids,
37+
virtual_identifier,
38+
'VS')
39+
ticket_mgr.attach_virtual_server(identifier, vs_id)
40+
else:
41+
raise exceptions.ArgumentError(
42+
"Must have a hardware or virtual server identifier to attach")

SoftLayer/CLI/ticket/create.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,49 @@
55

66
import SoftLayer
77
from SoftLayer.CLI import environment
8+
from SoftLayer.CLI import helpers
89
from SoftLayer.CLI import ticket
910

1011

1112
@click.command()
1213
@click.option('--title', required=True, help="The title of the ticket")
1314
@click.option('--subject-id',
15+
type=int,
1416
required=True,
1517
help="""The subject id to use for the ticket,
1618
issue 'slcli ticket subjects' to get the list""")
1719
@click.option('--body', help="The ticket body")
20+
@click.option('--hardware',
21+
'hardware_identifier',
22+
help="The identifier for hardware to attach")
23+
@click.option('--virtual',
24+
'virtual_identifier',
25+
help="The identifier for a virtual server to attach")
1826
@environment.pass_env
19-
def cli(env, title, subject_id, body):
27+
def cli(env, title, subject_id, body, hardware_identifier, virtual_identifier):
2028
"""Create a support ticket."""
21-
mgr = SoftLayer.TicketManager(env.client)
29+
ticket_mgr = SoftLayer.TicketManager(env.client)
2230

2331
if body is None:
2432
body = click.edit('\n\n' + ticket.TEMPLATE_MSG)
2533

26-
created_ticket = mgr.create_ticket(title=title,
27-
body=body,
28-
subject=subject_id)
29-
env.fout(ticket.get_ticket_results(mgr, created_ticket['id']))
34+
created_ticket = ticket_mgr.create_ticket(
35+
title=title,
36+
body=body,
37+
subject=subject_id)
38+
39+
if hardware_identifier:
40+
hardware_mgr = SoftLayer.HardwareManager(env.client)
41+
hardware_id = helpers.resolve_id(hardware_mgr.resolve_ids,
42+
hardware_identifier,
43+
'hardware')
44+
ticket_mgr.attach_hardware(created_ticket['id'], hardware_id)
45+
46+
if virtual_identifier:
47+
vs_mgr = SoftLayer.VSManager(env.client)
48+
vs_id = helpers.resolve_id(vs_mgr.resolve_ids,
49+
virtual_identifier,
50+
'VS')
51+
ticket_mgr.attach_virtual_server(created_ticket['id'], vs_id)
52+
53+
env.fout(ticket.get_ticket_results(ticket_mgr, created_ticket['id']))

SoftLayer/CLI/ticket/detach.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""Detach devices from a ticket."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
6+
import SoftLayer
7+
from SoftLayer.CLI import environment
8+
from SoftLayer.CLI import exceptions
9+
from SoftLayer.CLI import helpers
10+
11+
12+
@click.command()
13+
@click.argument('identifier', type=int)
14+
@click.option('--hardware',
15+
'hardware_identifier',
16+
help="The identifier for hardware to detach")
17+
@click.option('--virtual',
18+
'virtual_identifier',
19+
help="The identifier for a virtual server to detach")
20+
@environment.pass_env
21+
def cli(env, identifier, hardware_identifier, virtual_identifier):
22+
"""Detach devices from a ticket."""
23+
ticket_mgr = SoftLayer.TicketManager(env.client)
24+
25+
if hardware_identifier and virtual_identifier:
26+
raise exceptions.ArgumentError(
27+
"Cannot detach hardware and a virtual server at the same time")
28+
elif hardware_identifier:
29+
hardware_mgr = SoftLayer.HardwareManager(env.client)
30+
hardware_id = helpers.resolve_id(hardware_mgr.resolve_ids,
31+
hardware_identifier,
32+
'hardware')
33+
ticket_mgr.detach_hardware(identifier, hardware_id)
34+
elif virtual_identifier:
35+
vs_mgr = SoftLayer.VSManager(env.client)
36+
vs_id = helpers.resolve_id(vs_mgr.resolve_ids,
37+
virtual_identifier,
38+
'VS')
39+
ticket_mgr.detach_virtual_server(identifier, vs_id)
40+
else:
41+
raise exceptions.ArgumentError(
42+
"Must have a hardware or virtual server identifier to detach")

SoftLayer/fixtures/SoftLayer_Ticket.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,20 @@
3333
}
3434
edit = True
3535
addUpdate = {}
36+
37+
addAttachedHardware = {
38+
"id": 123,
39+
"createDate": "2013-08-01T14:14:04-07:00",
40+
"hardwareId": 1,
41+
"ticketId": 100
42+
}
43+
44+
addAttachedVirtualGuest = {
45+
"id": 123,
46+
"createDate": "2013-08-01T14:14:04-07:00",
47+
"virtualGuestId": 1,
48+
"ticketId": 100
49+
}
50+
51+
removeAttachedHardware = True
52+
removeAttachedVirtualGuest = True

SoftLayer/managers/ticket.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ def list_tickets(self, open_status=True, closed_status=True):
3939
return self.client.call('Account', call, mask=mask)
4040

4141
def list_subjects(self):
42-
"""List all tickets."""
42+
"""List all ticket subjects."""
4343
return self.client['Ticket_Subject'].getAllObjects()
4444

4545
def get_ticket(self, ticket_id):
4646
"""Get details about a ticket.
4747
48-
:param integer id: the ticket ID
48+
:param integer ticket_id: the ticket ID
4949
:returns: A dictionary containing a large amount of information about
5050
the specified ticket.
5151
@@ -79,3 +79,39 @@ def update_ticket(self, ticket_id=None, body=None):
7979
:param string body: entry to update in the ticket
8080
"""
8181
return self.ticket.addUpdate({'entry': body}, id=ticket_id)
82+
83+
def attach_hardware(self, ticket_id=None, hardware_id=None):
84+
"""Attach hardware to a ticket.
85+
86+
:param integer ticket_id: the id of the ticket to attach to
87+
:param integer hardware_id: the id of the hardware to attach
88+
:returns The new ticket attachment
89+
"""
90+
return self.ticket.addAttachedHardware(hardware_id, id=ticket_id)
91+
92+
def attach_virtual_server(self, ticket_id=None, virtual_id=None):
93+
"""Attach a virtual server to a ticket.
94+
95+
:param integer ticket_id: the id of the ticket to attach to
96+
:param integer virtual_id: the id of the virtual server to attach
97+
:returns The new ticket attachment
98+
"""
99+
return self.ticket.addAttachedVirtualGuest(virtual_id, id=ticket_id)
100+
101+
def detach_hardware(self, ticket_id=None, hardware_id=None):
102+
"""Detach hardware from a ticket.
103+
104+
:param ticket_id: the id of the ticket to detach from
105+
:param hardware_id: the id of the hardware to detach
106+
:return: Whether the detachment was successful
107+
"""
108+
return self.ticket.removeAttachedHardware(hardware_id, id=ticket_id)
109+
110+
def detach_virtual_server(self, ticket_id=None, virtual_id=None):
111+
"""Detach a virtual server from a ticket.
112+
113+
:param ticket_id: the id of the ticket to detach from
114+
:param virtual_id: the id of the virtual server to detach
115+
:return: Whether the detachment was successful
116+
"""
117+
return self.ticket.removeAttachedVirtualGuest(virtual_id, id=ticket_id)

tests/CLI/modules/ticket_tests.py

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
55
:license: MIT, see LICENSE for more details.
66
"""
7-
from SoftLayer import testing
8-
97
import json
8+
import mock
9+
10+
from SoftLayer.CLI import exceptions
11+
from SoftLayer import testing
1012

1113

1214
class TicketTests(testing.TestCase):
@@ -38,3 +40,130 @@ def test_detail(self):
3840
}
3941
self.assertEqual(result.exit_code, 0)
4042
self.assertEqual(json.loads(result.output), expected)
43+
44+
def test_create(self):
45+
result = self.run_command(['ticket', 'create', '--title=Test',
46+
'--subject-id=1000',
47+
'--body=ticket body'])
48+
49+
self.assertEqual(result.exit_code, 0)
50+
51+
args = ({'subjectId': 1000,
52+
'contents': 'ticket body',
53+
'assignedUserId': 12345,
54+
'title': 'Test'}, 'ticket body')
55+
56+
self.assert_called_with('SoftLayer_Ticket', 'createStandardTicket',
57+
args=args)
58+
59+
def test_create_and_attach(self):
60+
result = self.run_command(['ticket', 'create', '--title=Test',
61+
'--subject-id=1000',
62+
'--body=ticket body',
63+
'--hardware=234',
64+
'--virtual=567'])
65+
66+
self.assertEqual(result.exit_code, 0)
67+
68+
args = ({'subjectId': 1000,
69+
'contents': 'ticket body',
70+
'assignedUserId': 12345,
71+
'title': 'Test'}, 'ticket body')
72+
73+
self.assert_called_with('SoftLayer_Ticket', 'createStandardTicket',
74+
args=args)
75+
self.assert_called_with('SoftLayer_Ticket', 'addAttachedHardware',
76+
args=(234,),
77+
identifier=100)
78+
self.assert_called_with('SoftLayer_Ticket', 'addAttachedVirtualGuest',
79+
args=(567,),
80+
identifier=100)
81+
82+
@mock.patch('click.edit')
83+
def test_create_no_body(self, edit_mock):
84+
edit_mock.return_value = 'ticket body'
85+
result = self.run_command(['ticket', 'create', '--title=Test',
86+
'--subject-id=1000'])
87+
self.assertEqual(result.exit_code, 0)
88+
89+
args = ({'subjectId': 1000,
90+
'contents': 'ticket body',
91+
'assignedUserId': 12345,
92+
'title': 'Test'}, 'ticket body')
93+
94+
self.assert_called_with('SoftLayer_Ticket', 'createStandardTicket',
95+
args=args)
96+
97+
def test_subjects(self):
98+
list_expected_ids = [1001, 1002, 1003, 1004, 1005]
99+
result = self.run_command(['ticket', 'subjects'])
100+
101+
self.assertEqual(result.exit_code, 0)
102+
results = json.loads(result.output)
103+
for result in results:
104+
self.assertIn(result['id'], list_expected_ids)
105+
106+
def test_attach_no_identifier(self):
107+
result = self.run_command(['ticket', 'attach', '1'])
108+
109+
self.assertEqual(result.exit_code, 2)
110+
self.assertIsInstance(result.exception, exceptions.ArgumentError)
111+
112+
def test_attach_two_identifiers(self):
113+
result = self.run_command(['ticket',
114+
'attach',
115+
'1',
116+
'--hardware=100',
117+
'--virtual=100'])
118+
119+
self.assertEqual(result.exit_code, 2)
120+
self.assertIsInstance(result.exception, exceptions.ArgumentError)
121+
122+
def test_ticket_attach_hardware(self):
123+
result = self.run_command(['ticket', 'attach', '1', '--hardware=100'])
124+
125+
self.assertEqual(result.exit_code, 0)
126+
self.assert_called_with('SoftLayer_Ticket', 'addAttachedHardware',
127+
args=(100,),
128+
identifier=1)
129+
130+
def test_ticket_attach_virtual_server(self):
131+
result = self.run_command(['ticket', 'attach', '1', '--virtual=100'])
132+
133+
self.assertEqual(result.exit_code, 0)
134+
self.assert_called_with('SoftLayer_Ticket', 'addAttachedVirtualGuest',
135+
args=(100,),
136+
identifier=1)
137+
138+
def test_detach_no_identifier(self):
139+
result = self.run_command(['ticket', 'detach', '1'])
140+
141+
self.assertEqual(result.exit_code, 2)
142+
self.assertIsInstance(result.exception, exceptions.ArgumentError)
143+
144+
def test_detach_two_identifiers(self):
145+
result = self.run_command(['ticket',
146+
'detach',
147+
'1',
148+
'--hardware=100',
149+
'--virtual=100'])
150+
self.assertEqual(result.exit_code, 2)
151+
self.assertIsInstance(result.exception, exceptions.ArgumentError)
152+
153+
def test_ticket_detach_hardware(self):
154+
result = self.run_command(['ticket', 'detach', '1', '--hardware=100'])
155+
156+
self.assertEqual(result.exit_code, 0)
157+
self.assert_called_with('SoftLayer_Ticket',
158+
'removeAttachedHardware',
159+
args=(100,),
160+
identifier=1)
161+
162+
def test_ticket_detach_virtual_server(self):
163+
result = self.run_command(['ticket', 'detach', '1', '--virtual=100'])
164+
165+
self.assertEqual(result.exit_code, 0)
166+
self.assert_called_with('SoftLayer_Ticket',
167+
'removeAttachedVirtualGuest',
168+
args=(100,),
169+
identifier=1)

0 commit comments

Comments
 (0)