diff --git a/README.md b/README.md index 541593d..4254fe1 100644 --- a/README.md +++ b/README.md @@ -247,16 +247,16 @@ openstack esi switch trunk remove vlan - `switchport`: Switchport - `vlan`: VLAN -## `openstack esi orchestrate ` +## `openstack esi cluster ` -These commands orchestrate a bare metal cluster +These commands orchestrate and undeploy simple bare metal clusters. -### `openstack esi orchestrate cluster` +### `openstack esi cluster orchestrate` Orchestrate a simple cluster. ``` -openstack esi orchestrate cluster +openstack esi cluster orchestrate ``` - ``: Configuration file; for example @@ -296,12 +296,34 @@ openstack esi orchestrate cluster } ``` -### `openstack esi orchestrate openshift` +### `openstack esi cluster list` + +List clusters deployed through ESI, along with their associated resources. + +``` +openstack esi cluster list +``` + +### `openstack esi cluster undeploy` + +Undeploy a cluster deployed through ESI. + +``` +openstack esi cluster undeploy +``` + +- ``: Cluster UUID; can be found by running `openstack esi cluster list` + +## `openstack esi openshift ` + +These commands orchestrate and undeploy OpenShift clusters. + +### `openstack esi openshift orchestrate` Orchestrate an OpenShift cluster. ``` -openstack esi orchestrate openshift +openstack esi openshift orchestrate ``` - ``: Configuration file; for example @@ -320,4 +342,14 @@ openstack esi orchestrate openshift "private_subnet_name": "my-private-subnet", "nodes": ["node1", "node2", "node3"] } -``` \ No newline at end of file +``` + +### `openstack esi openshift undeploy` + +Undeploy an OpenShift cluster orchestrated through ESI. + +``` +openstack esi openshift undeploy +``` + +- ``: Configuration file used to orchestrate OpenShift cluster diff --git a/esiclient/tests/unit/v1/orchestrator/__init__.py b/esiclient/tests/unit/v1/cluster/__init__.py similarity index 100% rename from esiclient/tests/unit/v1/orchestrator/__init__.py rename to esiclient/tests/unit/v1/cluster/__init__.py diff --git a/esiclient/tests/unit/v1/orchestrator/test_cluster.py b/esiclient/tests/unit/v1/cluster/test_cluster.py similarity index 69% rename from esiclient/tests/unit/v1/orchestrator/test_cluster.py rename to esiclient/tests/unit/v1/cluster/test_cluster.py index dce8e06..4cba5c0 100644 --- a/esiclient/tests/unit/v1/orchestrator/test_cluster.py +++ b/esiclient/tests/unit/v1/cluster/test_cluster.py @@ -17,7 +17,84 @@ from esiclient.tests.unit import base from esiclient.tests.unit import utils -from esiclient.v1.orchestrator import cluster +from esiclient.v1.cluster import cluster + + +class TestList(base.TestCommand): + + def setUp(self): + super(TestList, self).setUp() + self.cmd = cluster.List(self.app, None) + + self.node1 = utils.create_mock_object({ + "uuid": "node_uuid_1", + "name": "node1", + "extra": { + "esi_cluster_uuid": "cluster-uuid-1", + "esi_trunk_uuid": "trunk-uuid-1", + "esi_fip_uuid": "fip-uuid-1" + } + }) + self.node2 = utils.create_mock_object({ + "uuid": "node_uuid_2", + "name": "node2", + "extra": { + "esi_cluster_uuid": "cluster-uuid-1", + "esi_port_uuid": "port-uuid-2" + } + }) + self.node3 = utils.create_mock_object({ + "uuid": "node_uuid_3", + "name": "node3", + "extra": { + "esi_cluster_uuid": "cluster-uuid-1", + "esi_port_uuid": "port-uuid-3" + } + }) + self.node4 = utils.create_mock_object({ + "uuid": "node_uuid_4", + "name": "node4", + "extra": { + "esi_cluster_uuid": "cluster-uuid-2", + "esi_trunk_uuid": "trunk-uuid-2", + "esi_fip_uuid": "fip-uuid-2" + } + }) + self.node5 = utils.create_mock_object({ + "uuid": "node_uuid_5", + "name": "node5", + "extra": {} + }) + self.trunk = utils.create_mock_object({ + "id": "trunk_uuid_1", + "name": "trunk", + }) + + self.app.client_manager.baremetal.node.list.return_value = [ + self.node1, self.node2, self.node3, self.node4] + + def test_take_action(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + results = self.cmd.take_action(parsed_args) + expected = ( + ['Cluster', 'Node', 'Associated'], + [['cluster-uuid-1', 'node1\nnode2\nnode3', + "{'esi_trunk_uuid': 'trunk-uuid-1', " + "'esi_fip_uuid': 'fip-uuid-1'}\n" + "{'esi_port_uuid': 'port-uuid-2'}\n" + "{'esi_port_uuid': 'port-uuid-3'}"], + ['cluster-uuid-2', 'node4', + "{'esi_trunk_uuid': 'trunk-uuid-2', " + "'esi_fip_uuid': 'fip-uuid-2'}"]] + ) + self.assertEqual(expected, results) + self.app.client_manager.baremetal.node.list.assert_called_once_with( + fields=["uuid", "name", "extra"], + ) class TestOrchestrate(base.TestCommand): @@ -67,6 +144,9 @@ def setUp(self): "id": "network_uuid_3", "name": "external_network", }) + self.fip = utils.create_mock_object({ + "id": "fip_uuid_1", + }) self.image = utils.create_mock_object({ "id": "image_uuid_1", "name": "image", @@ -108,9 +188,11 @@ def mock_find_network(name): @mock.patch( 'esiclient.utils.create_trunk', autospec=True) + @mock.patch('oslo_utils.uuidutils.generate_uuid', autospec=True) @mock.patch('json.load', autospec=True) - def test_take_action(self, mock_load, mock_ct, mock_gocp, mock_pnwi, - mock_bnfu, mock_goapfi, mock_gfnifp, mock_gfi): + def test_take_action(self, mock_load, mock_uuid, mock_ct, mock_gocp, + mock_pnwi, mock_bnfu, mock_goapfi, mock_gfnifp, + mock_gfi): mock_load.return_value = { "node_configs": [ { @@ -143,6 +225,8 @@ def test_take_action(self, mock_load, mock_ct, mock_gocp, mock_pnwi, } ] } + mock_goapfi.return_value = self.fip + mock_uuid.return_value = 'cluster-uuid' mock_ct.return_value = None, self.port1 mock_gocp.side_effect = [self.port2, self.port3] mock_gfnifp.return_value = [], [], [] @@ -160,6 +244,7 @@ def test_take_action(self, mock_load, mock_ct, mock_gocp, mock_pnwi, fields=["uuid", "name", "resource_class"], provision_state='available' ) + mock_uuid.assert_called_once self.app.client_manager.network.find_network.assert_has_calls([ call('private_network_1'), call('external_network') ]) @@ -191,6 +276,22 @@ def test_take_action(self, mock_load, mock_ct, mock_gocp, mock_pnwi, ) assert mock_gfnifp.call_count == 3 assert mock_gfi.call_count == 3 + self.app.client_manager.baremetal.node.update.assert_has_calls([ + call('node_uuid_1', [{'path': '/extra/esi_cluster_uuid', + 'value': 'cluster-uuid', 'op': 'add'}, + {'path': '/extra/esi_port_uuid', + 'value': 'port_uuid_1', 'op': 'add'}, + {'path': '/extra/esi_fip_uuid', + 'value': 'fip_uuid_1', 'op': 'add'}]), + call('node_uuid_2', [{'path': '/extra/esi_cluster_uuid', + 'value': 'cluster-uuid', 'op': 'add'}, + {'path': '/extra/esi_port_uuid', 'value': + 'port_uuid_2', 'op': 'add'}]), + call('node_uuid_3', [{'path': '/extra/esi_cluster_uuid', + 'value': 'cluster-uuid', 'op': 'add'}, + {'path': '/extra/esi_port_uuid', + 'value': 'port_uuid_3', 'op': 'add'}]) + ]) @mock.patch('json.load', autospec=True) def test_take_action_insufficient_nodes(self, mock_load): @@ -407,120 +508,98 @@ def setUp(self): super(TestUndeploy, self).setUp() self.cmd = cluster.Undeploy(self.app, None) - self.port1 = utils.create_mock_object({ - "id": "port_uuid_1", - "name": "esi-node2-network2", - "network_id": "network_uuid_1", + self.node1 = utils.create_mock_object({ + "uuid": "node_uuid_1", + "name": "node1", + "extra": { + "esi_cluster_uuid": "cluster-uuid-1", + "esi_trunk_uuid": "trunk-uuid-1", + "esi_fip_uuid": "fip-uuid-1" + } }) - self.port2 = utils.create_mock_object({ - "id": "port_uuid_2", - "name": "esi-node3-network2", - "network_id": "network_uuid_2", + self.node2 = utils.create_mock_object({ + "uuid": "node_uuid_2", + "name": "node2", + "extra": { + "esi_cluster_uuid": "cluster-uuid-1", + "esi_port_uuid": "port-uuid-2" + } }) - self.network1 = utils.create_mock_object({ - "id": "network_uuid_1", - "name": "network1", + self.node3 = utils.create_mock_object({ + "uuid": "node_uuid_3", + "name": "node3", + "extra": { + "esi_cluster_uuid": "cluster-uuid-1", + "esi_port_uuid": "port-uuid-3" + } + }) + self.node4 = utils.create_mock_object({ + "uuid": "node_uuid_4", + "name": "node4", + "extra": { + "esi_cluster_uuid": "cluster-uuid-2", + "esi_trunk_uuid": "trunk-uuid-2", + "esi_fip_uuid": "fip-uuid-2" + } }) - self.network2 = utils.create_mock_object({ - "id": "network_uuid_2", - "name": "network2", + self.node5 = utils.create_mock_object({ + "uuid": "node_uuid_5", + "name": "node5", + "extra": {} }) self.trunk = utils.create_mock_object({ "id": "trunk_uuid_1", "name": "trunk", }) - def mock_find_network(name): - if name == "network1": - return self.network1 - if name == "network2": - return self.network2 - return None - self.app.client_manager.network.find_network.\ - side_effect = mock_find_network - - def mock_find_port(name): - if name == "esi-node2-network2": - return self.port1 - if name == "esi-node3-network2": - return self.port2 - return None - self.app.client_manager.network.find_port.\ - side_effect = mock_find_port - self.app.client_manager.network.delete_port.\ - return_value = None - + self.app.client_manager.baremetal.node.list.return_value = [ + self.node1, self.node2, self.node3, self.node4] self.app.client_manager.network.find_trunk.return_value = \ self.trunk - self.app.client_manager.baremetal.node.set_provision_state.\ - return_value = None - @mock.patch( 'esiclient.utils.delete_trunk', autospec=True) - @mock.patch('time.sleep', autospec=True) - @mock.patch('json.load', autospec=True) - def test_take_action(self, mock_load, mock_sleep, mock_dt): - mock_load.return_value = { - "node_configs": [ - { - "nodes": { - "node_uuids": ["node1"] - }, - "network": { - "network_uuid": "network1", - "tagged_network_uuids": ["network2"], - "fip_network_uuid": "external_network" - }, - "provisioning": { - "provisioning_type": "image", - "image_uuid": "image_uuid", - "ssh_key": "/path/to/ssh/key" - } - }, - { - "nodes": { - "node_uuids": ["node2", "node3"] - }, - "network": { - "network_uuid": "network2" - }, - "provisioning": { - "provisioning_type": "image_url", - "url": "https://image.url" - } - } - ] - } - - arglist = ['config.json'] + def test_take_action(self, mock_dt): + arglist = ['cluster-uuid-1'] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with patch("builtins.open"): - self.cmd.take_action(parsed_args) + self.cmd.take_action(parsed_args) - self.app.client_manager.network.find_network.assert_has_calls([ - call('network1'), - call('network2') - ]) + self.app.client_manager.baremetal.node.list.assert_called_once_with( + fields=["uuid", "name", "extra"], + ) self.app.client_manager.baremetal.node.set_provision_state.\ assert_has_calls([ - call('node1', 'deleted'), - call('node2', 'deleted'), - call('node3', 'deleted') + call('node_uuid_1', 'deleted'), + call('node_uuid_2', 'deleted'), + call('node_uuid_3', 'deleted') ]) self.app.client_manager.network.find_trunk.\ - assert_called_once_with('esi-node1-trunk') + assert_called_once_with('trunk-uuid-1') mock_dt.assert_called_once_with( self.app.client_manager.network, self.trunk) - self.app.client_manager.network.find_port.assert_has_calls([ - call('esi-node2-network2'), - call('esi-node3-network2') - ]) self.app.client_manager.network.delete_port.assert_has_calls([ - call('port_uuid_1'), - call('port_uuid_2') + call('port-uuid-2'), + call('port-uuid-3') + ]) + self.app.client_manager.network.delete_ip.assert_called_once_with( + 'fip-uuid-1' + ) + self.app.client_manager.baremetal.node.update.assert_has_calls([ + call('node_uuid_1', [ + {'path': '/extra/esi_cluster_uuid', 'op': 'remove'}, + {'path': '/extra/esi_trunk_uuid', 'op': 'remove'}, + {'path': '/extra/esi_fip_uuid', 'op': 'remove'} + ]), + call('node_uuid_2', [ + {'path': '/extra/esi_cluster_uuid', 'op': 'remove'}, + {'path': '/extra/esi_port_uuid', 'op': 'remove'} + ]), + call('node_uuid_3', [ + {'path': '/extra/esi_cluster_uuid', 'op': 'remove'}, + {'path': '/extra/esi_port_uuid', 'op': 'remove'} + ]) ]) diff --git a/esiclient/tests/unit/v1/orchestrator/test_openshift.py b/esiclient/tests/unit/v1/cluster/test_openshift.py similarity index 99% rename from esiclient/tests/unit/v1/orchestrator/test_openshift.py rename to esiclient/tests/unit/v1/cluster/test_openshift.py index 2d822cf..70978ff 100644 --- a/esiclient/tests/unit/v1/orchestrator/test_openshift.py +++ b/esiclient/tests/unit/v1/cluster/test_openshift.py @@ -19,7 +19,7 @@ from esiclient.tests.unit import base from esiclient.tests.unit import utils -from esiclient.v1.orchestrator import openshift +from esiclient.v1.cluster import openshift class MockResponse: @@ -101,7 +101,7 @@ class TestWaitForNodes(TestCase): @mock.patch('time.sleep', autospec=True) @mock.patch( - 'esiclient.v1.orchestrator.openshift.call_assisted_installer_api', + 'esiclient.v1.cluster.openshift.call_assisted_installer_api', autospec=True) def test_wait_for_nodes(self, mock_caia, mock_sleep): first_response = [] @@ -318,10 +318,10 @@ def mock_find_port(uuid): 'esiclient.utils.boot_node_from_url', autospec=True) @mock.patch( - 'esiclient.v1.orchestrator.openshift.wait_for_nodes', + 'esiclient.v1.cluster.openshift.wait_for_nodes', autospec=True) @mock.patch( - 'esiclient.v1.orchestrator.openshift.call_assisted_installer_api', + 'esiclient.v1.cluster.openshift.call_assisted_installer_api', autospec=True) @mock.patch('time.sleep', autospec=True) @mock.patch('json.loads', autospec=True) diff --git a/esiclient/v1/orchestrator/__init__.py b/esiclient/v1/cluster/__init__.py similarity index 100% rename from esiclient/v1/orchestrator/__init__.py rename to esiclient/v1/cluster/__init__.py diff --git a/esiclient/v1/orchestrator/cluster.py b/esiclient/v1/cluster/cluster.py similarity index 65% rename from esiclient/v1/orchestrator/cluster.py rename to esiclient/v1/cluster/cluster.py index 1bacf7f..b36258d 100644 --- a/esiclient/v1/orchestrator/cluster.py +++ b/esiclient/v1/cluster/cluster.py @@ -13,18 +13,70 @@ import concurrent.futures import json import logging -import time from osc_lib.command import command from osc_lib.i18n import _ +from oslo_utils import uuidutils + from esiclient import utils +ESI_CLUSTER_UUID = 'esi_cluster_uuid' +ESI_TRUNK_UUID = 'esi_trunk_uuid' +ESI_PORT_UUID = 'esi_port_uuid' +ESI_FIP_UUID = 'esi_fip_uuid' + + class ESIOrchestrationException(Exception): pass +class List(command.Lister): + """List ESI clusters""" + + log = logging.getLogger(__name__ + ".List") + + def get_parser(self, prog_name): + parser = super(List, self).get_parser(prog_name) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + + ironic_client = self.app.client_manager.baremetal + + nodes = ironic_client.node.list(fields=['uuid', 'name', 'extra']) + + cluster_dict = {} + for node in nodes: + extra = node.extra + # only look at nodes that specify a cluster UUID + if ESI_CLUSTER_UUID in extra: + cluster_uuid = extra[ESI_CLUSTER_UUID] + esi_extra = {} + for key in extra: + # only display 'extra' attributes set by ESI + if key.startswith('esi') and key != ESI_CLUSTER_UUID: + esi_extra[key] = extra[key] + if cluster_uuid not in cluster_dict: + cluster_dict[cluster_uuid] = {} + cluster_dict[cluster_uuid][node.name] = esi_extra + + cluster_info = [] + for cluster_uuid in cluster_dict: + node_info = [] + extra_info = [] + for node in cluster_dict[cluster_uuid]: + node_info.append(node) + extra_info.append(str(cluster_dict[cluster_uuid][node])) + cluster_info.append([cluster_uuid, + "\n".join(node_info), + "\n".join(extra_info)]) + + return ["Cluster", "Node", "Associated"], cluster_info + + class Orchestrate(command.Lister): """Orchestrate an ESI cluster""" @@ -42,6 +94,16 @@ def get_parser(self, prog_name): return parser + def set_node_cluster_info(self, node, cluster_dict): + ironic_client = self.app.client_manager.baremetal + node_update = [] + for key, value in cluster_dict.items(): + node_update.append( + {'path': "/extra/%s" % key, + 'value': value, + 'op': 'add'}) + ironic_client.node.update(node.uuid, node_update) + def assign_nodes(self, cluster_config): print("ASSIGNING NODES") ironic_client = self.app.client_manager.baremetal @@ -122,20 +184,28 @@ def get_port_from_network_config(self, node, network_config): port = neutron_client.find_port(port.id) print("* Using trunk port %s" % trunk_name) else: + trunk = None port_name = "esi-%s-%s" % (node.name, network.name) port = utils.get_or_create_port(port_name, network, neutron_client) print("* Using port %s" % port_name) - return port + return port, trunk - def provision_node(self, node, provisioning_type, node_config): + def provision_node(self, node, provisioning_type, node_config, + cluster_uuid): glance_client = self.app.client_manager.image ironic_client = self.app.client_manager.baremetal neutron_client = self.app.client_manager.network + cluster_dict = {ESI_CLUSTER_UUID: cluster_uuid} + network_config = node_config['network'] # create network port - port = self.get_port_from_network_config(node, network_config) + port, trunk = self.get_port_from_network_config(node, network_config) + if trunk: + cluster_dict[ESI_TRUNK_UUID] = trunk.id + else: + cluster_dict[ESI_PORT_UUID] = port.id # provision if provisioning_type == 'image': @@ -164,8 +234,11 @@ def provision_node(self, node, provisioning_type, node_config): node.name, port.name)) fip_network = neutron_client.find_network( network_config['fip_network_uuid']) - utils.get_or_assign_port_floating_ip( + fip = utils.get_or_assign_port_floating_ip( port, fip_network, neutron_client) + cluster_dict[ESI_FIP_UUID] = fip.id + + self.set_node_cluster_info(node, cluster_dict) return node, port @@ -181,6 +254,7 @@ def take_action(self, parsed_args): print("") print("PROVISIONING NODES") + cluster_uuid = uuidutils.generate_uuid() node_configs = cluster_config['node_configs'] futures = [] with concurrent.futures.ThreadPoolExecutor() as executor: @@ -194,7 +268,7 @@ def take_action(self, parsed_args): for node in nodes: future = executor.submit( self.provision_node, node, provisioning_type, - node_config) + node_config, cluster_uuid) futures.append(future) print("NODE PROVISIONING COMPLETE") @@ -234,67 +308,65 @@ def get_parser(self, prog_name): parser = super(Undeploy, self).get_parser(prog_name) parser.add_argument( - "cluster_config_file", - metavar="", - help=_("File describing the cluster configuration")) + "cluster_uuid", + metavar="", + help=_("Cluster UUID")) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) - cluster_config_file = parsed_args.cluster_config_file - with open(cluster_config_file) as f: - cluster_config = json.load(f) + cluster_uuid = parsed_args.cluster_uuid - print("STARTING UNDEPLOY") + print("STARTING UNDEPLOY for CLUSTER %s" % cluster_uuid) ironic_client = self.app.client_manager.baremetal neutron_client = self.app.client_manager.network - node_configs = cluster_config['node_configs'] - uuid_node_configs = [node_config for node_config in node_configs - if 'node_uuids' in node_config['nodes']] - count = 0 - for node_config in uuid_node_configs: - count += 1 - print("* undeploying %s of %s node configs" % ( - count, len(uuid_node_configs))) - node_uuids = node_config['nodes']['node_uuids'] - network_config = node_config['network'] - network_uuid = network_config.get('network_uuid', None) - network = neutron_client.find_network(network_uuid) - for node in node_uuids: - print(" * %s" % node) - ironic_client.node.set_provision_state(node, 'deleted') - print(" * waiting for nodes to start undeploy before" - " deleting ports") - time.sleep(15) - if 'tagged_network_uuids' in network_config: - trunk_name = "esi-%s-trunk" % node - trunk = neutron_client.find_trunk(trunk_name) + nodes = ironic_client.node.list(fields=['uuid', 'name', 'extra']) + cluster_found = False + for node in nodes: + extra = node.extra + if extra.get(ESI_CLUSTER_UUID, None) == cluster_uuid: + cluster_found = True + print("* Node %s" % node.name) + node_extra_update = [] + node_extra_update.append( + {'path': "/extra/esi_cluster_uuid", + 'op': 'remove'}) + + if ESI_PORT_UUID in extra: + port_uuid = extra[ESI_PORT_UUID] + print(" * deleting port %s" % port_uuid) + neutron_client.delete_port(port_uuid) + node_extra_update.append( + {'path': "/extra/esi_port_uuid", + 'op': 'remove'}) + if ESI_TRUNK_UUID in extra: + trunk_uuid = extra[ESI_TRUNK_UUID] + print(" * deleting trunk %s" % trunk_uuid) + trunk = neutron_client.find_trunk(trunk_uuid) if trunk: - print(" * %s" % trunk_name) utils.delete_trunk(neutron_client, trunk) - else: - port_name = "esi-%s-%s" % (node, network.name) - port = neutron_client.find_port(port_name) - if port: - print(" * %s" % port_name) - neutron_client.delete_port(port.id) - - non_uuid_node_configs = [node_config for node_config in node_configs - if 'node_uuids' not in node_config['nodes']] - if len(non_uuid_node_configs) > 0: - print("* %s node configs were skipped, as they do not" - " specify specific nodes" % len(non_uuid_node_configs)) - print(" * these nodes and ports will have to be removed" - " manually") - print(" openstack baremetal node undeploy ") - print(" openstack port delete ") - - print("UNDEPLOY COMPLETE") - print("-----------------") - print("* node cleaning will take a while to complete") - print("* run `openstack baremetal node list` to see if" - " they are in the `available` state") + node_extra_update.append( + {'path': "/extra/esi_trunk_uuid", + 'op': 'remove'}) + if ESI_FIP_UUID in extra: + fip_uuid = extra[ESI_FIP_UUID] + print(" * deleting fip %s" % fip_uuid) + neutron_client.delete_ip(fip_uuid) + node_extra_update.append( + {'path': "/extra/esi_fip_uuid", + 'op': 'remove'}) + ironic_client.node.update(node.uuid, node_extra_update) + ironic_client.node.set_provision_state(node.uuid, 'deleted') + + if cluster_found: + print("UNDEPLOY COMPLETE") + print("-----------------") + print("* node cleaning will take a while to complete") + print("* run `openstack baremetal node list` to see if" + " they are in the `available` state") + else: + print("No cluster with UUID %s found" % cluster_uuid) diff --git a/esiclient/v1/orchestrator/openshift.py b/esiclient/v1/cluster/openshift.py similarity index 100% rename from esiclient/v1/orchestrator/openshift.py rename to esiclient/v1/cluster/openshift.py diff --git a/requirements.txt b/requirements.txt index 61086c8..90b985f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 Babel!=2.4.0,>=2.3.4 # BSD metalsmith>=2.0.0 openstacksdk<1.3.0 +oslo.utils>=4.5.0 # Apache-2.0 pbr!=2.1.0,>=2.0.0 # Apache-2.0 passlib>=1.7.0 # BSD psutil>=3.2.2 # BSD diff --git a/setup.cfg b/setup.cfg index bb19bbe..6e7385b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,10 +36,11 @@ openstack.esiclient.v1 = esi_node_network_detach = esiclient.v1.node_network:Detach esi_node_network_list = esiclient.v1.node_network:List esi_node_volume_attach = esiclient.v1.node_volume:Attach - esi_cluster_orchestrate = esiclient.v1.orchestrator.cluster:Orchestrate - esi_cluster_undeploy = esiclient.v1.orchestrator.cluster:Undeploy - esi_openshift_orchestrate = esiclient.v1.orchestrator.openshift:Orchestrate - esi_openshift_undeploy = esiclient.v1.orchestrator.openshift:Undeploy + esi_cluster_list = esiclient.v1.cluster.cluster:List + esi_cluster_orchestrate = esiclient.v1.cluster.cluster:Orchestrate + esi_cluster_undeploy = esiclient.v1.cluster.cluster:Undeploy + esi_openshift_orchestrate = esiclient.v1.cluster.openshift:Orchestrate + esi_openshift_undeploy = esiclient.v1.cluster.openshift:Undeploy esi_switch_vlan_list = esiclient.v1.switch:ListVLAN esi_switch_port_list = esiclient.v1.switch:ListSwitchPort esi_switch_list = esiclient.v1.switch:List