1# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5#      http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
13from unittest import mock
14
15from openstack.compute.v2 import server
16from openstack.image.v2 import image
17from openstack.tests.unit import base
18
19IDENTIFIER = 'IDENTIFIER'
20EXAMPLE = {
21    'OS-DCF:diskConfig': 'AUTO',
22    'OS-EXT-AZ:availability_zone': 'us-west',
23    'OS-EXT-SRV-ATTR:host': 'compute',
24    'OS-EXT-SRV-ATTR:hostname': 'new-server-test',
25    'OS-EXT-SRV-ATTR:hypervisor_hostname': 'fake-mini',
26    'OS-EXT-SRV-ATTR:instance_name': 'instance-00000001',
27    'OS-EXT-SRV-ATTR:kernel_id': '',
28    'OS-EXT-SRV-ATTR:launch_index': 0,
29    'OS-EXT-SRV-ATTR:ramdisk_id': '',
30    'OS-EXT-SRV-ATTR:reservation_id': 'r-ov3q80zj',
31    'OS-EXT-SRV-ATTR:root_device_name': '/dev/sda',
32    'OS-EXT-SRV-ATTR:user_data': 'IyEvYmluL2Jhc2gKL2Jpbi9IHlvdSEiCg==',
33    'OS-EXT-STS:power_state': 1,
34    'OS-EXT-STS:task_state': None,
35    'OS-EXT-STS:vm_state': 'active',
36    'OS-SRV-USG:launched_at': '2017-02-14T19:23:59.895661',
37    'OS-SRV-USG:terminated_at': '2015-03-09T12:15:57.233772',
38    'OS-SCH-HNT:scheduler_hints': {'key': '30'},
39    'accessIPv4': '1.2.3.4',
40    'accessIPv6': '80fe::',
41    'adminPass': '27',
42    'addresses': {
43        'private': [
44            {
45                'OS-EXT-IPS-MAC:mac_addr': 'aa:bb:cc:dd:ee:ff',
46                'OS-EXT-IPS:type': 'fixed',
47                'addr': '192.168.0.3',
48                'version': 4
49            }
50        ]
51    },
52    'block_device_mapping_v2': {'key': '29'},
53    'config_drive': '',
54    'created': '2017-02-14T19:23:58Z',
55    'description': 'dummy',
56    'flavorRef': '5',
57    'flavor': {
58        'disk': 1,
59        'ephemeral': 0,
60        'extra_specs': {
61            'hw:cpu_policy': 'dedicated',
62            'hw:mem_page_size': '2048'
63        },
64        'original_name': 'm1.tiny.specs',
65        'ram': 512,
66        'swap': 0,
67        'vcpus': 1
68    },
69    'hostId': '2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6',
70    'host_status': 'UP',
71    'id': IDENTIFIER,
72    'imageRef': '8',
73    'image': {
74        'id': '70a599e0-31e7-49b7-b260-868f441e862b',
75        'links': [
76            {
77                'href': 'http://openstack.example.com/images/70a599e0',
78                'rel': 'bookmark'
79            }
80        ]
81    },
82    'key_name': 'dummy',
83    'links': [
84        {
85            'href': 'http://openstack.example.com/v2.1/servers/9168b536',
86            'rel': 'self'
87        },
88        {
89            'href': 'http://openstack.example.com/servers/9168b536',
90            'rel': 'bookmark'
91        }
92    ],
93    'locked': True,
94    'metadata': {
95        'My Server Name': 'Apache1'
96    },
97    'name': 'new-server-test',
98    'networks': 'auto',
99    'os-extended-volumes:volumes_attached': [],
100    'progress': 0,
101    'security_groups': [
102        {
103            'name': 'default'
104        }
105    ],
106    'server_groups': ['3caf4187-8010-491f-b6f5-a4a68a40371e'],
107    'status': 'ACTIVE',
108    'tags': [],
109    'tenant_id': '6f70656e737461636b20342065766572',
110    'trusted_image_certificates': [
111        '0b5d2c72-12cc-4ba6-a8d7-3ff5cc1d8cb8',
112        '674736e3-f25c-405c-8362-bbf991e0ce0a'
113    ],
114    'updated': '2017-02-14T19:24:00Z',
115    'user_id': 'fake'
116}
117
118
119class TestServer(base.TestCase):
120
121    def setUp(self):
122        super(TestServer, self).setUp()
123        self.resp = mock.Mock()
124        self.resp.body = None
125        self.resp.json = mock.Mock(return_value=self.resp.body)
126        self.resp.status_code = 200
127        self.sess = mock.Mock()
128        self.sess.post = mock.Mock(return_value=self.resp)
129
130    def test_basic(self):
131        sot = server.Server()
132        self.assertEqual('server', sot.resource_key)
133        self.assertEqual('servers', sot.resources_key)
134        self.assertEqual('/servers', sot.base_path)
135        self.assertTrue(sot.allow_create)
136        self.assertTrue(sot.allow_fetch)
137        self.assertTrue(sot.allow_commit)
138        self.assertTrue(sot.allow_delete)
139        self.assertTrue(sot.allow_list)
140
141        self.assertDictEqual({"access_ipv4": "access_ip_v4",
142                              "access_ipv6": "access_ip_v6",
143                              "auto_disk_config": "auto_disk_config",
144                              "availability_zone": "availability_zone",
145                              "changes_before": "changes-before",
146                              "changes_since": "changes-since",
147                              "compute_host": "host",
148                              "has_config_drive": "config_drive",
149                              "created_at": "created_at",
150                              "description": "description",
151                              "flavor": "flavor",
152                              "hostname": "hostname",
153                              "image": "image",
154                              "ipv4_address": "ip",
155                              "ipv6_address": "ip6",
156                              "id": "uuid",
157                              "deleted_only": "deleted",
158                              "is_soft_deleted": "soft_deleted",
159                              "kernel_id": "kernel_id",
160                              "key_name": "key_name",
161                              "launch_index": "launch_index",
162                              "launched_at": "launched_at",
163                              "limit": "limit",
164                              "locked_by": "locked_by",
165                              "marker": "marker",
166                              "name": "name",
167                              "node": "node",
168                              "power_state": "power_state",
169                              "progress": "progress",
170                              "project_id": "project_id",
171                              "ramdisk_id": "ramdisk_id",
172                              "reservation_id": "reservation_id",
173                              "root_device_name": "root_device_name",
174                              "sort_dir": "sort_dir",
175                              "sort_key": "sort_key",
176                              "status": "status",
177                              "task_state": "task_state",
178                              "terminated_at": "terminated_at",
179                              "user_id": "user_id",
180                              "vm_state": "vm_state",
181                              "all_projects": "all_tenants",
182                              "tags": "tags",
183                              "any_tags": "tags-any",
184                              "not_tags": "not-tags",
185                              "not_any_tags": "not-tags-any",
186                              },
187                             sot._query_mapping._mapping)
188
189    def test_make_it(self):
190        sot = server.Server(**EXAMPLE)
191        self.assertEqual(EXAMPLE['accessIPv4'], sot.access_ipv4)
192        self.assertEqual(EXAMPLE['accessIPv6'], sot.access_ipv6)
193        self.assertEqual(EXAMPLE['addresses'], sot.addresses)
194        self.assertEqual(EXAMPLE['created'], sot.created_at)
195        self.assertEqual(EXAMPLE['config_drive'], sot.has_config_drive)
196        self.assertEqual(EXAMPLE['flavorRef'], sot.flavor_id)
197        self.assertEqual(EXAMPLE['flavor'], sot.flavor)
198        self.assertEqual(EXAMPLE['hostId'], sot.host_id)
199        self.assertEqual(EXAMPLE['host_status'], sot.host_status)
200        self.assertEqual(EXAMPLE['id'], sot.id)
201        self.assertEqual(EXAMPLE['imageRef'], sot.image_id)
202        self.assertEqual(image.Image(**EXAMPLE['image']), sot.image)
203        self.assertEqual(EXAMPLE['links'], sot.links)
204        self.assertEqual(EXAMPLE['metadata'], sot.metadata)
205        self.assertEqual(EXAMPLE['networks'], sot.networks)
206        self.assertEqual(EXAMPLE['name'], sot.name)
207        self.assertEqual(EXAMPLE['progress'], sot.progress)
208        self.assertEqual(EXAMPLE['tenant_id'], sot.project_id)
209        self.assertEqual(EXAMPLE['server_groups'], sot.server_groups)
210        self.assertEqual(EXAMPLE['status'], sot.status)
211        self.assertEqual(EXAMPLE['updated'], sot.updated_at)
212        self.assertEqual(EXAMPLE['user_id'], sot.user_id)
213        self.assertEqual(EXAMPLE['key_name'], sot.key_name)
214        self.assertEqual(EXAMPLE['OS-DCF:diskConfig'], sot.disk_config)
215        self.assertEqual(EXAMPLE['OS-EXT-AZ:availability_zone'],
216                         sot.availability_zone)
217        self.assertEqual(EXAMPLE['OS-EXT-STS:power_state'], sot.power_state)
218        self.assertEqual(EXAMPLE['OS-EXT-STS:task_state'], sot.task_state)
219        self.assertEqual(EXAMPLE['OS-EXT-STS:vm_state'], sot.vm_state)
220        self.assertEqual(EXAMPLE['os-extended-volumes:volumes_attached'],
221                         sot.attached_volumes)
222        self.assertEqual(EXAMPLE['OS-SRV-USG:launched_at'], sot.launched_at)
223        self.assertEqual(EXAMPLE['OS-SRV-USG:terminated_at'],
224                         sot.terminated_at)
225        self.assertEqual(EXAMPLE['security_groups'], sot.security_groups)
226        self.assertEqual(EXAMPLE['adminPass'], sot.admin_password)
227        self.assertEqual(EXAMPLE['block_device_mapping_v2'],
228                         sot.block_device_mapping)
229        self.assertEqual(EXAMPLE['OS-EXT-SRV-ATTR:host'],
230                         sot.compute_host)
231        self.assertEqual(EXAMPLE['OS-EXT-SRV-ATTR:hostname'],
232                         sot.hostname)
233        self.assertEqual(EXAMPLE['OS-EXT-SRV-ATTR:hypervisor_hostname'],
234                         sot.hypervisor_hostname)
235        self.assertEqual(EXAMPLE['OS-EXT-SRV-ATTR:instance_name'],
236                         sot.instance_name)
237        self.assertEqual(EXAMPLE['OS-EXT-SRV-ATTR:kernel_id'],
238                         sot.kernel_id)
239        self.assertEqual(EXAMPLE['OS-EXT-SRV-ATTR:launch_index'],
240                         sot.launch_index)
241        self.assertEqual(EXAMPLE['OS-EXT-SRV-ATTR:ramdisk_id'],
242                         sot.ramdisk_id)
243        self.assertEqual(EXAMPLE['OS-EXT-SRV-ATTR:reservation_id'],
244                         sot.reservation_id)
245        self.assertEqual(EXAMPLE['OS-EXT-SRV-ATTR:root_device_name'],
246                         sot.root_device_name)
247        self.assertEqual(EXAMPLE['OS-SCH-HNT:scheduler_hints'],
248                         sot.scheduler_hints)
249        self.assertEqual(EXAMPLE['OS-EXT-SRV-ATTR:user_data'], sot.user_data)
250        self.assertEqual(EXAMPLE['locked'], sot.is_locked)
251        self.assertEqual(EXAMPLE['trusted_image_certificates'],
252                         sot.trusted_image_certificates)
253
254    def test__prepare_server(self):
255        zone = 1
256        data = 2
257        hints = {"hint": 3}
258
259        sot = server.Server(id=1, availability_zone=zone, user_data=data,
260                            scheduler_hints=hints, min_count=2, max_count=3)
261        request = sot._prepare_request()
262
263        self.assertNotIn("OS-EXT-AZ:availability_zone",
264                         request.body[sot.resource_key])
265        self.assertEqual(request.body[sot.resource_key]["availability_zone"],
266                         zone)
267
268        self.assertNotIn("OS-EXT-SRV-ATTR:user_data",
269                         request.body[sot.resource_key])
270        self.assertEqual(request.body[sot.resource_key]["user_data"],
271                         data)
272
273        self.assertNotIn("OS-SCH-HNT:scheduler_hints",
274                         request.body[sot.resource_key])
275        self.assertEqual(request.body["OS-SCH-HNT:scheduler_hints"], hints)
276
277        self.assertEqual(2, request.body[sot.resource_key]['min_count'])
278        self.assertEqual(3, request.body[sot.resource_key]['max_count'])
279
280    def test_change_password(self):
281        sot = server.Server(**EXAMPLE)
282
283        self.assertIsNone(sot.change_password(self.sess, 'a'))
284
285        url = 'servers/IDENTIFIER/action'
286        body = {"changePassword": {"adminPass": "a"}}
287        headers = {'Accept': ''}
288        self.sess.post.assert_called_with(
289            url, json=body, headers=headers, microversion=None)
290
291    def test_reboot(self):
292        sot = server.Server(**EXAMPLE)
293
294        self.assertIsNone(sot.reboot(self.sess, 'HARD'))
295
296        url = 'servers/IDENTIFIER/action'
297        body = {"reboot": {"type": "HARD"}}
298        headers = {'Accept': ''}
299        self.sess.post.assert_called_with(
300            url, json=body, headers=headers, microversion=None)
301
302    def test_force_delete(self):
303        sot = server.Server(**EXAMPLE)
304
305        self.assertIsNone(sot.force_delete(self.sess))
306
307        url = 'servers/IDENTIFIER/action'
308        body = {'forceDelete': None}
309        headers = {'Accept': ''}
310        self.sess.post.assert_called_with(
311            url, json=body, headers=headers, microversion=None)
312
313    def test_rebuild(self):
314        sot = server.Server(**EXAMPLE)
315        # Let the translate pass through, that portion is tested elsewhere
316        sot._translate_response = lambda arg: arg
317
318        result = sot.rebuild(self.sess, name='noo', admin_password='seekr3t',
319                             image='http://image/1', access_ipv4="12.34.56.78",
320                             access_ipv6="fe80::100",
321                             metadata={"meta var": "meta val"},
322                             user_data="ZWNobyAiaGVsbG8gd29ybGQi")
323
324        self.assertIsInstance(result, server.Server)
325
326        url = 'servers/IDENTIFIER/action'
327        body = {
328            "rebuild": {
329                "name": "noo",
330                "imageRef": "http://image/1",
331                "adminPass": "seekr3t",
332                "accessIPv4": "12.34.56.78",
333                "accessIPv6": "fe80::100",
334                "metadata": {"meta var": "meta val"},
335                "user_data": "ZWNobyAiaGVsbG8gd29ybGQi",
336                "preserve_ephemeral": False
337            }
338        }
339        headers = {'Accept': ''}
340        self.sess.post.assert_called_with(
341            url, json=body, headers=headers, microversion=None)
342
343    def test_rebuild_minimal(self):
344        sot = server.Server(**EXAMPLE)
345        # Let the translate pass through, that portion is tested elsewhere
346        sot._translate_response = lambda arg: arg
347
348        result = sot.rebuild(self.sess, name='nootoo',
349                             admin_password='seekr3two',
350                             image='http://image/2')
351
352        self.assertIsInstance(result, server.Server)
353
354        url = 'servers/IDENTIFIER/action'
355        body = {
356            "rebuild": {
357                "name": "nootoo",
358                "imageRef": "http://image/2",
359                "adminPass": "seekr3two",
360                "preserve_ephemeral": False
361            }
362        }
363        headers = {'Accept': ''}
364        self.sess.post.assert_called_with(
365            url, json=body, headers=headers, microversion=None)
366
367    def test_resize(self):
368        sot = server.Server(**EXAMPLE)
369
370        self.assertIsNone(sot.resize(self.sess, '2'))
371
372        url = 'servers/IDENTIFIER/action'
373        body = {"resize": {"flavorRef": "2"}}
374        headers = {'Accept': ''}
375        self.sess.post.assert_called_with(
376            url, json=body, headers=headers, microversion=None)
377
378    def test_confirm_resize(self):
379        sot = server.Server(**EXAMPLE)
380
381        self.assertIsNone(sot.confirm_resize(self.sess))
382
383        url = 'servers/IDENTIFIER/action'
384        body = {"confirmResize": None}
385        headers = {'Accept': ''}
386        self.sess.post.assert_called_with(
387            url, json=body, headers=headers, microversion=None)
388
389    def test_revert_resize(self):
390        sot = server.Server(**EXAMPLE)
391
392        self.assertIsNone(sot.revert_resize(self.sess))
393
394        url = 'servers/IDENTIFIER/action'
395        body = {"revertResize": None}
396        headers = {'Accept': ''}
397        self.sess.post.assert_called_with(
398            url, json=body, headers=headers, microversion=None)
399
400    def test_create_image_header(self):
401        sot = server.Server(**EXAMPLE)
402        name = 'noo'
403        metadata = {'nu': 'image', 'created': 'today'}
404
405        url = 'servers/IDENTIFIER/action'
406        body = {"createImage": {'name': name, 'metadata': metadata}}
407        headers = {'Accept': ''}
408
409        rsp = mock.Mock()
410        rsp.json.return_value = None
411        rsp.headers = {'Location': 'dummy/dummy2'}
412        rsp.status_code = 200
413
414        self.sess.post.return_value = rsp
415
416        self.endpoint_data = mock.Mock(spec=['min_microversion',
417                                             'max_microversion'],
418                                       min_microversion=None,
419                                       max_microversion='2.44')
420        self.sess.get_endpoint_data.return_value = self.endpoint_data
421
422        image_id = sot.create_image(self.sess, name, metadata)
423
424        self.sess.post.assert_called_with(
425            url, json=body, headers=headers, microversion=None)
426
427        self.assertEqual('dummy2', image_id)
428
429    def test_create_image_microver(self):
430        sot = server.Server(**EXAMPLE)
431        name = 'noo'
432        metadata = {'nu': 'image', 'created': 'today'}
433
434        url = 'servers/IDENTIFIER/action'
435        body = {"createImage": {'name': name, 'metadata': metadata}}
436        headers = {'Accept': ''}
437
438        rsp = mock.Mock()
439        rsp.json.return_value = {'image_id': 'dummy3'}
440        rsp.headers = {'Location': 'dummy/dummy2'}
441        rsp.status_code = 200
442
443        self.sess.post.return_value = rsp
444
445        self.endpoint_data = mock.Mock(spec=['min_microversion',
446                                             'max_microversion'],
447                                       min_microversion='2.1',
448                                       max_microversion='2.56')
449        self.sess.get_endpoint_data.return_value = self.endpoint_data
450        self.sess.default_microversion = None
451
452        image_id = sot.create_image(self.sess, name, metadata)
453
454        self.sess.post.assert_called_with(
455            url, json=body, headers=headers, microversion='2.45')
456
457        self.assertEqual('dummy3', image_id)
458
459    def test_create_image_minimal(self):
460        sot = server.Server(**EXAMPLE)
461        name = 'noo'
462        url = 'servers/IDENTIFIER/action'
463        body = {"createImage": {'name': name}}
464        headers = {'Accept': ''}
465
466        rsp = mock.Mock()
467        rsp.json.return_value = None
468        rsp.headers = {'Location': 'dummy/dummy2'}
469        rsp.status_code = 200
470
471        self.sess.post.return_value = rsp
472
473        self.endpoint_data = mock.Mock(spec=['min_microversion',
474                                             'max_microversion'],
475                                       min_microversion='2.1',
476                                       max_microversion='2.56')
477        self.sess.get_endpoint_data.return_value = self.endpoint_data
478        self.sess.default_microversion = None
479
480        self.assertIsNone(self.resp.body, sot.create_image(self.sess, name))
481
482        self.sess.post.assert_called_with(
483            url, json=body, headers=headers, microversion='2.45')
484
485    def test_add_security_group(self):
486        sot = server.Server(**EXAMPLE)
487
488        self.assertIsNone(sot.add_security_group(self.sess, "group"))
489
490        url = 'servers/IDENTIFIER/action'
491        body = {"addSecurityGroup": {"name": "group"}}
492        headers = {'Accept': ''}
493        self.sess.post.assert_called_with(
494            url, json=body, headers=headers, microversion=None)
495
496    def test_remove_security_group(self):
497        sot = server.Server(**EXAMPLE)
498
499        self.assertIsNone(sot.remove_security_group(self.sess, "group"))
500
501        url = 'servers/IDENTIFIER/action'
502        body = {"removeSecurityGroup": {"name": "group"}}
503        headers = {'Accept': ''}
504        self.sess.post.assert_called_with(
505            url, json=body, headers=headers, microversion=None)
506
507    def test_reset_state(self):
508        sot = server.Server(**EXAMPLE)
509
510        self.assertIsNone(sot.reset_state(self.sess, 'active'))
511
512        url = 'servers/IDENTIFIER/action'
513        body = {"os-resetState": {"state": 'active'}}
514        headers = {'Accept': ''}
515        self.sess.post.assert_called_with(
516            url, json=body, headers=headers, microversion=None)
517
518    def test_add_fixed_ip(self):
519        sot = server.Server(**EXAMPLE)
520
521        res = sot.add_fixed_ip(self.sess, "NETWORK-ID")
522
523        self.assertIsNone(res)
524        url = 'servers/IDENTIFIER/action'
525        body = {"addFixedIp": {"networkId": "NETWORK-ID"}}
526        headers = {'Accept': ''}
527        self.sess.post.assert_called_with(
528            url, json=body, headers=headers, microversion=None)
529
530    def test_remove_fixed_ip(self):
531        sot = server.Server(**EXAMPLE)
532
533        res = sot.remove_fixed_ip(self.sess, "ADDRESS")
534
535        self.assertIsNone(res)
536        url = 'servers/IDENTIFIER/action'
537        body = {"removeFixedIp": {"address": "ADDRESS"}}
538        headers = {'Accept': ''}
539        self.sess.post.assert_called_with(
540            url, json=body, headers=headers, microversion=None)
541
542    def test_add_floating_ip(self):
543        sot = server.Server(**EXAMPLE)
544
545        res = sot.add_floating_ip(self.sess, "FLOATING-IP")
546
547        self.assertIsNone(res)
548        url = 'servers/IDENTIFIER/action'
549        body = {"addFloatingIp": {"address": "FLOATING-IP"}}
550        headers = {'Accept': ''}
551        self.sess.post.assert_called_with(
552            url, json=body, headers=headers, microversion=None)
553
554    def test_add_floating_ip_with_fixed_addr(self):
555        sot = server.Server(**EXAMPLE)
556
557        res = sot.add_floating_ip(self.sess, "FLOATING-IP", "FIXED-ADDR")
558
559        self.assertIsNone(res)
560        url = 'servers/IDENTIFIER/action'
561        body = {"addFloatingIp": {"address": "FLOATING-IP",
562                                  "fixed_address": "FIXED-ADDR"}}
563        headers = {'Accept': ''}
564        self.sess.post.assert_called_with(
565            url, json=body, headers=headers, microversion=None)
566
567    def test_remove_floating_ip(self):
568        sot = server.Server(**EXAMPLE)
569
570        res = sot.remove_floating_ip(self.sess, "I-AM-FLOATING")
571
572        self.assertIsNone(res)
573        url = 'servers/IDENTIFIER/action'
574        body = {"removeFloatingIp": {"address": "I-AM-FLOATING"}}
575        headers = {'Accept': ''}
576        self.sess.post.assert_called_with(
577            url, json=body, headers=headers, microversion=None)
578
579    def test_backup(self):
580        sot = server.Server(**EXAMPLE)
581
582        res = sot.backup(self.sess, "name", "daily", 1)
583
584        self.assertIsNone(res)
585        url = 'servers/IDENTIFIER/action'
586        body = {"createBackup": {"name": "name", "backup_type": "daily",
587                                 "rotation": 1}}
588        headers = {'Accept': ''}
589        self.sess.post.assert_called_with(
590            url, json=body, headers=headers, microversion=None)
591
592    def test_pause(self):
593        sot = server.Server(**EXAMPLE)
594
595        res = sot.pause(self.sess)
596
597        self.assertIsNone(res)
598        url = 'servers/IDENTIFIER/action'
599        body = {"pause": None}
600        headers = {'Accept': ''}
601        self.sess.post.assert_called_with(
602            url, json=body, headers=headers, microversion=None)
603
604    def test_unpause(self):
605        sot = server.Server(**EXAMPLE)
606
607        res = sot.unpause(self.sess)
608
609        self.assertIsNone(res)
610        url = 'servers/IDENTIFIER/action'
611        body = {"unpause": None}
612        headers = {'Accept': ''}
613        self.sess.post.assert_called_with(
614            url, json=body, headers=headers, microversion=None)
615
616    def test_suspend(self):
617        sot = server.Server(**EXAMPLE)
618
619        res = sot.suspend(self.sess)
620
621        self.assertIsNone(res)
622        url = 'servers/IDENTIFIER/action'
623        body = {"suspend": None}
624        headers = {'Accept': ''}
625        self.sess.post.assert_called_with(
626            url, json=body, headers=headers, microversion=None)
627
628    def test_resume(self):
629        sot = server.Server(**EXAMPLE)
630
631        res = sot.resume(self.sess)
632
633        self.assertIsNone(res)
634        url = 'servers/IDENTIFIER/action'
635        body = {"resume": None}
636        headers = {'Accept': ''}
637        self.sess.post.assert_called_with(
638            url, json=body, headers=headers, microversion=None)
639
640    def test_lock(self):
641        sot = server.Server(**EXAMPLE)
642
643        res = sot.lock(self.sess)
644
645        self.assertIsNone(res)
646        url = 'servers/IDENTIFIER/action'
647        body = {"lock": None}
648        headers = {'Accept': ''}
649        self.sess.post.assert_called_with(
650            url, json=body, headers=headers, microversion=None)
651
652    def test_unlock(self):
653        sot = server.Server(**EXAMPLE)
654
655        res = sot.unlock(self.sess)
656
657        self.assertIsNone(res)
658        url = 'servers/IDENTIFIER/action'
659        body = {"unlock": None}
660        headers = {'Accept': ''}
661        self.sess.post.assert_called_with(
662            url, json=body, headers=headers, microversion=None)
663
664    def test_rescue(self):
665        sot = server.Server(**EXAMPLE)
666
667        res = sot.rescue(self.sess)
668
669        self.assertIsNone(res)
670        url = 'servers/IDENTIFIER/action'
671        body = {"rescue": {}}
672        headers = {'Accept': ''}
673        self.sess.post.assert_called_with(
674            url, json=body, headers=headers, microversion=None)
675
676    def test_rescue_with_options(self):
677        sot = server.Server(**EXAMPLE)
678
679        res = sot.rescue(self.sess, admin_pass='SECRET', image_ref='IMG-ID')
680
681        self.assertIsNone(res)
682        url = 'servers/IDENTIFIER/action'
683        body = {"rescue": {'adminPass': 'SECRET',
684                           'rescue_image_ref': 'IMG-ID'}}
685        headers = {'Accept': ''}
686        self.sess.post.assert_called_with(
687            url, json=body, headers=headers, microversion=None)
688
689    def test_unrescue(self):
690        sot = server.Server(**EXAMPLE)
691
692        res = sot.unrescue(self.sess)
693
694        self.assertIsNone(res)
695        url = 'servers/IDENTIFIER/action'
696        body = {"unrescue": None}
697        headers = {'Accept': ''}
698        self.sess.post.assert_called_with(
699            url, json=body, headers=headers, microversion=None)
700
701    def test_evacuate(self):
702        sot = server.Server(**EXAMPLE)
703
704        res = sot.evacuate(self.sess)
705
706        self.assertIsNone(res)
707        url = 'servers/IDENTIFIER/action'
708        body = {"evacuate": {}}
709        headers = {'Accept': ''}
710        self.sess.post.assert_called_with(
711            url, json=body, headers=headers, microversion=None)
712
713    def test_evacuate_with_options(self):
714        sot = server.Server(**EXAMPLE)
715
716        res = sot.evacuate(self.sess, host='HOST2', admin_pass='NEW_PASS',
717                           force=True)
718
719        self.assertIsNone(res)
720        url = 'servers/IDENTIFIER/action'
721        body = {"evacuate": {'host': 'HOST2', 'adminPass': 'NEW_PASS',
722                             'force': True}}
723        headers = {'Accept': ''}
724        self.sess.post.assert_called_with(
725            url, json=body, headers=headers, microversion=None)
726
727    def test_start(self):
728        sot = server.Server(**EXAMPLE)
729
730        res = sot.start(self.sess)
731
732        self.assertIsNone(res)
733        url = 'servers/IDENTIFIER/action'
734        body = {"os-start": None}
735        headers = {'Accept': ''}
736        self.sess.post.assert_called_with(
737            url, json=body, headers=headers, microversion=None)
738
739    def test_stop(self):
740        sot = server.Server(**EXAMPLE)
741
742        res = sot.stop(self.sess)
743
744        self.assertIsNone(res)
745        url = 'servers/IDENTIFIER/action'
746        body = {"os-stop": None}
747        headers = {'Accept': ''}
748        self.sess.post.assert_called_with(
749            url, json=body, headers=headers, microversion=None)
750
751    def test_shelve(self):
752        sot = server.Server(**EXAMPLE)
753
754        res = sot.shelve(self.sess)
755
756        self.assertIsNone(res)
757        url = 'servers/IDENTIFIER/action'
758        body = {"shelve": None}
759        headers = {'Accept': ''}
760        self.sess.post.assert_called_with(
761            url, json=body, headers=headers, microversion=None)
762
763    def test_unshelve(self):
764        sot = server.Server(**EXAMPLE)
765
766        res = sot.unshelve(self.sess)
767
768        self.assertIsNone(res)
769        url = 'servers/IDENTIFIER/action'
770        body = {"unshelve": None}
771        headers = {'Accept': ''}
772        self.sess.post.assert_called_with(
773            url, json=body, headers=headers, microversion=None)
774
775    def test_unshelve_availability_zone(self):
776        sot = server.Server(**EXAMPLE)
777
778        res = sot.unshelve(self.sess, sot.availability_zone)
779
780        self.assertIsNone(res)
781        url = 'servers/IDENTIFIER/action'
782        body = {"unshelve": {
783            "availability_zone": sot.availability_zone
784        }}
785        headers = {'Accept': ''}
786        self.sess.post.assert_called_with(
787            url, json=body, headers=headers, microversion=None)
788
789    def test_migrate(self):
790        sot = server.Server(**EXAMPLE)
791
792        res = sot.migrate(self.sess)
793
794        self.assertIsNone(res)
795        url = 'servers/IDENTIFIER/action'
796        body = {"migrate": None}
797
798        headers = {'Accept': ''}
799        self.sess.post.assert_called_with(
800            url, json=body, headers=headers, microversion=None)
801
802    def test_get_console_output(self):
803        sot = server.Server(**EXAMPLE)
804
805        res = sot.get_console_output(self.sess)
806
807        self.assertIsNone(res)
808        url = 'servers/IDENTIFIER/action'
809        body = {'os-getConsoleOutput': {}}
810        headers = {'Accept': ''}
811        self.sess.post.assert_called_with(
812            url, json=body, headers=headers, microversion=None)
813
814        res = sot.get_console_output(self.sess, length=1)
815
816        self.assertIsNone(res)
817        url = 'servers/IDENTIFIER/action'
818        body = {'os-getConsoleOutput': {'length': 1}}
819
820        headers = {'Accept': ''}
821        self.sess.post.assert_called_with(
822            url, json=body, headers=headers, microversion=None)
823
824    def test_get_console_url(self):
825        sot = server.Server(**EXAMPLE)
826
827        resp = mock.Mock()
828        resp.body = {'console': {'a': 'b'}}
829        resp.json = mock.Mock(return_value=resp.body)
830        resp.status_code = 200
831        self.sess.post.return_value = resp
832
833        res = sot.get_console_url(self.sess, 'novnc')
834        self.sess.post.assert_called_with(
835            'servers/IDENTIFIER/action',
836            json={'os-getVNCConsole': {'type': 'novnc'}},
837            headers={'Accept': ''}, microversion=None)
838        self.assertDictEqual(resp.body['console'], res)
839
840        sot.get_console_url(self.sess, 'xvpvnc')
841        self.sess.post.assert_called_with(
842            'servers/IDENTIFIER/action',
843            json={'os-getVNCConsole': {'type': 'xvpvnc'}},
844            headers={'Accept': ''}, microversion=None)
845
846        sot.get_console_url(self.sess, 'spice-html5')
847        self.sess.post.assert_called_with(
848            'servers/IDENTIFIER/action',
849            json={'os-getSPICEConsole': {'type': 'spice-html5'}},
850            headers={'Accept': ''}, microversion=None)
851
852        sot.get_console_url(self.sess, 'rdp-html5')
853        self.sess.post.assert_called_with(
854            'servers/IDENTIFIER/action',
855            json={'os-getRDPConsole': {'type': 'rdp-html5'}},
856            headers={'Accept': ''}, microversion=None)
857
858        sot.get_console_url(self.sess, 'serial')
859        self.sess.post.assert_called_with(
860            'servers/IDENTIFIER/action',
861            json={'os-getSerialConsole': {'type': 'serial'}},
862            headers={'Accept': ''}, microversion=None)
863
864        self.assertRaises(ValueError,
865                          sot.get_console_url,
866                          self.sess,
867                          'fake_type'
868                          )
869
870    def test_live_migrate_no_force(self):
871        sot = server.Server(**EXAMPLE)
872
873        class FakeEndpointData:
874            min_microversion = None
875            max_microversion = None
876        self.sess.get_endpoint_data.return_value = FakeEndpointData()
877
878        ex = self.assertRaises(
879            ValueError,
880            sot.live_migrate,
881            self.sess, host='HOST2', force=False, block_migration=False)
882        self.assertIn(
883            "Live migration on this cloud implies 'force'",
884            str(ex))
885
886    def test_live_migrate_no_microversion_force_true(self):
887        sot = server.Server(**EXAMPLE)
888
889        class FakeEndpointData:
890            min_microversion = None
891            max_microversion = None
892        self.sess.get_endpoint_data.return_value = FakeEndpointData()
893
894        res = sot.live_migrate(
895            self.sess, host='HOST2', force=True, block_migration=True,
896            disk_over_commit=True)
897
898        self.assertIsNone(res)
899        url = 'servers/IDENTIFIER/action'
900        body = {
901            'os-migrateLive': {
902                'host': 'HOST2',
903                'disk_over_commit': True,
904                'block_migration': True
905            }
906        }
907
908        headers = {'Accept': ''}
909        self.sess.post.assert_called_with(
910            url, json=body, headers=headers, microversion=None)
911
912    def test_live_migrate_25(self):
913        sot = server.Server(**EXAMPLE)
914
915        class FakeEndpointData:
916            min_microversion = '2.1'
917            max_microversion = '2.25'
918        self.sess.get_endpoint_data.return_value = FakeEndpointData()
919        self.sess.default_microversion = None
920
921        res = sot.live_migrate(
922            self.sess, host='HOST2', force=True, block_migration=False)
923
924        self.assertIsNone(res)
925        url = 'servers/IDENTIFIER/action'
926        body = {
927            "os-migrateLive": {
928                'block_migration': False,
929                'host': 'HOST2',
930            }
931        }
932
933        headers = {'Accept': ''}
934        self.sess.post.assert_called_with(
935            url, json=body, headers=headers, microversion='2.25')
936
937    def test_live_migrate_25_default_block(self):
938        sot = server.Server(**EXAMPLE)
939
940        class FakeEndpointData:
941            min_microversion = '2.1'
942            max_microversion = '2.25'
943        self.sess.get_endpoint_data.return_value = FakeEndpointData()
944        self.sess.default_microversion = None
945
946        res = sot.live_migrate(
947            self.sess, host='HOST2', force=True, block_migration=None)
948
949        self.assertIsNone(res)
950        url = 'servers/IDENTIFIER/action'
951        body = {
952            "os-migrateLive": {
953                'block_migration': 'auto',
954                'host': 'HOST2',
955            }
956        }
957
958        headers = {'Accept': ''}
959        self.sess.post.assert_called_with(
960            url, json=body, headers=headers, microversion='2.25')
961
962    def test_live_migrate_30(self):
963        sot = server.Server(**EXAMPLE)
964
965        class FakeEndpointData:
966            min_microversion = '2.1'
967            max_microversion = '2.30'
968        self.sess.get_endpoint_data.return_value = FakeEndpointData()
969        self.sess.default_microversion = None
970
971        res = sot.live_migrate(
972            self.sess, host='HOST2', force=False, block_migration=False)
973
974        self.assertIsNone(res)
975        url = 'servers/IDENTIFIER/action'
976        body = {
977            'os-migrateLive': {
978                'block_migration': False,
979                'host': 'HOST2'
980            }
981        }
982
983        headers = {'Accept': ''}
984        self.sess.post.assert_called_with(
985            url, json=body, headers=headers, microversion='2.30')
986
987    def test_live_migrate_30_force(self):
988        sot = server.Server(**EXAMPLE)
989
990        class FakeEndpointData:
991            min_microversion = '2.1'
992            max_microversion = '2.30'
993        self.sess.get_endpoint_data.return_value = FakeEndpointData()
994        self.sess.default_microversion = None
995
996        res = sot.live_migrate(
997            self.sess, host='HOST2', force=True, block_migration=None)
998
999        self.assertIsNone(res)
1000        url = 'servers/IDENTIFIER/action'
1001        body = {
1002            'os-migrateLive': {
1003                'block_migration': 'auto',
1004                'host': 'HOST2',
1005                'force': True,
1006            }
1007        }
1008
1009        headers = {'Accept': ''}
1010        self.sess.post.assert_called_with(
1011            url, json=body, headers=headers, microversion='2.30')
1012
1013    def test_get_topology(self):
1014        sot = server.Server(**EXAMPLE)
1015
1016        class FakeEndpointData:
1017            min_microversion = '2.1'
1018            max_microversion = '2.78'
1019        self.sess.get_endpoint_data.return_value = FakeEndpointData()
1020        self.sess.default_microversion = None
1021
1022        response = mock.Mock()
1023
1024        topology = {
1025            "nodes": [
1026                {
1027                    "cpu_pinning": {
1028                        "0": 0,
1029                        "1": 5
1030                    },
1031                    "host_node": 0,
1032                    "memory_mb": 1024,
1033                    "siblings": [
1034                        [
1035                            0,
1036                            1
1037                        ]
1038                    ],
1039                    "vcpu_set": [
1040                        0,
1041                        1
1042                    ]
1043                },
1044                {
1045                    "cpu_pinning": {
1046                        "2": 1,
1047                        "3": 8
1048                    },
1049                    "host_node": 1,
1050                    "memory_mb": 2048,
1051                    "siblings": [
1052                        [
1053                            2,
1054                            3
1055                        ]
1056                    ],
1057                    "vcpu_set": [
1058                        2,
1059                        3
1060                    ]
1061                }
1062            ],
1063            "pagesize_kb": 4
1064        }
1065
1066        response.status_code = 200
1067        response.json.return_value = {
1068            'topology': topology
1069        }
1070
1071        self.sess.get.return_value = response
1072
1073        fetched_topology = sot.fetch_topology(self.sess)
1074
1075        url = 'servers/IDENTIFIER/topology'
1076        self.sess.get.assert_called_with(url)
1077
1078        self.assertEqual(fetched_topology, topology)
1079
1080    def test_get_security_groups(self):
1081        sot = server.Server(**EXAMPLE)
1082
1083        response = mock.Mock()
1084
1085        sgs = [{
1086            'description': 'default',
1087            'id': 1,
1088            'name': 'default',
1089            'rules': [
1090                {
1091                    'direction': 'egress',
1092                    'ethertype': 'IPv6',
1093                    'id': '3c0e45ff-adaf-4124-b083-bf390e5482ff',
1094                    'port_range_max': None,
1095                    'port_range_min': None,
1096                    'protocol': None,
1097                    'remote_group_id': None,
1098                    'remote_ip_prefix': None,
1099                    'security_group_id': '1',
1100                    'project_id': 'e4f50856753b4dc6afee5fa6b9b6c550',
1101                    'revision_number': 1,
1102                    'tags': ['tag1,tag2'],
1103                    'tenant_id': 'e4f50856753b4dc6afee5fa6b9b6c550',
1104                    'created_at': '2018-03-19T19:16:56Z',
1105                    'updated_at': '2018-03-19T19:16:56Z',
1106                    'description': ''
1107                }
1108            ],
1109            'tenant_id': 'e4f50856753b4dc6afee5fa6b9b6c550'
1110        }]
1111
1112        response.status_code = 200
1113        response.json.return_value = {
1114            'security_groups': sgs
1115        }
1116        self.sess.get.return_value = response
1117
1118        sot.fetch_security_groups(self.sess)
1119
1120        url = 'servers/IDENTIFIER/os-security-groups'
1121        self.sess.get.assert_called_with(url)
1122
1123        self.assertEqual(sot.security_groups, sgs)
1124