Add custom graph download and list support to the Fuel V2 CLI

Following commands are added:

* fuel2 graph download --env env_id --all [--type graph_type] [--file cluster_graph.yaml]

* fuel2 graph download --env env_id --cluster [--type graph_type] [--file cluster_graph.yaml]

* fuel2 graph download --env env_id --plugins [--type graph_type] [--file plugins_graph.yaml]

* fuel2 graph download --env env_id --release [--type graph_type] [--file release_graph.yaml]

--type is optional and ‘default’ graph will be downloaded in no type is
defined.

* fuel2 graph list --env env_id

Change-Id: Iba9255f2b201ffc54e81e0f15ba7b9e415ef63ba
Closes-Bug: #1563851
DocImpact
This commit is contained in:
Ilya Kutukov
2016-04-05 21:15:10 +03:00
parent 7a9cd8abec
commit 85311221ca
5 changed files with 252 additions and 0 deletions

View File

@@ -19,6 +19,7 @@ import os
from fuelclient.cli import error
from fuelclient.cli.serializers import Serializer
from fuelclient.commands import base
from fuelclient.common import data_utils
class FileMethodsMixin(object):
@@ -148,3 +149,134 @@ class GraphExecute(base.BaseCommand):
self.app.stdout.write(
"Deployment was executed\n"
)
class GraphDownload(base.BaseCommand):
"""Download deployment graph configuration."""
entity_name = 'graph'
def get_parser(self, prog_name):
parser = super(GraphDownload, self).get_parser(prog_name)
tasks_level = parser.add_mutually_exclusive_group()
parser.add_argument('-e',
'--env',
type=int,
required=True,
help='Id of the environment')
tasks_level.add_argument('-a',
'--all',
action="store_true",
required=False,
default=False,
help='Download merged graph for the '
'environment')
tasks_level.add_argument('-c',
'--cluster',
action="store_true",
required=False,
default=False,
help='Download cluster-specific tasks')
tasks_level.add_argument('-p',
'--plugins',
action="store_true",
required=False,
default=False,
help='Download plugins-specific tasks')
tasks_level.add_argument('-r',
'--release',
action="store_true",
required=False,
default=False,
help='Download release-specific tasks')
parser.add_argument('-t',
'--type',
type=str,
default=None,
required=False,
help='Graph type string')
parser.add_argument('-f',
'--file',
type=str,
required=False,
default=None,
help='YAML file that contains tasks data.')
return parser
@classmethod
def get_default_tasks_data_path(cls):
return os.path.join(
os.path.abspath(os.curdir),
"cluster_graph"
)
@classmethod
def write_tasks_to_file(cls, tasks_data, serializer=None, file_path=None):
serializer = serializer or Serializer()
if file_path:
return serializer.write_to_full_path(
file_path,
tasks_data
)
else:
return serializer.write_to_path(
cls.get_default_tasks_data_path(),
tasks_data
)
def take_action(self, args):
tasks_data = []
for tasks_level_name in ('all', 'cluster', 'release', 'plugins'):
if getattr(args, tasks_level_name):
tasks_data = self.client.download(
env_id=args.env,
level=tasks_level_name,
graph_type=args.type
)
break
# write to file
graph_data_file_path = self.write_tasks_to_file(
tasks_data=tasks_data,
serializer=Serializer(),
file_path=args.file)
self.app.stdout.write(
"Tasks were downloaded to {0}\n".format(graph_data_file_path)
)
class GraphList(base.BaseListCommand):
"""Upload deployment graph configuration."""
entity_name = 'graph'
columns = ("id",
"name",
"tasks",
"relations")
def get_parser(self, prog_name):
parser = super(GraphList, self).get_parser(prog_name)
parser.add_argument('-e',
'--env',
type=int,
required=True,
help='Id of the environment')
return parser
def take_action(self, parsed_args):
data = self.client.list(
env_id=parsed_args.env
)
# format fields
for d in data:
d['relations'] = "\n".join(
'as "{type}" to {model}(ID={model_id})'
.format(**r) for r in d['relations']
)
d['tasks'] = ', '.join(sorted(t['id'] for t in d['tasks']))
data = data_utils.get_display_data_multi(self.columns, data)
scolumn_ids = [self.columns.index(col)
for col in parsed_args.sort_columns]
data.sort(key=lambda x: [x[scolumn_id] for scolumn_id in scolumn_ids])
return self.columns, data

View File

@@ -15,6 +15,7 @@
# under the License.
import mock
import six
import yaml
from fuelclient.tests.unit.v2.cli import test_engine
@@ -86,3 +87,44 @@ class TestGraphActions(test_engine.BaseCLITest):
nodes=[1, 2, 3]
)
)
def test_download(self):
self._test_cmd(
'download',
'--env 1 --all --file existing_graph.yaml --type custom_graph',
dict(
env_id=1,
level='all',
graph_type='custom_graph'
)
)
def test_list(self):
with mock.patch('sys.stdout', new=six.moves.cStringIO()) as m_stdout:
self.m_get_client.reset_mock()
self.m_client.get_filtered.reset_mock()
self.m_client.list.return_value = [
{
'name': 'updated-graph-name',
'tasks': [{
'id': 'test-task2',
'type': 'puppet',
'task_name': 'test-task2',
'version': '2.0.0'
}],
'relations': [{
'model': 'cluster',
'model_id': 370,
'type': 'custom-graph'
}],
'id': 1
}
]
self.exec_command('graph list --env 1')
self.m_get_client.assert_called_once_with('graph', mock.ANY)
self.m_client.list.assert_called_once_with(env_id=1)
self.assertIn('1', m_stdout.getvalue())
self.assertIn('updated-graph-name', m_stdout.getvalue())
self.assertIn('custom-graph', m_stdout.getvalue())
self.assertIn('test-task2', m_stdout.getvalue())

View File

@@ -115,3 +115,19 @@ class TestDeploymentGraphFacade(test_api.BaseLibTest):
nodes=[1, 2, 3],
graph_type="custom_graph")
self.assertTrue(matcher_put.called)
def test_graphs_list(self):
matcher_get = self.m_request.get(
'/api/v1/clusters/1/deployment_graphs/',
json=[]
)
self.client.list(1)
self.assertTrue(matcher_get.called)
def test_graphs_download(self):
matcher_get = self.m_request.get(
'/api/v1/clusters/1/deployment_tasks/?graph_type=custom_graph',
json=[]
)
self.client.download(env_id=1, level='all', graph_type='custom_graph')
self.assertTrue(matcher_get.called)

View File

@@ -31,6 +31,15 @@ class GraphClient(base_v1.BaseV1Client):
cluster_deploy_api_path = "clusters/{env_id}/deploy/"
merged_cluster_tasks_api_path = "clusters/{env_id}/deployment_tasks" \
"/?graph_type={graph_type}"
merged_plugins_tasks_api_path = "clusters/{env_id}/deployment_tasks" \
"/plugins/?graph_type={graph_type}"
cluster_release_tasks_api_path = "clusters/{env_id}/deployment_tasks" \
"/release/?graph_type={graph_type}"
@classmethod
def update_graph_for_model(
cls, data, related_model, related_model_id, graph_type=None):
@@ -92,6 +101,57 @@ class GraphClient(base_v1.BaseV1Client):
deploy_data = APIClient.put_request(url, {})
return objects.DeployTask.init_with_data(deploy_data)
# download
@classmethod
def get_merged_cluster_tasks(cls, env_id, graph_type=None):
return APIClient.get_request(
cls.merged_cluster_tasks_api_path.format(
env_id=env_id,
graph_type=graph_type or ""))
@classmethod
def get_merged_plugins_tasks(cls, env_id, graph_type=None):
return APIClient.get_request(
cls.merged_plugins_tasks_api_path.format(
env_id=env_id,
graph_type=graph_type or ""))
@classmethod
def get_release_tasks_for_cluster(cls, env_id, graph_type=None):
return APIClient.get_request(
cls.merged_plugins_tasks_api_path.format(
env_id=env_id,
graph_type=graph_type or ""))
def download(self, env_id, level, graph_type):
tasks_levels = {
'all': lambda: self.get_merged_cluster_tasks(
env_id=env_id, graph_type=graph_type),
'cluster': lambda: self.get_graph_for_model(
related_model='clusters',
related_model_id=env_id,
graph_type=graph_type).get('tasks', []),
'plugins': lambda: self.get_merged_plugins_tasks(
env_id=env_id,
graph_type=graph_type),
'release': lambda: self.get_release_tasks_for_cluster(
env_id=env_id,
graph_type=graph_type)
}
return tasks_levels[level]()
# list
@classmethod
def list(cls, env_id):
# todo(ikutukov): extend lists to support all models
return APIClient.get_request(
cls.related_graphs_list_api_path.format(
related_model='clusters',
related_model_id=env_id))
def get_client():
return GraphClient()

View File

@@ -39,7 +39,9 @@ fuelclient =
env_spawn-vms=fuelclient.commands.environment:EnvSpawnVms
env_update=fuelclient.commands.environment:EnvUpdate
fuel-version=fuelclient.commands.fuelversion:FuelVersion
graph_download=fuelclient.commands.graph:GraphDownload
graph_execute=fuelclient.commands.graph:GraphExecute
graph_list=fuelclient.commands.graph:GraphList
graph_upload=fuelclient.commands.graph:GraphUpload
network-group_create=fuelclient.commands.network_group:NetworkGroupCreate
network-group_delete=fuelclient.commands.network_group:NetworkGroupDelete