diff --git a/fuelclient/commands/environment.py b/fuelclient/commands/environment.py index 6d7b97b2..f762edf6 100644 --- a/fuelclient/commands/environment.py +++ b/fuelclient/commands/environment.py @@ -254,3 +254,19 @@ class EnvDeploy(EnvMixIn, base.BaseCommand): 'has been started.\n'.format(t=task_id, e=parsed_args.id) self.app.stdout.write(msg) + + +class EnvSpawnVms(EnvMixIn, base.BaseCommand): + """Provision specified environment.""" + + def get_parser(self, prog_name): + parser = super(EnvSpawnVms, self).get_parser(prog_name) + + parser.add_argument('id', + type=int, + help='Id of the environment to be provision.') + + return parser + + def take_action(self, parsed_args): + return self.client.spawn_vms(parsed_args.id) diff --git a/fuelclient/commands/node.py b/fuelclient/commands/node.py index aacc47b6..a9dfa473 100644 --- a/fuelclient/commands/node.py +++ b/fuelclient/commands/node.py @@ -14,6 +14,7 @@ from fuelclient.commands import base from fuelclient.common import data_utils +from fuelclient import utils class NodeMixIn(object): @@ -77,3 +78,39 @@ class NodeShow(NodeMixIn, base.BaseShowCommand): # TODO(romcheg): network_data mostly never fits the screen # 'network_data', 'manufacturer') + + +class NodeVmsList(NodeMixIn, base.BaseShowCommand): + """Show list vms for node.""" + + columns = ('vms_conf',) + + def take_action(self, parsed_args): + data = self.client.get_node_vms_conf(parsed_args.id) + data = data_utils.get_display_data_single(self.columns, data) + + return (self.columns, data) + + +class NodeCreateVMsConf(NodeMixIn, base.BaseCommand): + """Create vms config in metadata for selected node.""" + + def get_parser(self, prog_name): + parser = super(NodeCreateVMsConf, self).get_parser(prog_name) + parser.add_argument('id', type=int, + help='Id of the {0}.'.format(self.entity_name)) + parser.add_argument( + '--conf', + type=str, + required=True, + nargs='+', + help='JSONs with VMs configuration', + ) + + return parser + + def take_action(self, parsed_args): + confs = utils.parse_to_list_of_dicts(parsed_args.conf) + data = self.client.node_vms_create(parsed_args.id, confs) + msg = "{0}".format(data) + self.app.stdout.write(msg) diff --git a/fuelclient/objects/environment.py b/fuelclient/objects/environment.py index e65f3305..35434a1b 100644 --- a/fuelclient/objects/environment.py +++ b/fuelclient/objects/environment.py @@ -451,3 +451,7 @@ class Environment(BaseObject): resp = self.connection.get_request_raw(url, params=params) resp.raise_for_status() return resp.text + + def spawn_vms(self): + url = 'clusters/{0}/spawn_vms/'.format(self.id) + return self.connection.put_request(url, {}) diff --git a/fuelclient/objects/node.py b/fuelclient/objects/node.py index dd3e95b8..2e88502e 100644 --- a/fuelclient/objects/node.py +++ b/fuelclient/objects/node.py @@ -121,6 +121,14 @@ class Node(BaseObject): def delete(self): self.connection.delete_request(self.instance_api_path.format(self.id)) + def node_vms_create(self, config): + url = "nodes/{0}/vms_conf/".format(self.id) + return self.connection.put_request(url, config) + + def get_node_vms_conf(self): + url = "nodes/{0}/vms_conf/".format(self.id) + return self.connection.get_request(url) + class NodeCollection(object): diff --git a/fuelclient/tests/v2/unit/cli/test_env.py b/fuelclient/tests/v2/unit/cli/test_env.py index b415c4ce..740c8e43 100644 --- a/fuelclient/tests/v2/unit/cli/test_env.py +++ b/fuelclient/tests/v2/unit/cli/test_env.py @@ -122,3 +122,11 @@ class TestEnvCommand(test_engine.BaseCLITest): self.m_get_client.assert_called_once_with('environment', mock.ANY) self.m_client.upgrade.assert_called_once_with(10, 15) + + def test_env_spawn_vms(self): + env_id = 10 + args = 'env spawn-vms {0}'.format(env_id) + self.exec_command(args) + + self.m_get_client.assert_called_once_with('environment', mock.ANY) + self.m_client.spawn_vms.assert_called_once_with(env_id) diff --git a/fuelclient/tests/v2/unit/cli/test_node.py b/fuelclient/tests/v2/unit/cli/test_node.py index 16ad3f67..d0fcf46d 100644 --- a/fuelclient/tests/v2/unit/cli/test_node.py +++ b/fuelclient/tests/v2/unit/cli/test_node.py @@ -54,3 +54,27 @@ class TestNodeCommand(test_engine.BaseCLITest): self.m_get_client.assert_called_once_with('node', mock.ANY) self.m_client.get_by_id.assert_called_once_with(node_id) + + def test_node_vms_conf_list(self): + node_id = 42 + args = 'node list-vms-conf {node_id}'.format(node_id=node_id) + + self.exec_command(args) + + self.m_get_client.assert_called_once_with('node', mock.ANY) + self.m_client.get_node_vms_conf.assert_called_once_with(node_id) + + def test_node_vms_conf_create(self): + vms_conf = """{"id":2} {"id":3}""" + config = [{'id': 2}, + {'id': 3}] + + node_id = 42 + + args = "node create-vms-conf {0} --conf {1}".format( + node_id, + vms_conf) + self.exec_command(args) + + self.m_get_client.assert_called_once_with('node', mock.ANY) + self.m_client.node_vms_create.assert_called_once_with(node_id, config) diff --git a/fuelclient/tests/v2/unit/lib/test_environment.py b/fuelclient/tests/v2/unit/lib/test_environment.py index a11d3ea3..c1706e08 100644 --- a/fuelclient/tests/v2/unit/lib/test_environment.py +++ b/fuelclient/tests/v2/unit/lib/test_environment.py @@ -158,3 +158,11 @@ class TestEnvFacade(test_api.BaseLibTest): for assignment in self.session_adapter.last_request.json(): # Check whether all assignments are expected self.assertIn(assignment, expected_body) + + def test_env_spawn_vms(self): + env_id = 10 + expected_uri = '/api/v1/clusters/{0}/spawn_vms/'.format(env_id) + self.client.spawn_vms(env_id) + + self.assertEqual(rm.PUT, self.session_adapter.last_request.method) + self.assertEqual(expected_uri, self.session_adapter.last_request.path) diff --git a/fuelclient/tests/v2/unit/lib/test_node.py b/fuelclient/tests/v2/unit/lib/test_node.py index f8e7f72c..84b64cf9 100644 --- a/fuelclient/tests/v2/unit/lib/test_node.py +++ b/fuelclient/tests/v2/unit/lib/test_node.py @@ -14,6 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. +import json import requests_mock as rm import fuelclient @@ -44,3 +45,26 @@ class TestNodeFacade(test_api.BaseLibTest): self.assertEqual(rm.GET, self.session_adapter.last_request.method) self.assertEqual(expected_uri, self.session_adapter.last_request.path) + + def test_node_vms_list(self): + node_id = 42 + expected_uri = "/api/v1/nodes/{0}/vms_conf/".format(node_id) + + self.client.get_node_vms_conf(node_id) + + self.assertEqual(rm.GET, self.session_adapter.last_request.method) + self.assertEqual(expected_uri, self.session_adapter.last_request.path) + + def test_node_vms_create(self): + config = [{'id': 1, 'opt2': 'val2'}, + {'id': 2, 'opt4': 'val4'}] + node_id = 42 + + expected_uri = "/api/v1/nodes/{0}/vms_conf/".format(node_id) + expected_body = json.dumps(config) + + self.client.node_vms_create(node_id, config) + + self.assertEqual(rm.PUT, self.session_adapter.last_request.method) + self.assertEqual(expected_uri, self.session_adapter.last_request.path) + self.assertEqual(expected_body, self.session_adapter.last_request.body) diff --git a/fuelclient/utils.py b/fuelclient/utils.py index 2f170aab..8c269e0e 100644 --- a/fuelclient/utils.py +++ b/fuelclient/utils.py @@ -16,6 +16,7 @@ import glob import io +import json import os import subprocess import yaml @@ -129,3 +130,22 @@ def file_exists(path): :returns: True if file is exist, Flase if is not """ return os.path.lexists(path) + + +def parse_to_list_of_dicts(str_list): + """Parse list of json strings to dictionaries + + :param list: list of dicts and json string + :returns" list of dictionaries + + """ + dict_list = [] + for json_str in str_list: + if not isinstance(json_str, dict): + try: + json_str = json.loads(json_str) + except Exception: + raise error.BadDataException( + 'Not valid JSON data: {0}'.format(json_str)) + dict_list.append(json_str) + return dict_list diff --git a/fuelclient/v1/environment.py b/fuelclient/v1/environment.py index 4a2582ec..6bc771d5 100644 --- a/fuelclient/v1/environment.py +++ b/fuelclient/v1/environment.py @@ -88,6 +88,10 @@ class EnvironmentClient(base_v1.BaseV1Client): return deploy_task.id + def spawn_vms(self, environment_id): + env = self._entity_wrapper(obj_id=environment_id) + return env.spawn_vms() + def get_client(): return EnvironmentClient() diff --git a/fuelclient/v1/node.py b/fuelclient/v1/node.py index 46dff36b..01b34ea2 100644 --- a/fuelclient/v1/node.py +++ b/fuelclient/v1/node.py @@ -28,6 +28,14 @@ class NodeClient(base_v1.BaseV1Client): return result + def get_node_vms_conf(self, node_id): + node = self._entity_wrapper(node_id) + return node.get_node_vms_conf() + + def node_vms_create(self, node_id, config): + node = self._entity_wrapper(node_id) + return node.node_vms_create(config) + def get_client(): return NodeClient() diff --git a/setup.cfg b/setup.cfg index 1571e4f9..f290c69c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,8 +36,11 @@ fuelclient = env_upgrade=fuelclient.commands.environment:EnvUpgrade env_deploy=fuelclient.commands.environment:EnvDeploy env_add_nodes=fuelclient.commands.environment:EnvAddNodes + env_spawn-vms=fuelclient.commands.environment:EnvSpawnVms node_list=fuelclient.commands.node:NodeList node_show=fuelclient.commands.node:NodeShow + node_list-vms-conf=fuelclient.commands.node:NodeVmsList + node_create-vms-conf=fuelclient.commands.node:NodeCreateVMsConf task_list=fuelclient.commands.task:TaskList task_show=fuelclient.commands.task:TaskShow