From e41e6511a9963f607c20aae2c12e1c04d1440c70 Mon Sep 17 00:00:00 2001 From: Vladimir Kuklin Date: Tue, 9 Aug 2016 19:43:20 +0300 Subject: [PATCH] Add subgraph execution support Add -S option to support subgraph execution. This would allow a user to pick which tasks to stop on. Example: fuel2 graph execute -S keystone-db:openstack-controller/1 will start with all keystone-db tasks and end at openstack-controller at node-1 DocImpact Change-Id: I42f9caf2e9c18072a2ae0447df2ba3500f687d5e Closes-bug: 1612616 --- fuelclient/commands/graph.py | 11 +++++- .../unit/v2/cli/test_deployment_graph.py | 38 ++++++++++++++++--- .../unit/v2/lib/test_deployment_graph.py | 37 +++++++++++++++++- fuelclient/v1/graph.py | 21 +++++++++- 4 files changed, 98 insertions(+), 9 deletions(-) diff --git a/fuelclient/commands/graph.py b/fuelclient/commands/graph.py index 9c1866e9..c73c9780 100644 --- a/fuelclient/commands/graph.py +++ b/fuelclient/commands/graph.py @@ -186,6 +186,15 @@ class GraphExecute(base.BaseTasksExecuteCommand): nargs='+', help='List of deployment tasks to run.' ) + parser.add_argument('-S', + '--subgraphs', + type=str, + nargs='+', + required=False, + help='List of subgraphs to execute' + 'Format is: ' + '[[/]]\ + [:/[]]') return parser def get_options(self, parsed_args): @@ -193,7 +202,7 @@ class GraphExecute(base.BaseTasksExecuteCommand): 'graph_types': parsed_args.graph_types, 'nodes': parsed_args.nodes, 'task_names': parsed_args.task_names, - + 'subgraphs': parsed_args.subgraphs } diff --git a/fuelclient/tests/unit/v2/cli/test_deployment_graph.py b/fuelclient/tests/unit/v2/cli/test_deployment_graph.py index 077a301c..3de9d8a0 100644 --- a/fuelclient/tests/unit/v2/cli/test_deployment_graph.py +++ b/fuelclient/tests/unit/v2/cli/test_deployment_graph.py @@ -178,7 +178,8 @@ class TestGraphActions(test_engine.BaseCLITest): dry_run=False, noop_run=False, force=False, - debug=False + debug=False, + subgraphs=None ) ) @@ -194,7 +195,8 @@ class TestGraphActions(test_engine.BaseCLITest): dry_run=True, noop_run=False, force=False, - debug=False + debug=False, + subgraphs=None ) ) @@ -210,7 +212,8 @@ class TestGraphActions(test_engine.BaseCLITest): dry_run=False, noop_run=False, force=True, - debug=False + debug=False, + subgraphs=None ) ) @@ -226,7 +229,8 @@ class TestGraphActions(test_engine.BaseCLITest): dry_run=False, noop_run=False, force=False, - debug=False + debug=False, + subgraphs=None ) ) @@ -242,7 +246,8 @@ class TestGraphActions(test_engine.BaseCLITest): dry_run=False, noop_run=True, force=False, - debug=False + debug=False, + subgraphs=None ) ) @@ -258,7 +263,28 @@ class TestGraphActions(test_engine.BaseCLITest): dry_run=False, noop_run=False, force=False, - debug=True + debug=True, + subgraphs=None + ) + ) + + def test_execute_w_dry_run_subgraph(self): + self._test_cmd( + 'execute', + '--env 1 --graph-types custom_graph --nodes 1 2 3 ' + '--dry-run --subgraphs primary-database/1,3:keystone-db/1-2,5' + ' openstack-controller', + dict( + env_id=1, + force=False, + graph_types=['custom_graph'], + nodes=[1, 2, 3], + noop_run=False, + dry_run=True, + subgraphs=['primary-database/1,3:keystone-db/1-2,5', + 'openstack-controller'], + task_names=None, + debug=False ) ) diff --git a/fuelclient/tests/unit/v2/lib/test_deployment_graph.py b/fuelclient/tests/unit/v2/lib/test_deployment_graph.py index 03023ca0..522e8175 100644 --- a/fuelclient/tests/unit/v2/lib/test_deployment_graph.py +++ b/fuelclient/tests/unit/v2/lib/test_deployment_graph.py @@ -289,8 +289,43 @@ class TestDeploymentGraphFacade(test_api.BaseLibTest): self.assertItemsEqual( cluster_graphs + not_this_env_cluster_graphs + release_graphs, - self.client.list(filters=['cluster', 'release']) + self.client.list(filters=['cluster', 'release'])) + + def test_new_graph_dry_run_subgraph(self): + matcher_post = self.m_request.post( + '/api/v1/graphs/execute/', + json=utils.get_fake_task(cluster=370)) + # this is required to form running task info + self.m_request.get( + '/api/v1/nodes/?cluster_id=370', + json={} ) + self.client.execute( + env_id=1, + nodes=[1, 2, 3], + graph_types=["custom_graph"], + dry_run=True, + subgraphs=['primary-database/1,3:keystone-db/1-2,5', + 'openstack-controller'] + ) + self.assertTrue(matcher_post.called) + expected_body = {'cluster': 1, + 'dry_run': True, + 'graphs': + [{ + 'nodes': [1, 2, 3], 'type': 'custom_graph', + 'tasks': ['rsync_core_puppet']}, + { + 'nodes': [1, 2, 3], + 'type': 'another_custom_graph', + 'tasks': ['rsync_core_puppet']}], + 'subgraphs': [ + {'start': ['primary-database/1,3'], + 'end': ['keystone-db/1-2,5']}, + {'start': ['openstack-controller'], + 'end': [None]}] + } + self.assertItemsEqual(matcher_post.last_request.json(), expected_body) def test_graphs_download_all(self): matcher_get = self.m_request.get( diff --git a/fuelclient/v1/graph.py b/fuelclient/v1/graph.py index 27bf5a99..28ddffbd 100644 --- a/fuelclient/v1/graph.py +++ b/fuelclient/v1/graph.py @@ -13,6 +13,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +import re import six from fuelclient.cli import error @@ -88,7 +89,7 @@ class GraphClient(base_v1.BaseV1Client): method(data, related_model, related_id, graph_type) def execute(self, env_id, nodes=None, graph_types=None, task_names=None, - **kwargs): + subgraphs=None, **kwargs): request_data = {'cluster': env_id} def map_args_to_graph_types(graph): @@ -100,12 +101,30 @@ class GraphClient(base_v1.BaseV1Client): result['tasks'] = task_names return result + def munge_subgraphs(subgraph): + regexp = re.compile("^([\w\-,]+(?:\/(?:(?:\d+(?:,|-)?)+))?)" + "?(:[\w\-,]+(?:\/(?:(?:\d+(?:,|-)?)+))?)?$") + result = regexp.match(subgraph) + start_vertex = None + end_vertex = None + if result: + if result.group(1): + start_vertex = result.group(1) + if result.group(2): + end_vertex = result.group(2)[1:] + return {'start': [start_vertex], 'end': [end_vertex]} + if graph_types: request_data['graphs'] = list(six.moves.map( map_args_to_graph_types, graph_types )) + if subgraphs: + request_data['subgraphs'] = list( + six.moves.map(lambda s: munge_subgraphs(s), subgraphs)) + request_data.update(kwargs) + deploy_data = self.connection.post_request( self.cluster_deploy_api_path, request_data