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
(cherry picked from commit 85311221ca)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user