summaryrefslogtreecommitdiffstats
path: root/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli
diff options
context:
space:
mode:
authorStuart Mackie <wsmackie@juniper.net>2016-10-07 12:24:58 -0700
committerStuart Mackie <wsmackie@juniper.net>2016-10-07 12:24:58 -0700
commit4faa7f927149a5c4ef7a03523f7bc14523cb9baa (patch)
tree0be55aa0809cc395e45baeae63db660b4e72fe83 /charms/trusty/ceilometer-agent/hooks/charmhelpers/cli
parent82f1a7eb5535b30a95b1e71ff18c315d40d1e6f0 (diff)
Charms for Contrail 3.1 with Mitaka
Change-Id: Id37f3b9743d1974e31fcd7cd9c54be41bb0c47fb Signed-off-by: Stuart Mackie <wsmackie@juniper.net>
Diffstat (limited to 'charms/trusty/ceilometer-agent/hooks/charmhelpers/cli')
-rw-r--r--charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/__init__.py189
-rw-r--r--charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/benchmark.py34
-rw-r--r--charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/commands.py30
-rw-r--r--charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/hookenv.py21
-rw-r--r--charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/host.py29
-rw-r--r--charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/unitdata.py37
6 files changed, 340 insertions, 0 deletions
diff --git a/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/__init__.py b/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/__init__.py
new file mode 100644
index 0000000..389b490
--- /dev/null
+++ b/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/__init__.py
@@ -0,0 +1,189 @@
+# Copyright 2014-2015 Canonical Limited.
+#
+# 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 inspect
+import argparse
+import sys
+
+from six.moves import zip
+
+import charmhelpers.core.unitdata
+
+
+class OutputFormatter(object):
+ def __init__(self, outfile=sys.stdout):
+ self.formats = (
+ "raw",
+ "json",
+ "py",
+ "yaml",
+ "csv",
+ "tab",
+ )
+ self.outfile = outfile
+
+ def add_arguments(self, argument_parser):
+ formatgroup = argument_parser.add_mutually_exclusive_group()
+ choices = self.supported_formats
+ formatgroup.add_argument("--format", metavar='FMT',
+ help="Select output format for returned data, "
+ "where FMT is one of: {}".format(choices),
+ choices=choices, default='raw')
+ for fmt in self.formats:
+ fmtfunc = getattr(self, fmt)
+ formatgroup.add_argument("-{}".format(fmt[0]),
+ "--{}".format(fmt), action='store_const',
+ const=fmt, dest='format',
+ help=fmtfunc.__doc__)
+
+ @property
+ def supported_formats(self):
+ return self.formats
+
+ def raw(self, output):
+ """Output data as raw string (default)"""
+ if isinstance(output, (list, tuple)):
+ output = '\n'.join(map(str, output))
+ self.outfile.write(str(output))
+
+ def py(self, output):
+ """Output data as a nicely-formatted python data structure"""
+ import pprint
+ pprint.pprint(output, stream=self.outfile)
+
+ def json(self, output):
+ """Output data in JSON format"""
+ import json
+ json.dump(output, self.outfile)
+
+ def yaml(self, output):
+ """Output data in YAML format"""
+ import yaml
+ yaml.safe_dump(output, self.outfile)
+
+ def csv(self, output):
+ """Output data as excel-compatible CSV"""
+ import csv
+ csvwriter = csv.writer(self.outfile)
+ csvwriter.writerows(output)
+
+ def tab(self, output):
+ """Output data in excel-compatible tab-delimited format"""
+ import csv
+ csvwriter = csv.writer(self.outfile, dialect=csv.excel_tab)
+ csvwriter.writerows(output)
+
+ def format_output(self, output, fmt='raw'):
+ fmtfunc = getattr(self, fmt)
+ fmtfunc(output)
+
+
+class CommandLine(object):
+ argument_parser = None
+ subparsers = None
+ formatter = None
+ exit_code = 0
+
+ def __init__(self):
+ if not self.argument_parser:
+ self.argument_parser = argparse.ArgumentParser(description='Perform common charm tasks')
+ if not self.formatter:
+ self.formatter = OutputFormatter()
+ self.formatter.add_arguments(self.argument_parser)
+ if not self.subparsers:
+ self.subparsers = self.argument_parser.add_subparsers(help='Commands')
+
+ def subcommand(self, command_name=None):
+ """
+ Decorate a function as a subcommand. Use its arguments as the
+ command-line arguments"""
+ def wrapper(decorated):
+ cmd_name = command_name or decorated.__name__
+ subparser = self.subparsers.add_parser(cmd_name,
+ description=decorated.__doc__)
+ for args, kwargs in describe_arguments(decorated):
+ subparser.add_argument(*args, **kwargs)
+ subparser.set_defaults(func=decorated)
+ return decorated
+ return wrapper
+
+ def test_command(self, decorated):
+ """
+ Subcommand is a boolean test function, so bool return values should be
+ converted to a 0/1 exit code.
+ """
+ decorated._cli_test_command = True
+ return decorated
+
+ def no_output(self, decorated):
+ """
+ Subcommand is not expected to return a value, so don't print a spurious None.
+ """
+ decorated._cli_no_output = True
+ return decorated
+
+ def subcommand_builder(self, command_name, description=None):
+ """
+ Decorate a function that builds a subcommand. Builders should accept a
+ single argument (the subparser instance) and return the function to be
+ run as the command."""
+ def wrapper(decorated):
+ subparser = self.subparsers.add_parser(command_name)
+ func = decorated(subparser)
+ subparser.set_defaults(func=func)
+ subparser.description = description or func.__doc__
+ return wrapper
+
+ def run(self):
+ "Run cli, processing arguments and executing subcommands."
+ arguments = self.argument_parser.parse_args()
+ argspec = inspect.getargspec(arguments.func)
+ vargs = []
+ for arg in argspec.args:
+ vargs.append(getattr(arguments, arg))
+ if argspec.varargs:
+ vargs.extend(getattr(arguments, argspec.varargs))
+ output = arguments.func(*vargs)
+ if getattr(arguments.func, '_cli_test_command', False):
+ self.exit_code = 0 if output else 1
+ output = ''
+ if getattr(arguments.func, '_cli_no_output', False):
+ output = ''
+ self.formatter.format_output(output, arguments.format)
+ if charmhelpers.core.unitdata._KV:
+ charmhelpers.core.unitdata._KV.flush()
+
+
+cmdline = CommandLine()
+
+
+def describe_arguments(func):
+ """
+ Analyze a function's signature and return a data structure suitable for
+ passing in as arguments to an argparse parser's add_argument() method."""
+
+ argspec = inspect.getargspec(func)
+ # we should probably raise an exception somewhere if func includes **kwargs
+ if argspec.defaults:
+ positional_args = argspec.args[:-len(argspec.defaults)]
+ keyword_names = argspec.args[-len(argspec.defaults):]
+ for arg, default in zip(keyword_names, argspec.defaults):
+ yield ('--{}'.format(arg),), {'default': default}
+ else:
+ positional_args = argspec.args
+
+ for arg in positional_args:
+ yield (arg,), {}
+ if argspec.varargs:
+ yield (argspec.varargs,), {'nargs': '*'}
diff --git a/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/benchmark.py b/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/benchmark.py
new file mode 100644
index 0000000..303af14
--- /dev/null
+++ b/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/benchmark.py
@@ -0,0 +1,34 @@
+# Copyright 2014-2015 Canonical Limited.
+#
+# 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 . import cmdline
+from charmhelpers.contrib.benchmark import Benchmark
+
+
+@cmdline.subcommand(command_name='benchmark-start')
+def start():
+ Benchmark.start()
+
+
+@cmdline.subcommand(command_name='benchmark-finish')
+def finish():
+ Benchmark.finish()
+
+
+@cmdline.subcommand_builder('benchmark-composite', description="Set the benchmark composite score")
+def service(subparser):
+ subparser.add_argument("value", help="The composite score.")
+ subparser.add_argument("units", help="The units the composite score represents, i.e., 'reads/sec'.")
+ subparser.add_argument("direction", help="'asc' if a lower score is better, 'desc' if a higher score is better.")
+ return Benchmark.set_composite_score
diff --git a/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/commands.py b/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/commands.py
new file mode 100644
index 0000000..b931056
--- /dev/null
+++ b/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/commands.py
@@ -0,0 +1,30 @@
+# Copyright 2014-2015 Canonical Limited.
+#
+# 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.
+
+"""
+This module loads sub-modules into the python runtime so they can be
+discovered via the inspect module. In order to prevent flake8 from (rightfully)
+telling us these are unused modules, throw a ' # noqa' at the end of each import
+so that the warning is suppressed.
+"""
+
+from . import CommandLine # noqa
+
+"""
+Import the sub-modules which have decorated subcommands to register with chlp.
+"""
+from . import host # noqa
+from . import benchmark # noqa
+from . import unitdata # noqa
+from . import hookenv # noqa
diff --git a/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/hookenv.py b/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/hookenv.py
new file mode 100644
index 0000000..bd72f44
--- /dev/null
+++ b/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/hookenv.py
@@ -0,0 +1,21 @@
+# Copyright 2014-2015 Canonical Limited.
+#
+# 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 . import cmdline
+from charmhelpers.core import hookenv
+
+
+cmdline.subcommand('relation-id')(hookenv.relation_id._wrapped)
+cmdline.subcommand('service-name')(hookenv.service_name)
+cmdline.subcommand('remote-service-name')(hookenv.remote_service_name._wrapped)
diff --git a/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/host.py b/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/host.py
new file mode 100644
index 0000000..4039684
--- /dev/null
+++ b/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/host.py
@@ -0,0 +1,29 @@
+# Copyright 2014-2015 Canonical Limited.
+#
+# 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 . import cmdline
+from charmhelpers.core import host
+
+
+@cmdline.subcommand()
+def mounts():
+ "List mounts"
+ return host.mounts()
+
+
+@cmdline.subcommand_builder('service', description="Control system services")
+def service(subparser):
+ subparser.add_argument("action", help="The action to perform (start, stop, etc...)")
+ subparser.add_argument("service_name", help="Name of the service to control")
+ return host.service
diff --git a/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/unitdata.py b/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/unitdata.py
new file mode 100644
index 0000000..c572858
--- /dev/null
+++ b/charms/trusty/ceilometer-agent/hooks/charmhelpers/cli/unitdata.py
@@ -0,0 +1,37 @@
+# Copyright 2014-2015 Canonical Limited.
+#
+# 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 . import cmdline
+from charmhelpers.core import unitdata
+
+
+@cmdline.subcommand_builder('unitdata', description="Store and retrieve data")
+def unitdata_cmd(subparser):
+ nested = subparser.add_subparsers()
+ get_cmd = nested.add_parser('get', help='Retrieve data')
+ get_cmd.add_argument('key', help='Key to retrieve the value of')
+ get_cmd.set_defaults(action='get', value=None)
+ set_cmd = nested.add_parser('set', help='Store data')
+ set_cmd.add_argument('key', help='Key to set')
+ set_cmd.add_argument('value', help='Value to store')
+ set_cmd.set_defaults(action='set')
+
+ def _unitdata_cmd(action, key, value):
+ if action == 'get':
+ return unitdata.kv().get(key)
+ elif action == 'set':
+ unitdata.kv().set(key, value)
+ unitdata.kv().flush()
+ return ''
+ return _unitdata_cmd