Files
python-fuelclient/fuelclient/cli/actions/base.py
Sebastian Kalinowski 0f4ca9c279 Add CLI for graph visualization
* 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
2015-03-11 07:17:55 +00:00

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)