diff options
author | Zuul <zuul@review.openstack.org> | 2018-02-05 16:47:52 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2018-02-05 16:47:52 +0000 |
commit | 68877e995973e0a78d8ea501b4c19e64b720e7e8 (patch) | |
tree | 68291ea50d8e2163b16fd932b95b25b86bf91e97 | |
parent | 5eb380d4c77986ad20c330fc9281514edeeb4f1a (diff) | |
parent | 9213ec2d32fa173ec9943c28fb6c3ba5c196015d (diff) | |
download | python-novaclient-68877e995973e0a78d8ea501b4c19e64b720e7e8.tar.gz |
Merge "Implement hypervisor hostname exact pattern match"
-rw-r--r-- | novaclient/tests/unit/v2/fakes.py | 38 | ||||
-rw-r--r-- | novaclient/tests/unit/v2/test_shell.py | 171 | ||||
-rw-r--r-- | novaclient/v2/shell.py | 107 | ||||
-rw-r--r-- | releasenotes/notes/strict_hostname_match-f37243f0520a09a2.yaml | 9 |
4 files changed, 289 insertions, 36 deletions
diff --git a/novaclient/tests/unit/v2/fakes.py b/novaclient/tests/unit/v2/fakes.py index 4647d805..bd69bf9d 100644 --- a/novaclient/tests/unit/v2/fakes.py +++ b/novaclient/tests/unit/v2/fakes.py @@ -633,6 +633,12 @@ class FakeSessionClient(base_client.SessionClient): def post_servers_uuid4_metadata(self, **kw): return (204, {}, {'metadata': {'key1': 'val1'}}) + def post_servers_uuid5_metadata(self, **kw): + return (204, {}, {'metadata': {'key1': 'val1'}}) + + def post_servers_uuid6_metadata(self, **kw): + return (204, {}, {'metadata': {'key1': 'val1'}}) + def delete_servers_uuid1_metadata_key1(self, **kw): return (200, {}, {'data': 'Fake diagnostics'}) @@ -645,6 +651,12 @@ class FakeSessionClient(base_client.SessionClient): def delete_servers_uuid4_metadata_key1(self, **kw): return (200, {}, {'data': 'Fake diagnostics'}) + def delete_servers_uuid5_metadata_key1(self, **kw): + return (200, {}, {'data': 'Fake diagnostics'}) + + def delete_servers_uuid6_metadata_key1(self, **kw): + return (200, {}, {'data': 'Fake diagnostics'}) + def get_servers_1234_os_security_groups(self, **kw): return (200, {}, { "security_groups": [{ @@ -1773,6 +1785,26 @@ class FakeSessionClient(base_client.SessionClient): {'name': 'inst4', 'uuid': 'uuid4'}]}] }) + def get_os_hypervisors_hyper1_servers(self, **kw): + return (200, {}, { + 'hypervisors': [ + {'id': 1234, + 'hypervisor_hostname': 'hyper1', + 'servers': [ + {'name': 'inst1', 'uuid': 'uuid1'}, + {'name': 'inst2', 'uuid': 'uuid2'}]}] + }) + + def get_os_hypervisors_hyper2_servers(self, **kw): + return (200, {}, { + 'hypervisors': [ + {'id': 5678, + 'hypervisor_hostname': 'hyper2', + 'servers': [ + {'name': 'inst3', 'uuid': 'uuid3'}, + {'name': 'inst4', 'uuid': 'uuid4'}]}] + }) + def get_os_hypervisors_hyper_no_servers_servers(self, **kw): return (200, {}, {'hypervisors': [{'id': 1234, 'hypervisor_hostname': 'hyper1'}]}) @@ -1986,6 +2018,12 @@ class FakeSessionClient(base_client.SessionClient): def post_servers_uuid4_action(self, **kw): return 202, {}, {} + def post_servers_uuid5_action(self, **kw): + return 202, {}, {} + + def post_servers_uuid6_action(self, **kw): + return 202, {}, {} + def get_os_cells_child_cell(self, **kw): cell = {'cell': { 'username': 'cell1_user', diff --git a/novaclient/tests/unit/v2/test_shell.py b/novaclient/tests/unit/v2/test_shell.py index 7a8169cb..2dc0f395 100644 --- a/novaclient/tests/unit/v2/test_shell.py +++ b/novaclient/tests/unit/v2/test_shell.py @@ -1921,16 +1921,40 @@ class ShellTest(utils.TestCase): {'metadata': {'key1': 'val1', 'key2': 'val2'}}, pos=4) + def test_set_host_meta_strict(self): + self.run_command('host-meta hyper1 --strict set key1=val1 key2=val2') + self.assert_called('GET', '/os-hypervisors/hyper1/servers', pos=0) + self.assert_called('POST', '/servers/uuid1/metadata', + {'metadata': {'key1': 'val1', 'key2': 'val2'}}, + pos=1) + self.assert_called('POST', '/servers/uuid2/metadata', + {'metadata': {'key1': 'val1', 'key2': 'val2'}}, + pos=2) + + def test_set_host_meta_no_match(self): + cmd = 'host-meta hyper --strict set key1=val1 key2=val2' + self.assertRaises(exceptions.NotFound, self.run_command, cmd) + def test_set_host_meta_with_no_servers(self): self.run_command('host-meta hyper_no_servers set key1=val1 key2=val2') self.assert_called('GET', '/os-hypervisors/hyper_no_servers/servers') + def test_set_host_meta_with_no_servers_strict(self): + cmd = 'host-meta hyper_no_servers --strict set key1=val1 key2=val2' + self.assertRaises(exceptions.NotFound, self.run_command, cmd) + def test_delete_host_meta(self): self.run_command('host-meta hyper delete key1') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('DELETE', '/servers/uuid1/metadata/key1', pos=1) self.assert_called('DELETE', '/servers/uuid2/metadata/key1', pos=2) + def test_delete_host_meta_strict(self): + self.run_command('host-meta hyper1 --strict delete key1') + self.assert_called('GET', '/os-hypervisors/hyper1/servers', pos=0) + self.assert_called('DELETE', '/servers/uuid1/metadata/key1', pos=1) + self.assert_called('DELETE', '/servers/uuid2/metadata/key1', pos=2) + def test_usage_list(self): cmd = 'usage-list --start 2000-01-20 --end 2005-02-01' stdout, _stderr = self.run_command(cmd) @@ -2306,6 +2330,19 @@ class ShellTest(utils.TestCase): self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) + def test_host_evacuate_live_with_no_target_host_strict(self): + self.run_command('host-evacuate-live hyper1 --strict') + self.assert_called('GET', '/os-hypervisors/hyper1/servers', pos=0) + body = {'os-migrateLive': {'host': None, + 'block_migration': False, + 'disk_over_commit': False}} + self.assert_called('POST', '/servers/uuid1/action', body, pos=1) + self.assert_called('POST', '/servers/uuid2/action', body, pos=2) + + def test_host_evacuate_live_no_match(self): + cmd = 'host-evacuate-live hyper --strict' + self.assertRaises(exceptions.NotFound, self.run_command, cmd) + def test_host_evacuate_live_2_25(self): self.run_command('host-evacuate-live hyper', api_version='2.25') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) @@ -2315,6 +2352,14 @@ class ShellTest(utils.TestCase): self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) + def test_host_evacuate_live_2_25_strict(self): + self.run_command('host-evacuate-live hyper1 --strict', + api_version='2.25') + self.assert_called('GET', '/os-hypervisors/hyper1/servers', pos=0) + body = {'os-migrateLive': {'host': None, 'block_migration': 'auto'}} + self.assert_called('POST', '/servers/uuid1/action', body, pos=1) + self.assert_called('POST', '/servers/uuid2/action', body, pos=2) + def test_host_evacuate_live_with_target_host(self): self.run_command('host-evacuate-live hyper ' '--target-host hostname') @@ -2327,6 +2372,16 @@ class ShellTest(utils.TestCase): self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) + def test_host_evacuate_live_with_target_host_strict(self): + self.run_command('host-evacuate-live hyper1 ' + '--target-host hostname --strict') + self.assert_called('GET', '/os-hypervisors/hyper1/servers', pos=0) + body = {'os-migrateLive': {'host': 'hostname', + 'block_migration': False, + 'disk_over_commit': False}} + self.assert_called('POST', '/servers/uuid1/action', body, pos=1) + self.assert_called('POST', '/servers/uuid2/action', body, pos=2) + def test_host_evacuate_live_2_30(self): self.run_command('host-evacuate-live --force hyper ' '--target-host hostname', @@ -2340,6 +2395,17 @@ class ShellTest(utils.TestCase): self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) + def test_host_evacuate_live_2_30_strict(self): + self.run_command('host-evacuate-live --force hyper1 ' + '--target-host hostname --strict', + api_version='2.30') + self.assert_called('GET', '/os-hypervisors/hyper1/servers', pos=0) + body = {'os-migrateLive': {'host': 'hostname', + 'block_migration': 'auto', + 'force': True}} + self.assert_called('POST', '/servers/uuid1/action', body, pos=1) + self.assert_called('POST', '/servers/uuid2/action', body, pos=2) + def test_host_evacuate_live_with_block_migration(self): self.run_command('host-evacuate-live --block-migrate hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) @@ -2351,6 +2417,15 @@ class ShellTest(utils.TestCase): self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) + def test_host_evacuate_live_with_block_migration_strict(self): + self.run_command('host-evacuate-live --block-migrate hyper2 --strict') + self.assert_called('GET', '/os-hypervisors/hyper2/servers', pos=0) + body = {'os-migrateLive': {'host': None, + 'block_migration': True, + 'disk_over_commit': False}} + self.assert_called('POST', '/servers/uuid3/action', body, pos=1) + self.assert_called('POST', '/servers/uuid4/action', body, pos=2) + def test_host_evacuate_live_with_block_migration_2_25(self): self.run_command('host-evacuate-live --block-migrate hyper', api_version='2.25') @@ -2361,6 +2436,14 @@ class ShellTest(utils.TestCase): self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) + def test_host_evacuate_live_with_block_migration_2_25_strict(self): + self.run_command('host-evacuate-live --block-migrate hyper2 --strict', + api_version='2.25') + self.assert_called('GET', '/os-hypervisors/hyper2/servers', pos=0) + body = {'os-migrateLive': {'host': None, 'block_migration': True}} + self.assert_called('POST', '/servers/uuid3/action', body, pos=1) + self.assert_called('POST', '/servers/uuid4/action', body, pos=2) + def test_host_evacuate_live_with_disk_over_commit(self): self.run_command('host-evacuate-live --disk-over-commit hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) @@ -2372,11 +2455,26 @@ class ShellTest(utils.TestCase): self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) + def test_host_evacuate_live_with_disk_over_commit_strict(self): + self.run_command('host-evacuate-live --disk-over-commit hyper2 ' + '--strict') + self.assert_called('GET', '/os-hypervisors/hyper2/servers', pos=0) + body = {'os-migrateLive': {'host': None, + 'block_migration': False, + 'disk_over_commit': True}} + self.assert_called('POST', '/servers/uuid3/action', body, pos=1) + self.assert_called('POST', '/servers/uuid4/action', body, pos=2) + def test_host_evacuate_live_with_disk_over_commit_2_25(self): self.assertRaises(SystemExit, self.run_command, 'host-evacuate-live --disk-over-commit hyper', api_version='2.25') + def test_host_evacuate_live_with_disk_over_commit_2_25_strict(self): + self.assertRaises(SystemExit, self.run_command, + 'host-evacuate-live --disk-over-commit hyper2 ' + '--strict', api_version='2.25') + def test_host_evacuate_list_with_max_servers(self): self.run_command('host-evacuate-live --max-servers 1 hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) @@ -2385,6 +2483,14 @@ class ShellTest(utils.TestCase): 'disk_over_commit': False}} self.assert_called('POST', '/servers/uuid1/action', body, pos=1) + def test_host_evacuate_list_with_max_servers_strict(self): + self.run_command('host-evacuate-live --max-servers 1 hyper1 --strict') + self.assert_called('GET', '/os-hypervisors/hyper1/servers', pos=0) + body = {'os-migrateLive': {'host': None, + 'block_migration': False, + 'disk_over_commit': False}} + self.assert_called('POST', '/servers/uuid1/action', body, pos=1) + def test_reset_state(self): self.run_command('reset-state sample-server') self.assert_called('POST', '/servers/1234/action', @@ -2512,6 +2618,15 @@ class ShellTest(utils.TestCase): self.assert_called('POST', '/servers/uuid4/action', {'evacuate': {'host': 'target_hyper'}}, pos=4) + def test_host_evacuate_v2_14_strict(self): + self.run_command('host-evacuate hyper1 --target target_hyper --strict', + api_version='2.14') + self.assert_called('GET', '/os-hypervisors/hyper1/servers', pos=0) + self.assert_called('POST', '/servers/uuid1/action', + {'evacuate': {'host': 'target_hyper'}}, pos=1) + self.assert_called('POST', '/servers/uuid2/action', + {'evacuate': {'host': 'target_hyper'}}, pos=2) + def test_host_evacuate(self): self.run_command('host-evacuate hyper --target target_hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) @@ -2528,6 +2643,20 @@ class ShellTest(utils.TestCase): {'evacuate': {'host': 'target_hyper', 'onSharedStorage': False}}, pos=4) + def test_host_evacuate_strict(self): + self.run_command('host-evacuate hyper1 --target target_hyper --strict') + self.assert_called('GET', '/os-hypervisors/hyper1/servers', pos=0) + self.assert_called('POST', '/servers/uuid1/action', + {'evacuate': {'host': 'target_hyper', + 'onSharedStorage': False}}, pos=1) + self.assert_called('POST', '/servers/uuid2/action', + {'evacuate': {'host': 'target_hyper', + 'onSharedStorage': False}}, pos=2) + + def test_host_evacuate_no_match(self): + cmd = 'host-evacuate hyper --target target_hyper --strict' + self.assertRaises(exceptions.NotFound, self.run_command, cmd) + def test_host_evacuate_v2_29(self): self.run_command('host-evacuate hyper --target target_hyper --force', api_version='2.29') @@ -2545,6 +2674,17 @@ class ShellTest(utils.TestCase): {'evacuate': {'host': 'target_hyper', 'force': True} }, pos=4) + def test_host_evacuate_v2_29_strict(self): + self.run_command('host-evacuate hyper1 --target target_hyper' + ' --force --strict', api_version='2.29') + self.assert_called('GET', '/os-hypervisors/hyper1/servers', pos=0) + self.assert_called('POST', '/servers/uuid1/action', + {'evacuate': {'host': 'target_hyper', 'force': True} + }, pos=1) + self.assert_called('POST', '/servers/uuid2/action', + {'evacuate': {'host': 'target_hyper', 'force': True} + }, pos=2) + def test_host_evacuate_with_shared_storage(self): self.run_command( 'host-evacuate --on-shared-storage hyper --target target_hyper') @@ -2562,6 +2702,17 @@ class ShellTest(utils.TestCase): {'evacuate': {'host': 'target_hyper', 'onSharedStorage': True}}, pos=4) + def test_host_evacuate_with_shared_storage_strict(self): + self.run_command('host-evacuate --on-shared-storage hyper1' + ' --target target_hyper --strict') + self.assert_called('GET', '/os-hypervisors/hyper1/servers', pos=0) + self.assert_called('POST', '/servers/uuid1/action', + {'evacuate': {'host': 'target_hyper', + 'onSharedStorage': True}}, pos=1) + self.assert_called('POST', '/servers/uuid2/action', + {'evacuate': {'host': 'target_hyper', + 'onSharedStorage': True}}, pos=2) + def test_host_evacuate_with_no_target_host(self): self.run_command('host-evacuate --on-shared-storage hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) @@ -2574,6 +2725,14 @@ class ShellTest(utils.TestCase): self.assert_called('POST', '/servers/uuid4/action', {'evacuate': {'onSharedStorage': True}}, pos=4) + def test_host_evacuate_with_no_target_host_strict(self): + self.run_command('host-evacuate --on-shared-storage hyper1 --strict') + self.assert_called('GET', '/os-hypervisors/hyper1/servers', pos=0) + self.assert_called('POST', '/servers/uuid1/action', + {'evacuate': {'onSharedStorage': True}}, pos=1) + self.assert_called('POST', '/servers/uuid2/action', + {'evacuate': {'onSharedStorage': True}}, pos=2) + def test_host_servers_migrate(self): self.run_command('host-servers-migrate hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) @@ -2586,6 +2745,18 @@ class ShellTest(utils.TestCase): self.assert_called('POST', '/servers/uuid4/action', {'migrate': None}, pos=4) + def test_host_servers_migrate_strict(self): + self.run_command('host-servers-migrate hyper1 --strict') + self.assert_called('GET', '/os-hypervisors/hyper1/servers', pos=0) + self.assert_called('POST', + '/servers/uuid1/action', {'migrate': None}, pos=1) + self.assert_called('POST', + '/servers/uuid2/action', {'migrate': None}, pos=2) + + def test_host_servers_migrate_no_match(self): + cmd = 'host-servers-migrate hyper --strict' + self.assertRaises(exceptions.NotFound, self.run_command, cmd) + def test_hypervisor_list(self): self.run_command('hypervisor-list') self.assert_called('GET', '/os-hypervisors') diff --git a/novaclient/v2/shell.py b/novaclient/v2/shell.py index 2343c696..1f062946 100644 --- a/novaclient/v2/shell.py +++ b/novaclient/v2/shell.py @@ -4675,6 +4675,23 @@ def _server_evacuate(cs, server, args): "error_message": error_message}) +def _hyper_servers(cs, host, strict): + hypervisors = cs.hypervisors.search(host, servers=True) + for hyper in hypervisors: + if strict and hyper.hypervisor_hostname != host: + continue + if hasattr(hyper, 'servers'): + for server in hyper.servers: + yield server + if strict: + break + else: + if strict: + msg = (_("No hypervisor matching '%s' could be found.") % + host) + raise exceptions.NotFound(404, msg) + + @utils.arg('host', metavar='<host>', help='The hypervisor hostname (or pattern) to search for. ' 'WARNING: Use a fully qualified domain name if you only ' @@ -4700,18 +4717,22 @@ def _server_evacuate(cs, server, args): default=False, help=_('Force to not verify the scheduler if a host is provided.'), start_version='2.29') +@utils.arg( + '--strict', + dest='strict', + action='store_true', + default=False, + help=_('Evacuate host with exact hypervisor hostname match')) def do_host_evacuate(cs, args): """Evacuate all instances from failed host.""" - - hypervisors = cs.hypervisors.search(args.host, servers=True) response = [] - for hyper in hypervisors: - if hasattr(hyper, 'servers'): - for server in hyper.servers: - response.append(_server_evacuate(cs, server, args)) - - utils.print_list(response, - ["Server UUID", "Evacuate Accepted", "Error Message"]) + for server in _hyper_servers(cs, args.host, args.strict): + response.append(_server_evacuate(cs, server, args)) + utils.print_list(response, [ + "Server UUID", + "Evacuate Accepted", + "Error Message", + ]) def _server_live_migrate(cs, server, args): @@ -4781,22 +4802,29 @@ def _server_live_migrate(cs, server, args): default=False, help=_('Force to not verify the scheduler if a host is provided.'), start_version='2.30') +@utils.arg( + '--strict', + dest='strict', + action='store_true', + default=False, + help=_('live Evacuate host with exact hypervisor hostname match')) def do_host_evacuate_live(cs, args): """Live migrate all instances of the specified host to other available hosts. """ - hypervisors = cs.hypervisors.search(args.host, servers=True) response = [] migrating = 0 - for hyper in hypervisors: - for server in getattr(hyper, 'servers', []): - response.append(_server_live_migrate(cs, server, args)) - migrating += 1 - if args.max_servers is not None and migrating >= args.max_servers: - break - - utils.print_list(response, ["Server UUID", "Live Migration Accepted", - "Error Message"]) + for server in _hyper_servers(cs, args.host, args.strict): + response.append(_server_live_migrate(cs, server, args)) + migrating = migrating + 1 + if (args.max_servers is not None and + migrating >= args.max_servers): + break + utils.print_list(response, [ + "Server UUID", + "Live Migration Accepted", + "Error Message", + ]) class HostServersMigrateResponse(base.Resource): @@ -4821,20 +4849,24 @@ def _server_migrate(cs, server): help='The hypervisor hostname (or pattern) to search for. ' 'WARNING: Use a fully qualified domain name if you only ' 'want to cold migrate from a specific host.') +@utils.arg( + '--strict', + dest='strict', + action='store_true', + default=False, + help=_('Migrate host with exact hypervisor hostname match')) def do_host_servers_migrate(cs, args): """Cold migrate all instances off the specified host to other available hosts. """ - - hypervisors = cs.hypervisors.search(args.host, servers=True) response = [] - for hyper in hypervisors: - if hasattr(hyper, 'servers'): - for server in hyper.servers: - response.append(_server_migrate(cs, server)) - - utils.print_list(response, - ["Server UUID", "Migration Accepted", "Error Message"]) + for server in _hyper_servers(cs, args.host, args.strict): + response.append(_server_migrate(cs, server)) + utils.print_list(response, [ + "Server UUID", + "Migration Accepted", + "Error Message", + ]) @utils.arg( @@ -4964,17 +4996,20 @@ def do_list_extensions(cs, _args): action='append', default=[], help=_('Metadata to set or delete (only key is necessary on delete)')) +@utils.arg( + '--strict', + dest='strict', + action='store_true', + default=False, + help=_('Set host-meta to the hypervisor with exact hostname match')) def do_host_meta(cs, args): """Set or Delete metadata on all instances of a host.""" - hypervisors = cs.hypervisors.search(args.host, servers=True) - for hyper in hypervisors: + for server in _hyper_servers(cs, args.host, args.strict): metadata = _extract_metadata(args) - if hasattr(hyper, 'servers'): - for server in hyper.servers: - if args.action == 'set': - cs.servers.set_meta(server['uuid'], metadata) - elif args.action == 'delete': - cs.servers.delete_meta(server['uuid'], metadata.keys()) + if args.action == 'set': + cs.servers.set_meta(server['uuid'], metadata) + elif args.action == 'delete': + cs.servers.delete_meta(server['uuid'], metadata.keys()) def _print_migrations(cs, migrations): diff --git a/releasenotes/notes/strict_hostname_match-f37243f0520a09a2.yaml b/releasenotes/notes/strict_hostname_match-f37243f0520a09a2.yaml new file mode 100644 index 00000000..04d77d4a --- /dev/null +++ b/releasenotes/notes/strict_hostname_match-f37243f0520a09a2.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Provides "--strict" option for "nova host-servers-migrate", "nova host-evacuate", + "nova host-evacuate-live" and "nova host-meta" commands. When "--strict" option is + used, the action will be applied to a single compute with the exact hypervisor + hostname string match rather than to the computes with hostname substring match. + When the specified hostname does not exist in the system, "NotFound" error code + will be returned. |