* added new action 'graph' to get deployment graph in DOT format:
- download full graph for cluster 1
Examples:
fuel graph --env 1 --download
fuel graph --env 1 --download > graph.gv
- it's possible to pass 'start', 'end', 'skip' to manipulate what
include in graph
Examples:
fuel graph --env 1 --download --start netconfig
fuel graph --env 1 --download --skip hiera
- get graph with task's parents
Examples:
fuel graph --env 1 --download --parents-for post_deployment
- render graph (optional)
Examples:
fuel graph --render path/to/graph.gv
Rendering requires installed pydot or pygraphivz together with
Graphviz app.
* params that was used to download graph are saved as comments
in file with graph in DOT format
* added requests-mock for http requests mocking
* improved readability of tasks related args:
- removed redundant 'flag' argument causing to display arg two
times in help
- added metavar 'TASK' to tell better what we expect
Depends on: Id6fe85efe2549a63737ad50e5e55a70a480c83ab
Implements: blueprint granular-deployment-based-on-tasks
Change-Id: I4d60d8b4c8eca53f459a5459cb3f54af09e04306
137 lines
4.7 KiB
Python
137 lines
4.7 KiB
Python
# Copyright 2014 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.
|
|
|
|
from functools import partial
|
|
from functools import wraps
|
|
from itertools import imap
|
|
import os
|
|
|
|
import six
|
|
|
|
from fuelclient.cli import error
|
|
from fuelclient.cli.formatting import quote_and_join
|
|
from fuelclient.cli.serializers import Serializer
|
|
from fuelclient.client import APIClient
|
|
|
|
|
|
class Action(object):
|
|
"""Action class generalizes logic of action execution
|
|
method action_func - entry point of parser with parsed arguments
|
|
|
|
flag_func_map - is tuple of pairs ("flag", self.some_method) where
|
|
"flag" is name of argument which causes "some_method" to be called.
|
|
None is used as "flag" when method will be called without any flag.
|
|
|
|
serializer - is Serializer class instance which supposed to be the
|
|
only way to read and write to output or file system.
|
|
|
|
args - tuple of function calls of functions from arguments module,
|
|
is a manifest of all arguments used in action, and is used to initialize
|
|
argparse subparser of that action.
|
|
"""
|
|
def __init__(self):
|
|
# Mapping of flags to methods
|
|
self.flag_func_map = None
|
|
self.serializer = Serializer()
|
|
|
|
def action_func(self, params):
|
|
"""Entry point for all actions subclasses
|
|
"""
|
|
APIClient.debug_mode(debug=params.debug)
|
|
if getattr(params, 'user') and getattr(params, 'password'):
|
|
APIClient.user = params.user
|
|
APIClient.password = params.password
|
|
# tenant is set by default to 'admin' in parser.add_argument
|
|
APIClient.tenant = params.tenant
|
|
APIClient.initialize_keystone_client()
|
|
|
|
self.serializer = Serializer.from_params(params)
|
|
if self.flag_func_map is not None:
|
|
for flag, method in self.flag_func_map:
|
|
if flag is None or getattr(params, flag):
|
|
method(params)
|
|
break
|
|
|
|
@property
|
|
def examples(self):
|
|
"""examples property is concatenation of __doc__ strings from
|
|
methods in child action classes, and is added as epilog of help
|
|
output
|
|
"""
|
|
methods_with_docs = set(
|
|
method
|
|
for _, method in self.flag_func_map
|
|
)
|
|
return "Examples:\n\n" + \
|
|
"\n".join(
|
|
imap(
|
|
lambda method: (
|
|
"\t" + method.__doc__.replace("\n ", "\n")
|
|
),
|
|
methods_with_docs
|
|
)
|
|
).format(
|
|
action_name=self.action_name
|
|
)
|
|
|
|
def full_path_directory(self, directory, base_name):
|
|
full_path = os.path.join(directory, base_name)
|
|
if not os.path.exists(full_path):
|
|
try:
|
|
os.mkdir(full_path)
|
|
except OSError as e:
|
|
raise error.ActionException(six.text_type(e))
|
|
return full_path
|
|
|
|
def default_directory(self, directory=None):
|
|
return os.path.abspath(os.curdir if directory is None else directory)
|
|
|
|
|
|
def wrap(method, args, f):
|
|
"""wrap - is second order function, purpose of which is to
|
|
generalize argument checking for methods in actions in form
|
|
of decorator with arguments.
|
|
|
|
'check_all' and 'check_any' are partial function of wrap.
|
|
"""
|
|
@wraps(f)
|
|
def wrapped_f(self, params):
|
|
if method(getattr(params, _arg) for _arg in args):
|
|
return f(self, params)
|
|
else:
|
|
raise error.ArgumentException(
|
|
"{0} required!".format(
|
|
quote_and_join(
|
|
"--" + arg for arg in args
|
|
)
|
|
)
|
|
)
|
|
return wrapped_f
|
|
|
|
|
|
def check_all(*args):
|
|
"""check_all - decorator with arguments, which checks that
|
|
all arguments are given before running action method, if
|
|
not all arguments are given, it raises an ArgumentException.
|
|
"""
|
|
return partial(wrap, all, args)
|
|
|
|
|
|
def check_any(*args):
|
|
"""check_any - decorator with arguments, which checks that
|
|
at least one arguments is given before running action method,
|
|
if no arguments were given, it raises an ArgumentException.
|
|
"""
|
|
return partial(wrap, any, args)
|