Commands to execute only part of graph
Added several flags to execute particular tasks Examples of commands below: Next command will execute only tasks specified in tasks parameter fuel node --node 2 --tasks hiera netconfig Command with skip, will requests all tasks and remove tasks in skip parameter fuel node --node 2 --skip hiera netconfig Command with end flag, will requests all tasks up to the end node in graph and execute them fuel node --node 2 --end hiera Also end can be combined with skip, and then part of graph will be fetched and part of graph removed fuel node --node 2 --end netconfig --skip hiera netconfig implements blueprint granular-deployment-based-on-tasks Change-Id: I48340bdabe8c1aabc2e47b1a2e153618742981d2
This commit is contained in:
@@ -64,7 +64,12 @@ class NodeAction(Action):
|
||||
Args.get_node_arg("Node id."),
|
||||
Args.get_force_arg("Bypassing parameter validation."),
|
||||
Args.get_all_arg("Select all nodes."),
|
||||
Args.get_role_arg("Role to assign for node.")
|
||||
Args.get_role_arg("Role to assign for node."),
|
||||
group(
|
||||
Args.get_skip_tasks(),
|
||||
Args.get_tasks()
|
||||
),
|
||||
Args.get_graph_endpoint()
|
||||
]
|
||||
|
||||
self.flag_func_map = (
|
||||
@@ -75,6 +80,9 @@ class NodeAction(Action):
|
||||
("deploy", self.start),
|
||||
("provision", self.start),
|
||||
("delete-from-db", self.delete_from_db),
|
||||
("tasks", self.execute_tasks),
|
||||
("skip", self.execute_tasks),
|
||||
("end", self.execute_tasks),
|
||||
(None, self.list)
|
||||
)
|
||||
|
||||
@@ -205,6 +213,14 @@ class NodeAction(Action):
|
||||
" to:\n{1}".format(attribute_type, "\n".join(files))
|
||||
print(message)
|
||||
|
||||
def get_env_id(self, node_collection):
|
||||
env_ids = set(n.env_id for n in node_collection)
|
||||
if len(env_ids) != 1:
|
||||
raise ActionException(
|
||||
"Inputed nodes assigned to multiple environments!")
|
||||
else:
|
||||
return env_ids.pop()
|
||||
|
||||
@check_all("env", "node")
|
||||
def start(self, params):
|
||||
"""Deploy/Provision some node:
|
||||
@@ -213,19 +229,40 @@ class NodeAction(Action):
|
||||
"""
|
||||
node_collection = NodeCollection.init_with_ids(params.node)
|
||||
method_type = "deploy" if params.deploy else "provision"
|
||||
env_ids = set(n.env_id for n in node_collection)
|
||||
if len(env_ids) != 1:
|
||||
raise ActionException(
|
||||
"Inputed nodes assigned to multiple environments!")
|
||||
else:
|
||||
env_id_to_start = env_ids.pop()
|
||||
env_id_to_start = self.get_env_id(node_collection)
|
||||
|
||||
task = Environment(env_id_to_start).install_selected_nodes(
|
||||
method_type, node_collection.collection)
|
||||
|
||||
self.serializer.print_to_output(
|
||||
task.data,
|
||||
"Started {0}ing {1}."
|
||||
.format(method_type, node_collection))
|
||||
|
||||
@check_all("node")
|
||||
def execute_tasks(self, params):
|
||||
"""Execute deployment tasks
|
||||
fuel node --node 2 --tasks hiera netconfig
|
||||
fuel node --node 2 --skip hiera netconfig
|
||||
fuel node --node 2 --skip rsync --end pre_deployment
|
||||
fuel node --node 2 --end netconfig
|
||||
"""
|
||||
node_collection = NodeCollection.init_with_ids(params.node)
|
||||
env_id_to_start = self.get_env_id(node_collection)
|
||||
|
||||
env = Environment(env_id_to_start)
|
||||
|
||||
if params.tasks:
|
||||
tasks = params.tasks
|
||||
else:
|
||||
tasks = env.get_tasks(skip=params.skip, end=params.end)
|
||||
|
||||
task = env.execute_tasks(node_collection.collection, tasks=tasks)
|
||||
|
||||
self.serializer.print_to_output(
|
||||
task.data,
|
||||
"Started tasks {0} for nodes {1}.".format(tasks, node_collection))
|
||||
|
||||
def list(self, params):
|
||||
"""To list all available nodes:
|
||||
fuel node
|
||||
|
||||
@@ -292,6 +292,33 @@ def get_net_arg(help_msg):
|
||||
default="nova")
|
||||
|
||||
|
||||
def get_graph_endpoint():
|
||||
return get_arg(
|
||||
'end',
|
||||
flags=('--end',),
|
||||
action="store",
|
||||
default=None,
|
||||
help="Specify endpoint for the graph of tasks.")
|
||||
|
||||
|
||||
def get_skip_tasks():
|
||||
return get_arg(
|
||||
'skip',
|
||||
flags=('--skip',),
|
||||
nargs = '+',
|
||||
default=[],
|
||||
help="Get list of tasks to be skipped.")
|
||||
|
||||
|
||||
def get_tasks():
|
||||
return get_arg(
|
||||
'tasks',
|
||||
flags=('--tasks',),
|
||||
nargs = '+',
|
||||
default=[],
|
||||
help="Get list of tasks to be executed.")
|
||||
|
||||
|
||||
def get_nst_arg(help_msg):
|
||||
return get_arg("nst",
|
||||
flags=("--net-segment-type",),
|
||||
|
||||
@@ -144,7 +144,7 @@ class Client(object):
|
||||
return resp.json()
|
||||
|
||||
@exceptions_decorator
|
||||
def get_request(self, api, ostf=False):
|
||||
def get_request(self, api, ostf=False, params=None):
|
||||
"""Make GET request to specific API
|
||||
"""
|
||||
url = (self.ostf_root if ostf else self.api_root) + api
|
||||
@@ -154,7 +154,8 @@ class Client(object):
|
||||
)
|
||||
|
||||
headers = {'x-auth-token': self.auth_token}
|
||||
resp = requests.get(url, headers=headers)
|
||||
params = params or {}
|
||||
resp = requests.get(url, params=params, headers=headers)
|
||||
resp.raise_for_status()
|
||||
|
||||
return resp.json()
|
||||
|
||||
@@ -370,9 +370,29 @@ class Environment(BaseObject):
|
||||
)
|
||||
)
|
||||
|
||||
def get_deployment_tasks(self):
|
||||
def execute_tasks(self, nodes, tasks):
|
||||
return Task.init_with_data(
|
||||
self.connection.put_request(
|
||||
self._get_method_url('deploy_tasks', nodes),
|
||||
tasks
|
||||
)
|
||||
)
|
||||
|
||||
def get_tasks(self, skip=None, end=None):
|
||||
"""Stores logic to filter tasks by known parameters.
|
||||
|
||||
:param skip: list of tasks or None
|
||||
:param end: string or None
|
||||
"""
|
||||
tasks = [t['id'] for t in self.get_deployment_tasks(end=end)]
|
||||
if skip:
|
||||
tasks_to_execute = set(tasks) - set(skip)
|
||||
return list(tasks_to_execute)
|
||||
return tasks
|
||||
|
||||
def get_deployment_tasks(self, end=None):
|
||||
url = self.deployment_tasks_path.format(self.id)
|
||||
return self.connection.get_request(url)
|
||||
return self.connection.get_request(url, params={'end': end})
|
||||
|
||||
def update_deployment_tasks(self, data):
|
||||
url = self.deployment_tasks_path.format(self.id)
|
||||
|
||||
@@ -54,3 +54,10 @@ class TestEnvironmentOstf(base.UnitTestCase):
|
||||
self.assertEqual(tests, [
|
||||
{'id': 1, 'status': 'running'},
|
||||
{'id': 2, 'status': 'finished'}])
|
||||
|
||||
@mock.patch('fuelclient.client.requests')
|
||||
def test_get_deployment_tasks_with_end(self, mrequests):
|
||||
end = 'task1'
|
||||
self.env.get_deployment_tasks(end=end)
|
||||
kwargs = mrequests.get.call_args[1]
|
||||
self.assertEqual(kwargs['params'], {'end': end})
|
||||
|
||||
64
fuelclient/tests/test_nodes_execute_tasks.py
Normal file
64
fuelclient/tests/test_nodes_execute_tasks.py
Normal file
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2015 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 json
|
||||
|
||||
from mock import patch
|
||||
|
||||
from fuelclient.tests import base
|
||||
|
||||
|
||||
@patch('fuelclient.client.requests')
|
||||
class TestNodeExecuteTasksAction(base.UnitTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestNodeExecuteTasksAction, self).setUp()
|
||||
self.tasks = ['netconfig', 'hiera', 'install']
|
||||
|
||||
def test_execute_provided_list_of_tasks(self, mrequests):
|
||||
|
||||
self.execute(['fuel', 'node', '--node', '1,2', '--tasks'] + self.tasks)
|
||||
kwargs = mrequests.put.call_args_list[0][1]
|
||||
self.assertEqual(json.loads(kwargs['data']), self.tasks)
|
||||
|
||||
@patch('fuelclient.objects.environment.Environment.get_deployment_tasks')
|
||||
def test_skipped_tasks(self, get_tasks, mrequests):
|
||||
get_tasks.return_value = [{'id': t} for t in self.tasks]
|
||||
self.execute(
|
||||
['fuel', 'node', '--node', '1,2', '--skip'] + self.tasks[:2])
|
||||
|
||||
kwargs = mrequests.put.call_args_list[0][1]
|
||||
self.assertEqual(json.loads(kwargs['data']), self.tasks[2:])
|
||||
|
||||
@patch('fuelclient.objects.environment.Environment.get_deployment_tasks')
|
||||
def test_end_param(self, get_tasks, mrequests):
|
||||
get_tasks.return_value = [{'id': t} for t in self.tasks[:2]]
|
||||
self.execute(
|
||||
['fuel', 'node', '--node', '1,2', '--end', self.tasks[-2]])
|
||||
kwargs = mrequests.put.call_args_list[0][1]
|
||||
self.assertEqual(json.loads(kwargs['data']), self.tasks[:2])
|
||||
get_tasks.assert_called_once_with(end=self.tasks[-2])
|
||||
|
||||
@patch('fuelclient.objects.environment.Environment.get_deployment_tasks')
|
||||
def test_skip_with_end_param(self, get_tasks, mrequests):
|
||||
get_tasks.return_value = [{'id': t} for t in self.tasks]
|
||||
self.execute(
|
||||
['fuel', 'node', '--node', '1,2',
|
||||
'--end', self.tasks[-1], '--skip'] + self.tasks[:2])
|
||||
|
||||
kwargs = mrequests.put.call_args_list[0][1]
|
||||
self.assertEqual(json.loads(kwargs['data']), self.tasks[2:])
|
||||
get_tasks.assert_called_once_with(end=self.tasks[-1])
|
||||
Reference in New Issue
Block a user