Skip to content

Commit 200ed62

Browse files
author
Dean Troyer
committed
Add server image create command
Translation of 'nova image-create', with tests! Change-Id: I8a833aeff6f291e4774063ed235876eb2ba9c13c
1 parent 6460f1e commit 200ed62

5 files changed

Lines changed: 169 additions & 8 deletions

File tree

‎openstackclient/compute/v2/server.py‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,73 @@ def take_action(self, parsed_args):
384384
return zip(*sorted(six.iteritems(details)))
385385

386386

387+
class CreateServerImage(show.ShowOne):
388+
"""Create a new disk image from a running server"""
389+
390+
log = logging.getLogger(__name__ + '.CreateServerImage')
391+
392+
def get_parser(self, prog_name):
393+
parser = super(CreateServerImage, self).get_parser(prog_name)
394+
parser.add_argument(
395+
'server',
396+
metavar='<server',
397+
help='Server (name or ID)',
398+
)
399+
parser.add_argument(
400+
'--name',
401+
metavar='<image-name>',
402+
help='Name of new image (default is server name)',
403+
)
404+
parser.add_argument(
405+
'--wait',
406+
action='store_true',
407+
help='Wait for image create to complete',
408+
)
409+
return parser
410+
411+
def take_action(self, parsed_args):
412+
self.log.debug('take_action(%s)' % parsed_args)
413+
compute_client = self.app.client_manager.compute
414+
image_client = self.app.client_manager.image
415+
server = utils.find_resource(
416+
compute_client.servers,
417+
parsed_args.server,
418+
)
419+
if parsed_args.name:
420+
name = parsed_args.name
421+
else:
422+
name = server.name
423+
424+
image = compute_client.servers.create_image(
425+
server,
426+
name,
427+
)
428+
429+
if parsed_args.wait:
430+
if utils.wait_for_status(
431+
image_client.images.get,
432+
image,
433+
callback=_show_progress,
434+
):
435+
sys.stdout.write('\n')
436+
else:
437+
self.log.error(
438+
'Error creating server snapshot: %s' %
439+
parsed_args.image_name,
440+
)
441+
sys.stdout.write('\nError creating server snapshot')
442+
raise SystemExit
443+
444+
image = utils.find_resource(
445+
image_client.images,
446+
image.id,
447+
)
448+
449+
info = {}
450+
info.update(image._info)
451+
return zip(*sorted(six.iteritems(info)))
452+
453+
387454
class DeleteServer(command.Command):
388455
"""Delete server command"""
389456

‎openstackclient/tests/compute/v2/fakes.py‎

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,10 @@
1616
import mock
1717

1818
from openstackclient.tests import fakes
19+
from openstackclient.tests.image.v2 import fakes as image_fakes
1920
from openstackclient.tests import utils
2021

2122

22-
image_id = 'im1'
23-
24-
IMAGE = {
25-
'id': image_id,
26-
}
27-
28-
2923
server_id = 'serv1'
3024
server_name = 'waiter'
3125

@@ -53,3 +47,8 @@ def setUp(self):
5347
endpoint=fakes.AUTH_URL,
5448
token=fakes.AUTH_TOKEN,
5549
)
50+
51+
self.app.client_manager.image = image_fakes.FakeImagev2Client(
52+
endpoint=fakes.AUTH_URL,
53+
token=fakes.AUTH_TOKEN,
54+
)

‎openstackclient/tests/compute/v2/test_server.py‎

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from openstackclient.compute.v2 import server
1919
from openstackclient.tests.compute.v2 import fakes as compute_fakes
2020
from openstackclient.tests import fakes
21+
from openstackclient.tests.image.v2 import fakes as image_fakes
2122

2223

2324
class TestServer(compute_fakes.TestComputev2):
@@ -29,6 +30,10 @@ def setUp(self):
2930
self.servers_mock = self.app.client_manager.compute.servers
3031
self.servers_mock.reset_mock()
3132

33+
# Get a shortcut to the ImageManager Mock
34+
self.images_mock = self.app.client_manager.image.images
35+
self.images_mock.reset_mock()
36+
3237

3338
class TestServerDelete(TestServer):
3439

@@ -61,3 +66,89 @@ def test_server_delete_no_options(self):
6166
self.servers_mock.delete.assert_called_with(
6267
compute_fakes.server_id,
6368
)
69+
70+
71+
class TestServerImageCreate(TestServer):
72+
73+
def setUp(self):
74+
super(TestServerImageCreate, self).setUp()
75+
76+
# This is the return value for utils.find_resource()
77+
self.servers_mock.get.return_value = fakes.FakeResource(
78+
None,
79+
copy.deepcopy(compute_fakes.SERVER),
80+
loaded=True,
81+
)
82+
83+
self.servers_mock.create_image.return_value = fakes.FakeResource(
84+
None,
85+
copy.deepcopy(image_fakes.IMAGE),
86+
loaded=True,
87+
)
88+
89+
self.images_mock.get.return_value = fakes.FakeResource(
90+
None,
91+
copy.deepcopy(image_fakes.IMAGE),
92+
loaded=True,
93+
)
94+
95+
# Get the command object to test
96+
self.cmd = server.CreateServerImage(self.app, None)
97+
98+
def test_server_image_create_no_options(self):
99+
arglist = [
100+
compute_fakes.server_id,
101+
]
102+
verifylist = [
103+
('server', compute_fakes.server_id),
104+
]
105+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
106+
107+
# DisplayCommandBase.take_action() returns two tuples
108+
columns, data = self.cmd.take_action(parsed_args)
109+
110+
# ServerManager.create_image(server, image_name, metadata=)
111+
self.servers_mock.create_image.assert_called_with(
112+
self.servers_mock.get.return_value,
113+
compute_fakes.server_name,
114+
)
115+
116+
collist = ('id', 'is_public', 'name', 'owner')
117+
self.assertEqual(columns, collist)
118+
datalist = (
119+
image_fakes.image_id,
120+
False,
121+
image_fakes.image_name,
122+
image_fakes.image_owner,
123+
)
124+
self.assertEqual(data, datalist)
125+
126+
def test_server_image_create_name(self):
127+
arglist = [
128+
'--name', 'img-nam',
129+
compute_fakes.server_id,
130+
]
131+
verifylist = [
132+
('name', 'img-nam'),
133+
('server', compute_fakes.server_id),
134+
]
135+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
136+
137+
# DisplayCommandBase.take_action() returns two tuples
138+
columns, data = self.cmd.take_action(parsed_args)
139+
140+
# ServerManager.create_image(server, image_name, metadata=)
141+
self.servers_mock.create_image.assert_called_with(
142+
self.servers_mock.get.return_value,
143+
'img-nam',
144+
)
145+
146+
collist = ('id', 'is_public', 'name', 'owner')
147+
self.assertEqual(columns, collist)
148+
datalist = (
149+
image_fakes.image_id,
150+
False,
151+
image_fakes.image_name,
152+
image_fakes.image_owner,
153+
)
154+
self.assertEqual(data, datalist)

‎openstackclient/tests/image/v2/fakes.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@
2121

2222
image_id = 'im1'
2323
image_name = 'graven'
24+
image_owner = 'baal'
2425

2526
IMAGE = {
2627
'id': image_id,
27-
'name': image_name
28+
'name': image_name,
29+
'is_public': False,
30+
'owner': image_owner,
2831
}
2932

3033

‎setup.cfg‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ openstack.compute.v2 =
9494
server_add_volume = openstackclient.compute.v2.server:AddServerVolume
9595
server_create = openstackclient.compute.v2.server:CreateServer
9696
server_delete = openstackclient.compute.v2.server:DeleteServer
97+
server_image_create = openstackclient.compute.v2.server:CreateServerImage
9798
server_list = openstackclient.compute.v2.server:ListServer
9899
server_lock = openstackclient.compute.v2.server:LockServer
99100
server_migrate = openstackclient.compute.v2.server:MigrateServer

0 commit comments

Comments
 (0)