Merge tag '1.0.0' into debian/newton
python-senlinclient 1.0.0 release * New upstream release. * Fixed (build-)depends for this release. Change-Id: I0176a850863b5b624c5d699792f0fe6eb217fac5
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/python-senlinclient.git
|
||||
project=openstack/deb-python-senlinclient.git
|
||||
defaultbranch=debian/newton
|
||||
|
||||
7
debian/changelog
vendored
7
debian/changelog
vendored
@@ -1,3 +1,10 @@
|
||||
python-senlinclient (1.0.0-1) experimental; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
* Fixed (build-)depends for this release.
|
||||
|
||||
-- Thomas Goirand <zigo@debian.org> Wed, 14 Sep 2016 09:25:36 +0200
|
||||
|
||||
python-senlinclient (0.5.0-1) experimental; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
|
||||
42
debian/control
vendored
42
debian/control
vendored
@@ -17,42 +17,40 @@ Build-Depends: debhelper (>= 9),
|
||||
python3-setuptools,
|
||||
Build-Depends-Indep: git,
|
||||
python-babel (>= 2.3.4),
|
||||
python-cliff (>= 1.15.0),
|
||||
python-coverage,
|
||||
python-fixtures (>= 3.0.0),
|
||||
python-hacking,
|
||||
python-heatclient (>= 1.1.0),
|
||||
python-heatclient (>= 1.4.0),
|
||||
python-mock (>= 2.0),
|
||||
python-mox3 (>= 0.7.0),
|
||||
python-openstacksdk (>= 0.8.6),
|
||||
python-openstackclient (>= 2.1.0),
|
||||
python-openstacksdk (>= 0.9.4),
|
||||
python-osc-lib (>= 1.0.2),
|
||||
python-oslo.i18n (>= 2.1.0),
|
||||
python-oslo.serialization (>= 1.10.0),
|
||||
python-oslo.utils (>= 3.11.0),
|
||||
python-oslo.utils (>= 3.16.0),
|
||||
python-oslosphinx (>= 2.5.0),
|
||||
python-oslotest (>= 1.10.0),
|
||||
python-prettytable,
|
||||
python-requests (>= 2.10.0),
|
||||
python-requests-mock (>= 0.7.0),
|
||||
python-requests-mock (>= 1.0.0),
|
||||
python-six (>= 1.9.0),
|
||||
python-testscenarios,
|
||||
python-testtools (>= 1.4.0),
|
||||
python-yaml,
|
||||
python3-babel (>= 2.3.4),
|
||||
python3-cliff (>= 1.15.0),
|
||||
python3-fixtures (>= 3.0.0),
|
||||
python3-heatclient (>= 1.1.0),
|
||||
python3-heatclient (>= 1.4.0),
|
||||
python3-mock (>= 2.0),
|
||||
python3-mox3 (>= 0.7.0),
|
||||
python3-openstacksdk (>= 0.8.6),
|
||||
python3-openstackclient (>= 2.1.0),
|
||||
python3-openstacksdk (>= 0.9.4),
|
||||
python3-osc-lib (>= 1.0.2),
|
||||
python3-oslo.i18n (>= 2.1.0),
|
||||
python3-oslo.serialization (>= 1.10.0),
|
||||
python3-oslo.utils (>= 3.11.0),
|
||||
python3-oslo.utils (>= 3.16.0),
|
||||
python3-oslotest (>= 1.10.0),
|
||||
python3-prettytable,
|
||||
python3-requests (>= 2.10.0),
|
||||
python3-requests-mock (>= 0.7.0),
|
||||
python3-requests-mock (>= 1.0.0),
|
||||
python3-six (>= 1.9.0),
|
||||
python3-subunit,
|
||||
python3-testscenarios,
|
||||
@@ -61,20 +59,20 @@ Build-Depends-Indep: git,
|
||||
subunit,
|
||||
testrepository,
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Browser: https://anonscm.debian.org/cgit/openstack/python-senlinclient.git/
|
||||
Vcs-Git: https://anonscm.debian.org/git/openstack/python-senlinclient.git
|
||||
Vcs-Browser: https://git.openstack.org/cgit/openstack/deb-python-senlinclient
|
||||
Vcs-Git: https://git.openstack.org/openstack/deb-python-senlinclient
|
||||
Homepage: https://github.com/openstack/python-senlinclient
|
||||
|
||||
Package: python-senlinclient
|
||||
Architecture: all
|
||||
Depends: python-babel (>= 2.3.4),
|
||||
python-cliff (>= 1.15.0),
|
||||
python-heatclient (>= 1.1.0),
|
||||
python-openstacksdk (>= 0.8.6),
|
||||
python-heatclient (>= 1.4.0),
|
||||
python-openstackclient (>= 2.1.0),
|
||||
python-openstacksdk (>= 0.9.4),
|
||||
python-osc-lib (>= 1.0.2),
|
||||
python-oslo.i18n (>= 1.5.0),
|
||||
python-oslo.serialization (>= 1.10.0),
|
||||
python-oslo.utils (>= 3.11.0),
|
||||
python-oslo.utils (>= 3.16.0),
|
||||
python-pbr (>= 1.8),
|
||||
python-prettytable,
|
||||
python-requests (>= 2.10.0),
|
||||
@@ -93,13 +91,13 @@ Description: OpenStack Clustering API Client Library - Python 2.7
|
||||
Package: python3-senlinclient
|
||||
Architecture: all
|
||||
Depends: python3-babel (>= 2.3.4),
|
||||
python3-cliff (>= 1.15.0),
|
||||
python3-heatclient (>= 1.1.0),
|
||||
python3-openstacksdk (>= 0.8.6),
|
||||
python3-heatclient (>= 1.4.0),
|
||||
python3-openstackclient (>= 2.1.0),
|
||||
python3-openstacksdk (>= 0.9.4),
|
||||
python3-osc-lib (>= 1.0.2),
|
||||
python3-oslo.i18n (>= 1.5.0),
|
||||
python3-oslo.serialization (>= 1.10.0),
|
||||
python3-oslo.utils (>= 3.11.0),
|
||||
python3-oslo.utils (>= 3.16.0),
|
||||
python3-pbr (>= 1.8),
|
||||
python3-prettytable,
|
||||
python3-requests (>= 2.10.0),
|
||||
|
||||
4
releasenotes/notes/cli-deprecation-241b9569b85f8fbd.yaml
Normal file
4
releasenotes/notes/cli-deprecation-241b9569b85f8fbd.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
other:
|
||||
- The 'senlin' CLI will be removed in April 2017. This message is now
|
||||
explicitly printed when senlin CLI commands are invoked.
|
||||
5
releasenotes/notes/cluster-collect-a9d1bc8c2e799c7c.yaml
Normal file
5
releasenotes/notes/cluster-collect-a9d1bc8c2e799c7c.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- A new command 'senlin cluster-collect' and its corresponding OSC plugin
|
||||
command has been added. This new command can be used to aggregate a
|
||||
specific property across a cluster.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
fixes:
|
||||
- The cluster policy list command was broken by new SDK changes and then
|
||||
fixed. The 'enabled' field is now renamed to 'is_enabled'.
|
||||
5
releasenotes/notes/cluster-run-210247ab70b289a5.yaml
Normal file
5
releasenotes/notes/cluster-run-210247ab70b289a5.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- A new CLI command 'senlin cluster-run' and a new OSC plugin command
|
||||
'openstack cluster run' have been added. Use the 'help' command to find
|
||||
out how to use it.
|
||||
5
releasenotes/notes/deletion-output-a841931367a2689d.yaml
Normal file
5
releasenotes/notes/deletion-output-a841931367a2689d.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- The senlin CLI 'node-delete' and the OSC plugin command
|
||||
'cluster node delete' now outputs the action IDs when successful. Error
|
||||
messages are printed when appropriate.
|
||||
4
releasenotes/notes/micro-version-a292ce3b00d886af.yaml
Normal file
4
releasenotes/notes/micro-version-a292ce3b00d886af.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- The senlinclient now supports API micro-versioning. Current supported
|
||||
version is 'clustering 1.2'.
|
||||
4
releasenotes/notes/policy-validate-193a5ebb7db3440a.yaml
Normal file
4
releasenotes/notes/policy-validate-193a5ebb7db3440a.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- A policy-validate command has been added to senlin command line.
|
||||
OSC support is added as well.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- A profile-validate command has been added to command line. It can be
|
||||
used for validating the spec of a profile without creating it.
|
||||
3
releasenotes/notes/python-3.5-c9fd8e34c4046357.yaml
Normal file
3
releasenotes/notes/python-3.5-c9fd8e34c4046357.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- The support to python 3.5 has been verified and gated.
|
||||
5
releasenotes/notes/receiver-create-8305d4efbdf35f35.yaml
Normal file
5
releasenotes/notes/receiver-create-8305d4efbdf35f35.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
other:
|
||||
- The receiver creation command (both senlin CLI and OSC plugin command)
|
||||
now allow 'cluster' and 'action' to be left unspecified if the receiver
|
||||
type is not 'webhook'.
|
||||
@@ -12,6 +12,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import pbr.version
|
||||
|
||||
# Senlin Release Notes documentation build configuration file, created by
|
||||
# sphinx-quickstart on Tue Nov 3 17:40:50 2015.
|
||||
#
|
||||
@@ -63,8 +65,8 @@ copyright = u'2015, Senlin Developers'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
import pbr.version
|
||||
senlin_version = pbr.version.VersionInfo('python-muranoclient')
|
||||
|
||||
senlin_version = pbr.version.VersionInfo('python-senlinclient')
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = senlin_version.version_string_with_vcs()
|
||||
# The short X.Y version.
|
||||
|
||||
40
releasenotes/source/locale/zh_CN/LC_MESSAGES/releasenotes.po
Normal file
40
releasenotes/source/locale/zh_CN/LC_MESSAGES/releasenotes.po
Normal file
@@ -0,0 +1,40 @@
|
||||
# zzxwill <zzxwill@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Senlin Client Release Notes 0.5.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-07-04 03:44+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-06-25 02:41+0000\n"
|
||||
"Last-Translator: zzxwill <zzxwill@gmail.com>\n"
|
||||
"Language-Team: Chinese (China)\n"
|
||||
"Language: zh-CN\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "0.5.0"
|
||||
msgstr "0.5.0"
|
||||
|
||||
msgid "Added command for node-check and node-recover."
|
||||
msgstr "已为node-check和node-recover添加了命令。"
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "当前版本发布说明"
|
||||
|
||||
msgid "New Features"
|
||||
msgstr "新特性"
|
||||
|
||||
msgid ""
|
||||
"OSC commands for cluster scaling are changed from 'cluster scale in' and "
|
||||
"'cluster scale out' to 'cluster shrink' and 'cluster expand' respectively."
|
||||
msgstr ""
|
||||
"集群扩展的OSC命令分别从'cluster scale in'和'cluster scale out'改成了'cluster "
|
||||
"shrink'和'cluster expand'。"
|
||||
|
||||
msgid "Senlin Client Release Notes"
|
||||
msgstr "Senlin Client发布说明"
|
||||
|
||||
msgid "Upgrade Notes"
|
||||
msgstr "升级说明"
|
||||
@@ -4,14 +4,13 @@
|
||||
|
||||
Babel>=2.3.4 # BSD
|
||||
pbr>=1.6 # Apache-2.0
|
||||
cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0
|
||||
PrettyTable<0.8,>=0.7 # BSD
|
||||
openstacksdk>=0.8.6 # Apache-2.0
|
||||
openstacksdk>=0.9.4 # Apache-2.0
|
||||
osc-lib>=1.0.2 # Apache-2.0
|
||||
oslo.i18n>=2.1.0 # Apache-2.0
|
||||
oslo.serialization>=1.10.0 # Apache-2.0
|
||||
oslo.utils>=3.11.0 # Apache-2.0
|
||||
python-heatclient>=1.1.0 # Apache-2.0
|
||||
python-openstackclient>=2.1.0 # Apache-2.0
|
||||
oslo.utils>=3.16.0 # Apache-2.0
|
||||
python-heatclient>=1.4.0 # Apache-2.0
|
||||
PyYAML>=3.1.0 # MIT
|
||||
requests>=2.10.0 # Apache-2.0
|
||||
six>=1.9.0 # MIT
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
import argparse
|
||||
|
||||
from senlinclient.common.i18n import _
|
||||
from senlinclient.common import sdk
|
||||
from senlinclient.common import utils
|
||||
|
||||
|
||||
@@ -140,35 +139,6 @@ def add_global_identity_args(parser):
|
||||
default=utils.env('OS_ACCESS_INFO'),
|
||||
help=_('Access info, defaults to env[OS_ACCESS_INFO]'))
|
||||
|
||||
parser.add_argument(
|
||||
'--os-api-name', dest='user_preferences',
|
||||
metavar='<service>=<name>',
|
||||
action=sdk.ProfileAction,
|
||||
default=sdk.ProfileAction.env('OS_API_NAME'),
|
||||
help=_('Desired API names, defaults to env[OS_API_NAME]'))
|
||||
|
||||
parser.add_argument(
|
||||
'--os-api-region', dest='user_preferences',
|
||||
metavar='<service>=<region>',
|
||||
action=sdk.ProfileAction,
|
||||
default=sdk.ProfileAction.env('OS_API_REGION', 'OS_REGION_NAME'),
|
||||
help=_('Desired API region, defaults to env[OS_API_REGION]'))
|
||||
|
||||
parser.add_argument(
|
||||
'--os-api-version', dest='user_preferences',
|
||||
metavar='<service>=<version>',
|
||||
action=sdk.ProfileAction,
|
||||
default=sdk.ProfileAction.env('OS_API_VERSION'),
|
||||
help=_('Desired API versions, defaults to env[OS_API_VERSION]'))
|
||||
|
||||
parser.add_argument(
|
||||
'--os-api-interface', dest='user_preferences',
|
||||
metavar='<service>=<interface>',
|
||||
action=sdk.ProfileAction,
|
||||
default=sdk.ProfileAction.env('OS_INTERFACE'),
|
||||
help=_('Desired API interface, defaults to env[OS_INTERFACE]'))
|
||||
|
||||
|
||||
# parser.add_argument(
|
||||
# '--os-cert',
|
||||
# help=_('Path of certificate file to use in SSL connection. This '
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from cliff import show
|
||||
from osc_lib.command import command
|
||||
|
||||
|
||||
class RawFormat(show.ShowOne):
|
||||
class RawFormat(command.ShowOne):
|
||||
|
||||
def produce_output(self, parsed_args, column_names, data):
|
||||
if data is None:
|
||||
|
||||
@@ -10,118 +10,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import argparse
|
||||
from openstack import connection
|
||||
from openstack import exceptions
|
||||
from openstack import profile
|
||||
from openstack import resource as base
|
||||
from openstack import utils
|
||||
import os
|
||||
|
||||
from six.moves.urllib import parse as url_parse
|
||||
|
||||
from senlinclient.common import exc
|
||||
|
||||
# Alias here for consistency
|
||||
prop = base.prop
|
||||
|
||||
|
||||
class ProfileAction(argparse.Action):
|
||||
"""A custom action to parse user preferences as key=value pairs
|
||||
|
||||
Stores results in users preferences object.
|
||||
"""
|
||||
prof = profile.Profile()
|
||||
|
||||
@classmethod
|
||||
def env(cls, *vars):
|
||||
for v in vars:
|
||||
values = os.environ.get(v, None)
|
||||
if values is None:
|
||||
continue
|
||||
cls.set_option(v, values)
|
||||
return cls.prof
|
||||
return cls.prof
|
||||
|
||||
@classmethod
|
||||
def set_option(cls, var, values):
|
||||
if var == '--os-extensions':
|
||||
cls.prof.load_extension(values)
|
||||
return
|
||||
if var == 'OS_REGION_NAME':
|
||||
var = 'region'
|
||||
var = var.replace('--os-api-', '')
|
||||
var = var.replace('OS_API_', '')
|
||||
var = var.lower()
|
||||
for kvp in values.split(','):
|
||||
if '=' in kvp:
|
||||
service, value = kvp.split('=')
|
||||
else:
|
||||
service = cls.prof.ALL
|
||||
value = kvp
|
||||
if var == 'name':
|
||||
cls.prof.set_name(service, value)
|
||||
elif var == 'region':
|
||||
cls.prof.set_region(service, value)
|
||||
elif var == 'version':
|
||||
cls.prof.set_version(service, value)
|
||||
elif var == 'interface':
|
||||
cls.prof.set_interface(service, value)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
if getattr(namespace, self.dest, None) is None:
|
||||
setattr(namespace, self.dest, ProfileAction.prof)
|
||||
self.set_option(option_string, values)
|
||||
|
||||
|
||||
class Resource(base.Resource):
|
||||
"""Senlin version of resource.
|
||||
|
||||
These classes are here because the OpenStack SDK base version is making
|
||||
some assumptions about operations that cannot be satisfied in Senlin.
|
||||
"""
|
||||
|
||||
def create(self, session, extra_attrs=False):
|
||||
"""Create a remote resource from this instance.
|
||||
|
||||
:param extra_attrs: If true, all attributions that
|
||||
included in response will be collected and returned
|
||||
to user after resource creation
|
||||
|
||||
"""
|
||||
resp = self.create_by_id(session, self._attrs, self.id, path_args=self)
|
||||
self._attrs[self.id_attribute] = resp[self.id_attribute]
|
||||
if extra_attrs:
|
||||
for attr in resp:
|
||||
self._attrs[attr] = resp[attr]
|
||||
self._reset_dirty()
|
||||
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def get_data_with_args(cls, session, resource_id, args=None):
|
||||
if not cls.allow_retrieve:
|
||||
raise exceptions.MethodNotSupported('list')
|
||||
|
||||
url = utils.urljoin(cls.base_path, resource_id)
|
||||
if args:
|
||||
args.pop('id')
|
||||
url = '%s?%s' % (url, url_parse.urlencode(args))
|
||||
resp = session.get(url, endpoint_filter=cls.service)
|
||||
body = resp.json()
|
||||
if cls.resource_key:
|
||||
body = body[cls.resource_key]
|
||||
|
||||
return body
|
||||
|
||||
def get_with_args(self, session, args=None):
|
||||
body = self.get_data_with_args(session, self.id, args=args)
|
||||
|
||||
self._attrs.update(body)
|
||||
self._loaded = True
|
||||
|
||||
return self
|
||||
|
||||
|
||||
def create_connection(prof=None, user_agent=None, **kwargs):
|
||||
if not prof:
|
||||
@@ -133,6 +27,7 @@ def create_connection(prof=None, user_agent=None, **kwargs):
|
||||
if region_name:
|
||||
prof.set_region('clustering', region_name)
|
||||
|
||||
prof.set_api_version('clustering', '1.2')
|
||||
try:
|
||||
conn = connection.Connection(profile=prof, user_agent=user_agent,
|
||||
**kwargs)
|
||||
|
||||
@@ -80,6 +80,8 @@ def format_nested_dict(d, fields, column_names):
|
||||
value = d[field]
|
||||
if not isinstance(value, six.string_types):
|
||||
value = jsonutils.dumps(value, indent=2, ensure_ascii=False)
|
||||
if value is None:
|
||||
value = '-'
|
||||
pt.add_row([field, value.strip('"')])
|
||||
|
||||
return pt.get_string()
|
||||
@@ -129,14 +131,16 @@ def _print_list(objs, fields, formatters=None, sortby_index=0,
|
||||
row = []
|
||||
for field in fields:
|
||||
if field in formatters:
|
||||
row.append(formatters[field](o))
|
||||
data = formatters[field](o)
|
||||
else:
|
||||
if field in mixed_case_fields:
|
||||
field_name = field.replace(' ', '_')
|
||||
else:
|
||||
field_name = field.lower().replace(' ', '_')
|
||||
data = getattr(o, field_name, '')
|
||||
row.append(data)
|
||||
if data is None:
|
||||
data = '-'
|
||||
row.append(data)
|
||||
pt.add_row(row)
|
||||
|
||||
if six.PY3:
|
||||
@@ -169,9 +173,12 @@ def print_dict(d, formatters=None):
|
||||
|
||||
for field in d.keys():
|
||||
if field in formatters:
|
||||
pt.add_row([field, formatters[field](d[field])])
|
||||
data = formatters[field](d[field])
|
||||
else:
|
||||
pt.add_row([field, d[field]])
|
||||
data = d[field]
|
||||
if data is None:
|
||||
data = '-'
|
||||
pt.add_row([field, data])
|
||||
|
||||
content = pt.get_string(sortby='Property')
|
||||
if six.PY3:
|
||||
@@ -180,6 +187,14 @@ def print_dict(d, formatters=None):
|
||||
print(encodeutils.safe_encode(content))
|
||||
|
||||
|
||||
def print_action_result(rid, res):
|
||||
if res[0] == "OK":
|
||||
output = _("accepted by action %s") % res[1]
|
||||
else:
|
||||
output = _("failed due to '%s'") % res[1]
|
||||
print(_(" %(cid)s: %(output)s") % {"cid": rid, "output": output})
|
||||
|
||||
|
||||
def format_parameters(params, parse_semicolon=True):
|
||||
"""Reformat parameters into dict of format expected by the API."""
|
||||
if not params:
|
||||
@@ -240,7 +255,7 @@ def process_stack_spec(spec):
|
||||
new_spec = {
|
||||
# TODO(Qiming): add context support
|
||||
'disable_rollback': spec.get('disable_rollback', True),
|
||||
'context': spec.get('context', {}),
|
||||
'context': spec.get('context', {}),
|
||||
'parameters': spec.get('parameters', {}),
|
||||
'timeout': spec.get('timeout', 60),
|
||||
'template': template,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
# Zheng Xi Zhou <zzxwill@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: python-senlinclient 0.4.2.dev33\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-06-06 07:37+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-06-16 05:48+0000\n"
|
||||
"Last-Translator: Zheng Xi Zhou <zzxwill@gmail.com>\n"
|
||||
"Language-Team: Chinese (China)\n"
|
||||
"Language: zh-CN\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "Ctrl-c detected."
|
||||
msgstr "检测到Ctrl-c。"
|
||||
|
||||
msgid "Ctrl-d detected"
|
||||
msgstr "检测到Ctrl-d。"
|
||||
@@ -0,0 +1,19 @@
|
||||
# Zheng Xi Zhou <zzxwill@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: python-senlinclient 0.4.2.dev33\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-06-06 07:37+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-06-16 05:49+0000\n"
|
||||
"Last-Translator: Zheng Xi Zhou <zzxwill@gmail.com>\n"
|
||||
"Language-Team: Chinese (China)\n"
|
||||
"Language: zh-CN\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
#, python-format
|
||||
msgid "\"%(old)s\" is deprecated, please use \"%(new)s\" instead."
|
||||
msgstr "\"%(old)s\"已弃用,请使用\"%(new)s\"。"
|
||||
1256
senlinclient/locale/zh_CN/LC_MESSAGES/senlinclient.po
Normal file
1256
senlinclient/locale/zh_CN/LC_MESSAGES/senlinclient.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,19 +13,24 @@
|
||||
import logging
|
||||
|
||||
from openstack import connection
|
||||
from openstackclient.common import utils
|
||||
from openstack import profile
|
||||
from osc_lib import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_CLUSTERING_API_VERSION = '1'
|
||||
API_VERSION_OPTION = 'os_clustering_api_version'
|
||||
API_NAME = 'clustering'
|
||||
CURRENT_API_VERSION = '1.2'
|
||||
|
||||
|
||||
def make_client(instance):
|
||||
"""Returns a clustering proxy"""
|
||||
prof = profile.Profile()
|
||||
prof.set_api_version(API_NAME, CURRENT_API_VERSION)
|
||||
|
||||
conn = connection.Connection(authenticator=instance.session.auth)
|
||||
conn = connection.Connection(profile=prof,
|
||||
authenticator=instance.session.auth)
|
||||
LOG.debug('Connection: %s', conn)
|
||||
LOG.debug('Clustering client initialized using OpenStackSDK: %s',
|
||||
conn.cluster)
|
||||
|
||||
@@ -181,8 +181,8 @@ class SenlinShell(object):
|
||||
raise exc.CommandError(msg)
|
||||
|
||||
# project name or ID is needed, or else sdk may find the wrong project
|
||||
if (not (args.project_id or args.project_name or args.tenant_id
|
||||
or args.tenant_name)):
|
||||
if (not (args.project_id or args.project_name or args.tenant_id or
|
||||
args.tenant_name)):
|
||||
if not (args.user_id):
|
||||
msg = _('Either project/tenant ID or project/tenant name '
|
||||
'must be specified, or else Senlin cannot know '
|
||||
@@ -209,9 +209,9 @@ class SenlinShell(object):
|
||||
not (args.project_domain_id or args.project_domain_name)):
|
||||
msg = _('Either project domain ID (--project-domain-id / '
|
||||
'env[OS_PROJECT_DOMAIN_ID]) orr project domain name '
|
||||
'(--project-domain-name / env[OS_PROJECT_DOMAIN_NAME '
|
||||
'must be specified, because project/tenant name may '
|
||||
'not be unique.')
|
||||
'(--project-domain-name / '
|
||||
'env[OS_PROJECT_DOMAIN_NAME]) must be specified, '
|
||||
'because project/tenant name may not be unique.')
|
||||
raise exc.CommandError(msg)
|
||||
|
||||
def _setup_senlin_client(self, api_ver, args):
|
||||
@@ -235,8 +235,7 @@ class SenlinShell(object):
|
||||
'trust_id': args.trust_id,
|
||||
}
|
||||
|
||||
return senlin_client.Client('1', args.user_preferences, USER_AGENT,
|
||||
**kwargs)
|
||||
return senlin_client.Client('1', user_agent=USER_AGENT, **kwargs)
|
||||
|
||||
def main(self, argv):
|
||||
# Parse args once to find version
|
||||
|
||||
@@ -41,10 +41,6 @@ class TestCliArgs(testtools.TestCase):
|
||||
'--os-trust-id',
|
||||
'--os-token',
|
||||
'--os-access-info',
|
||||
'--os-api-name',
|
||||
'--os-api-region',
|
||||
'--os-api-version',
|
||||
'--os-api-interface'
|
||||
]
|
||||
|
||||
options = [arg[0][0] for arg in parser.add_argument.call_args_list]
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import json
|
||||
import yaml
|
||||
|
||||
from openstackclient.tests import utils
|
||||
from osc_lib.tests import utils
|
||||
from senlinclient.common import format_utils
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import os
|
||||
import testtools
|
||||
|
||||
from openstack import connection as sdk_connection
|
||||
@@ -22,42 +21,6 @@ from senlinclient.common import sdk
|
||||
|
||||
class TestSdk(testtools.TestCase):
|
||||
|
||||
@mock.patch('senlinclient.common.sdk.ProfileAction.set_option')
|
||||
def test_env(self, mock_set_option):
|
||||
os.environ['test_senlin_sdk_env'] = '1'
|
||||
sdk.ProfileAction.env('test_senlin_sdk_env')
|
||||
mock_set_option.assert_called_once_with('test_senlin_sdk_env', '1')
|
||||
|
||||
@mock.patch('senlinclient.common.sdk.ProfileAction.prof')
|
||||
def test_set_option_set_name(self, mock_prof):
|
||||
mock_prof.ALL = 'mock_prof.ALL'
|
||||
sdk.ProfileAction.set_option('name', 'test=val1')
|
||||
mock_prof.set_name.assert_called_once_with('test', 'val1')
|
||||
mock_prof.reset_mock()
|
||||
sdk.ProfileAction.set_option('name', 'val2')
|
||||
mock_prof.set_name.assert_called_once_with(mock_prof.ALL, 'val2')
|
||||
|
||||
@mock.patch('senlinclient.common.sdk.ProfileAction.prof')
|
||||
def test_set_option_set_region(self, mock_prof):
|
||||
mock_prof.ALL = 'mock_prof.ALL'
|
||||
sdk.ProfileAction.set_option('OS_REGION_NAME', 'test=val1')
|
||||
mock_prof.set_region.assert_called_once_with('test', 'val1')
|
||||
mock_prof.reset_mock()
|
||||
sdk.ProfileAction.set_option('OS_REGION_NAME', 'val2')
|
||||
mock_prof.set_region.assert_called_once_with(mock_prof.ALL, 'val2')
|
||||
|
||||
@mock.patch('senlinclient.common.sdk.ProfileAction.prof')
|
||||
def test_set_option_set_version(self, mock_prof):
|
||||
mock_prof.ALL = 'mock_prof.ALL'
|
||||
sdk.ProfileAction.set_option('version', 'test=val1')
|
||||
mock_prof.set_version.assert_called_once_with('test', 'val1')
|
||||
|
||||
@mock.patch('senlinclient.common.sdk.ProfileAction.prof')
|
||||
def test_set_option_set_interface(self, mock_prof):
|
||||
mock_prof.ALL = 'mock_prof.ALL'
|
||||
sdk.ProfileAction.set_option('interface', 'test=val1')
|
||||
mock_prof.set_interface.assert_called_once_with('test', 'val1')
|
||||
|
||||
@mock.patch.object(sdk_connection, 'Connection')
|
||||
def test_create_connection_with_profile(self, mock_connection):
|
||||
mock_prof = mock.Mock()
|
||||
|
||||
@@ -332,9 +332,9 @@ class ShellTest(testtools.TestCase):
|
||||
args.project_domain_name = None
|
||||
msg = _('Either project domain ID (--project-domain-id / '
|
||||
'env[OS_PROJECT_DOMAIN_ID]) orr project domain name '
|
||||
'(--project-domain-name / env[OS_PROJECT_DOMAIN_NAME '
|
||||
'must be specified, because project/tenant name may '
|
||||
'not be unique.')
|
||||
'(--project-domain-name / '
|
||||
'env[OS_PROJECT_DOMAIN_NAME]) must be specified, '
|
||||
'because project/tenant name may not be unique.')
|
||||
ex = self.assertRaises(exc.CommandError,
|
||||
sh._check_identity_arguments, args)
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
@@ -366,7 +366,7 @@ class ShellTest(testtools.TestCase):
|
||||
mock_conn.return_value = conn
|
||||
conn.session = mock.Mock()
|
||||
sh._setup_senlin_client('1', args)
|
||||
mock_conn.assert_called_once_with(args.user_preferences, USER_AGENT,
|
||||
mock_conn.assert_called_once_with(prof=None, user_agent=USER_AGENT,
|
||||
**kwargs)
|
||||
client = mock.Mock()
|
||||
senlin_client.Client = mock.MagicMock(return_value=client)
|
||||
|
||||
@@ -130,7 +130,7 @@ class PrintListTestCase(testtools.TestCase):
|
||||
+-----------+-----------+
|
||||
""", cso.read())
|
||||
|
||||
def test_print_list_with_None_data(self):
|
||||
def test_print_list_with_None_string(self):
|
||||
Row = collections.namedtuple('Row', ['foo', 'bar'])
|
||||
to_print = [Row(foo='fake_foo1', bar='None'),
|
||||
Row(foo='fake_foo2', bar='fake_bar1')]
|
||||
@@ -144,6 +144,22 @@ class PrintListTestCase(testtools.TestCase):
|
||||
| fake_foo1 | None |
|
||||
| fake_foo2 | fake_bar1 |
|
||||
+-----------+-----------+
|
||||
""", cso.read())
|
||||
|
||||
def test_print_list_with_None_data(self):
|
||||
Row = collections.namedtuple('Row', ['foo', 'bar'])
|
||||
to_print = [Row(foo='fake_foo1', bar=None),
|
||||
Row(foo='fake_foo2', bar='fake_bar1')]
|
||||
with CaptureStdout() as cso:
|
||||
utils.print_list(to_print, ['foo', 'bar'])
|
||||
# Output should be sorted by the first key (foo)
|
||||
self.assertEqual("""\
|
||||
+-----------+-----------+
|
||||
| foo | bar |
|
||||
+-----------+-----------+
|
||||
| fake_foo1 | - |
|
||||
| fake_foo2 | fake_bar1 |
|
||||
+-----------+-----------+
|
||||
""", cso.read())
|
||||
|
||||
def test_print_list_with_list_sortby(self):
|
||||
@@ -211,4 +227,20 @@ class PrintDictTestCase(testtools.TestCase):
|
||||
| bar | fake_bar |
|
||||
| foo | fake_foo |
|
||||
+----------+----------+
|
||||
""", cso.read())
|
||||
|
||||
def test_print_dict_with_None_data(self):
|
||||
Row = collections.namedtuple('Row', ['foo', 'bar'])
|
||||
to_print = [Row(foo='fake_foo1', bar=None),
|
||||
Row(foo='fake_foo2', bar='fake_bar1')]
|
||||
with CaptureStdout() as cso:
|
||||
utils.print_list(to_print, ['foo', 'bar'])
|
||||
# Output should be sorted by the first key (foo)
|
||||
self.assertEqual("""\
|
||||
+-----------+-----------+
|
||||
| foo | bar |
|
||||
+-----------+-----------+
|
||||
| fake_foo1 | - |
|
||||
| fake_foo2 | fake_bar1 |
|
||||
+-----------+-----------+
|
||||
""", cso.read())
|
||||
|
||||
@@ -16,7 +16,7 @@ import requests
|
||||
import six
|
||||
import sys
|
||||
|
||||
from openstackclient.tests import utils
|
||||
from osc_lib.tests import utils
|
||||
|
||||
|
||||
AUTH_TOKEN = "foobar"
|
||||
@@ -141,7 +141,7 @@ class FakeResource(object):
|
||||
self._loaded = loaded
|
||||
|
||||
def _add_details(self, info):
|
||||
for (k, v) in six.iteritems(info):
|
||||
for (k, v) in info.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
@@ -15,7 +15,7 @@ import mock
|
||||
|
||||
from openstack.cluster.v1 import action as sdk_action
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from osc_lib import exceptions as exc
|
||||
|
||||
from senlinclient.tests.unit.v1 import fakes
|
||||
from senlinclient.v1 import action as osc_action
|
||||
@@ -159,7 +159,7 @@ class TestActionList(TestAction):
|
||||
|
||||
|
||||
class TestActionShow(TestAction):
|
||||
get_response = {
|
||||
response = {
|
||||
"action": "CLUSTER_DELETE",
|
||||
"cause": "RPC Request",
|
||||
"context": {},
|
||||
@@ -184,7 +184,7 @@ class TestActionShow(TestAction):
|
||||
super(TestActionShow, self).setUp()
|
||||
self.cmd = osc_action.ShowAction(self.app, None)
|
||||
self.mock_client.get_action = mock.Mock(
|
||||
return_value=sdk_action.Action(attrs=self.get_response))
|
||||
return_value=sdk_action.Action(**self.response))
|
||||
|
||||
def test_action_show(self):
|
||||
arglist = ['my_action']
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
import mock
|
||||
|
||||
from openstack.cluster.v1 import build_info as sdk_build_info
|
||||
from openstack.cluster.v1 import build_info as sbi
|
||||
|
||||
from senlinclient.tests.unit.v1 import fakes
|
||||
from senlinclient.v1 import build_info as osc_build_info
|
||||
@@ -33,7 +33,7 @@ class TestBuildInfo(fakes.TestClusteringv1):
|
||||
self.cmd = osc_build_info.BuildInfo(self.app, None)
|
||||
self.mock_client = self.app.client_manager.clustering
|
||||
self.mock_client.get_build_info = mock.Mock(
|
||||
return_value=sdk_build_info.BuildInfo(None, self.response))
|
||||
return_value=sbi.BuildInfo(**self.response['build_info']))
|
||||
|
||||
def test_build_info(self):
|
||||
arglist = []
|
||||
|
||||
@@ -33,16 +33,17 @@ class ClientTest(testtools.TestCase):
|
||||
|
||||
self.assertEqual(self.conn, sc.conn)
|
||||
self.assertEqual(self.service, sc.service)
|
||||
mock_conn.assert_called_once_with(None, None)
|
||||
mock_conn.assert_called_once_with(prof=None, user_agent=None)
|
||||
|
||||
def test_init_with_params(self, mock_conn):
|
||||
mock_conn.return_value = self.conn
|
||||
|
||||
sc = client.Client(preferences='FOO', user_agent='BAR', zoo='LARR')
|
||||
sc = client.Client(prof='FOO', user_agent='BAR', zoo='LARR')
|
||||
|
||||
self.assertEqual(self.conn, sc.conn)
|
||||
self.assertEqual(self.service, sc.service)
|
||||
mock_conn.assert_called_once_with('FOO', 'BAR', zoo='LARR')
|
||||
mock_conn.assert_called_once_with(prof='FOO', user_agent='BAR',
|
||||
zoo='LARR')
|
||||
|
||||
def test_profile_types(self, mock_conn):
|
||||
mock_conn.return_value = self.conn
|
||||
@@ -335,16 +336,16 @@ class ClientTest(testtools.TestCase):
|
||||
|
||||
res = sc.get_node('FOOBAR')
|
||||
self.assertEqual(self.service.get_node.return_value, res)
|
||||
self.service.get_node.assert_called_once_with('FOOBAR', args=None)
|
||||
self.service.get_node.assert_called_once_with('FOOBAR', details=False)
|
||||
|
||||
def test_get_node_with_details(self, mock_conn):
|
||||
mock_conn.return_value = self.conn
|
||||
sc = client.Client()
|
||||
|
||||
res = sc.get_node('FOOBAR', args={'show_details': True})
|
||||
res = sc.get_node('FOOBAR', details=True)
|
||||
self.assertEqual(self.service.get_node.return_value, res)
|
||||
self.service.get_node.assert_called_once_with(
|
||||
'FOOBAR', args={'show_details': True})
|
||||
'FOOBAR', details=True)
|
||||
|
||||
def test_create_node(self, mock_conn):
|
||||
mock_conn.return_value = self.conn
|
||||
|
||||
@@ -11,12 +11,13 @@
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import mock
|
||||
import six
|
||||
import subprocess
|
||||
|
||||
import mock
|
||||
from openstack.cluster.v1 import cluster as sdk_cluster
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from osc_lib import exceptions as exc
|
||||
import six
|
||||
|
||||
from senlinclient.tests.unit.v1 import fakes
|
||||
from senlinclient.v1 import cluster as osc_cluster
|
||||
@@ -43,7 +44,7 @@ class TestClusterList(TestCluster):
|
||||
"metadata": {},
|
||||
"min_size": 0,
|
||||
"name": "cluster1",
|
||||
"nodes": [
|
||||
"node_ids": [
|
||||
"b07c57c8-7ab2-47bf-bdf8-e894c0c601b9",
|
||||
"ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61",
|
||||
"da1e9c87-e584-4626-a120-022da5062dac"
|
||||
@@ -147,7 +148,7 @@ class TestClusterList(TestCluster):
|
||||
|
||||
|
||||
class TestClusterShow(TestCluster):
|
||||
get_response = {"cluster": {
|
||||
response = {"cluster": {
|
||||
"created_at": "2015-02-11T15:13:20",
|
||||
"data": {},
|
||||
"desired_capacity": 0,
|
||||
@@ -158,7 +159,7 @@ class TestClusterShow(TestCluster):
|
||||
"metadata": {},
|
||||
"min_size": 0,
|
||||
"name": "my_cluster",
|
||||
"nodes": [],
|
||||
"node_ids": [],
|
||||
"policies": [],
|
||||
"profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a",
|
||||
"profile_name": "mystack",
|
||||
@@ -174,8 +175,7 @@ class TestClusterShow(TestCluster):
|
||||
super(TestClusterShow, self).setUp()
|
||||
self.cmd = osc_cluster.ShowCluster(self.app, None)
|
||||
self.mock_client.get_cluster = mock.Mock(
|
||||
return_value=sdk_cluster.Cluster(
|
||||
attrs=self.get_response['cluster']))
|
||||
return_value=sdk_cluster.Cluster(**self.response['cluster']))
|
||||
|
||||
def test_cluster_show(self):
|
||||
arglist = ['my_cluster']
|
||||
@@ -205,7 +205,7 @@ class TestClusterCreate(TestCluster):
|
||||
"metadata": {},
|
||||
"min_size": 0,
|
||||
"name": "test_cluster",
|
||||
"nodes": [],
|
||||
"node_ids": [],
|
||||
"policies": [],
|
||||
"profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a",
|
||||
"profile_name": "mystack",
|
||||
@@ -231,9 +231,9 @@ class TestClusterCreate(TestCluster):
|
||||
super(TestClusterCreate, self).setUp()
|
||||
self.cmd = osc_cluster.CreateCluster(self.app, None)
|
||||
self.mock_client.create_cluster = mock.Mock(
|
||||
return_value=sdk_cluster.Cluster(attrs=self.response['cluster']))
|
||||
return_value=sdk_cluster.Cluster(**self.response['cluster']))
|
||||
self.mock_client.get_cluster = mock.Mock(
|
||||
return_value=sdk_cluster.Cluster(attrs=self.response['cluster']))
|
||||
return_value=sdk_cluster.Cluster(**self.response['cluster']))
|
||||
|
||||
def test_cluster_create_defaults(self):
|
||||
arglist = ['test_cluster', '--profile', 'mystack']
|
||||
@@ -275,7 +275,7 @@ class TestClusterUpdate(TestCluster):
|
||||
"metadata": {},
|
||||
"min_size": 0,
|
||||
"name": "test_cluster",
|
||||
"nodes": [],
|
||||
"node_ids": [],
|
||||
"policies": [],
|
||||
"profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a",
|
||||
"profile_name": "mystack",
|
||||
@@ -301,11 +301,11 @@ class TestClusterUpdate(TestCluster):
|
||||
super(TestClusterUpdate, self).setUp()
|
||||
self.cmd = osc_cluster.UpdateCluster(self.app, None)
|
||||
self.mock_client.update_cluster = mock.Mock(
|
||||
return_value=sdk_cluster.Cluster(attrs=self.response['cluster']))
|
||||
return_value=sdk_cluster.Cluster(**self.response['cluster']))
|
||||
self.mock_client.get_cluster = mock.Mock(
|
||||
return_value=sdk_cluster.Cluster(attrs=self.response['cluster']))
|
||||
return_value=sdk_cluster.Cluster(**self.response['cluster']))
|
||||
self.mock_client.find_cluster = mock.Mock(
|
||||
return_value=sdk_cluster.Cluster(attrs=self.response['cluster']))
|
||||
return_value=sdk_cluster.Cluster(**self.response['cluster']))
|
||||
|
||||
def test_cluster_update_defaults(self):
|
||||
arglist = ['--name', 'new_cluster', '--metadata', 'nk1=nv1;nk2=nv2',
|
||||
@@ -330,7 +330,8 @@ class TestClusterDelete(TestCluster):
|
||||
def setUp(self):
|
||||
super(TestClusterDelete, self).setUp()
|
||||
self.cmd = osc_cluster.DeleteCluster(self.app, None)
|
||||
self.mock_client.delete_cluster = mock.Mock()
|
||||
mock_cluster = mock.Mock(location='abc/fake_action_id')
|
||||
self.mock_client.delete_cluster = mock.Mock(return_value=mock_cluster)
|
||||
|
||||
def test_cluster_delete(self):
|
||||
arglist = ['cluster1', 'cluster2', 'cluster3']
|
||||
@@ -354,10 +355,12 @@ class TestClusterDelete(TestCluster):
|
||||
arglist = ['my_cluster']
|
||||
self.mock_client.delete_cluster.side_effect = sdk_exc.ResourceNotFound
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = self.assertRaises(exc.CommandError, self.cmd.take_action,
|
||||
parsed_args)
|
||||
self.assertIn('Failed to delete 1 of the 1 specified cluster(s).',
|
||||
str(error))
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.mock_client.delete_cluster.assert_has_calls(
|
||||
[mock.call('my_cluster', False)]
|
||||
)
|
||||
|
||||
def test_cluster_delete_one_found_one_not_found(self):
|
||||
arglist = ['cluster1', 'cluster2']
|
||||
@@ -365,13 +368,12 @@ class TestClusterDelete(TestCluster):
|
||||
[None, sdk_exc.ResourceNotFound]
|
||||
)
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = self.assertRaises(exc.CommandError,
|
||||
self.cmd.take_action, parsed_args)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.mock_client.delete_cluster.assert_has_calls(
|
||||
[mock.call('cluster1', False), mock.call('cluster2', False)]
|
||||
)
|
||||
self.assertEqual('Failed to delete 1 of the 2 specified cluster(s).',
|
||||
str(error))
|
||||
|
||||
@mock.patch('sys.stdin', spec=six.StringIO)
|
||||
def test_cluster_delete_prompt_yes(self, mock_stdin):
|
||||
@@ -425,14 +427,20 @@ class TestClusterResize(TestCluster):
|
||||
self.assertEqual("Only one of 'capacity', 'adjustment' "
|
||||
"and 'percentage' can be specified.", str(error))
|
||||
|
||||
def test_cluster_resize_none_params(self):
|
||||
def test_cluster_resize_only_constraints(self):
|
||||
arglist = ['--min-size', '1', '--max-size', '20', 'my_cluster']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = self.assertRaises(exc.CommandError,
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
self.assertEqual("At least one of 'capacity', 'adjustment' and "
|
||||
"'percentage' should be specified.", str(error))
|
||||
self.cmd.take_action(parsed_args)
|
||||
kwargs = {
|
||||
'min_size': 1,
|
||||
'max_size': 20,
|
||||
'adjustment_type': None,
|
||||
'min_step': None,
|
||||
'number': None,
|
||||
'strict': False
|
||||
}
|
||||
self.mock_client.cluster_resize.assert_called_with('my_cluster',
|
||||
**kwargs)
|
||||
|
||||
def test_cluster_resize_capacity(self):
|
||||
arglist = ['--capacity', '2', '--min-size', '1', '--max-size', '20',
|
||||
@@ -777,7 +785,7 @@ class TestClusterRecover(TestCluster):
|
||||
self.mock_client.recover_cluster = mock.Mock(
|
||||
return_value=self.response)
|
||||
|
||||
def test_cluster_recoverk(self):
|
||||
def test_cluster_recover(self):
|
||||
arglist = ['cluster1', 'cluster2', 'cluster3']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
@@ -793,3 +801,216 @@ class TestClusterRecover(TestCluster):
|
||||
error = self.assertRaises(exc.CommandError, self.cmd.take_action,
|
||||
parsed_args)
|
||||
self.assertIn('Cluster not found: cluster1', str(error))
|
||||
|
||||
|
||||
class TestClusterCollect(TestCluster):
|
||||
response = [
|
||||
{
|
||||
"node_id": "8bb476c3-0f4c-44ee-9f64-c7b0260814de",
|
||||
"attr_value": "value 1",
|
||||
},
|
||||
{
|
||||
"node_id": "7d85f602-a948-4a30-afd4-e84f47471c15",
|
||||
"attr_value": "value 2",
|
||||
}
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(TestClusterCollect, self).setUp()
|
||||
self.cmd = osc_cluster.ClusterCollect(self.app, None)
|
||||
self.mock_client.collect_cluster_attrs = mock.Mock(
|
||||
return_value=self.response)
|
||||
|
||||
def test_cluster_collect(self):
|
||||
arglist = ['--path', 'path.to.attr', 'cluster1']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.mock_client.collect_cluster_attrs.assert_called_once_with(
|
||||
'cluster1', 'path.to.attr')
|
||||
self.assertEqual(['node_id', 'attr_value'], columns)
|
||||
|
||||
def test_cluster_collect_with_full_id(self):
|
||||
arglist = ['--path', 'path.to.attr', '--full-id', 'cluster1']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.mock_client.collect_cluster_attrs.assert_called_once_with(
|
||||
'cluster1', 'path.to.attr')
|
||||
self.assertEqual(['node_id', 'attr_value'], columns)
|
||||
|
||||
|
||||
class TestClusterRun(TestCluster):
|
||||
attrs = [
|
||||
mock.Mock(node_id="NODE_ID1",
|
||||
attr_value={"addresses": 'ADDRESS CONTENT 1'}),
|
||||
mock.Mock(node_id="NODE_ID2",
|
||||
attr_value={"addresses": 'ADDRESS CONTENT 2'})
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(TestClusterRun, self).setUp()
|
||||
self.cmd = osc_cluster.ClusterRun(self.app, None)
|
||||
self.mock_client.collect_cluster_attrs = mock.Mock(
|
||||
return_value=self.attrs)
|
||||
|
||||
@mock.patch('subprocess.Popen')
|
||||
def test__run_script(self, mock_proc):
|
||||
x_proc = mock.Mock(returncode=0)
|
||||
x_stdout = 'OUTPUT'
|
||||
x_stderr = 'ERROR'
|
||||
x_proc.communicate.return_value = (x_stdout, x_stderr)
|
||||
mock_proc.return_value = x_proc
|
||||
|
||||
addr = {
|
||||
'private': [
|
||||
{
|
||||
'OS-EXT-IPS:type': 'floating',
|
||||
'version': 4,
|
||||
'addr': '1.2.3.4',
|
||||
}
|
||||
]
|
||||
}
|
||||
output = {}
|
||||
|
||||
self.cmd._run_script('NODE_ID', addr, 'private', 'floating', 22,
|
||||
'john', False, 'identity_path', 'echo foo',
|
||||
'-f bar',
|
||||
output=output)
|
||||
mock_proc.assert_called_once_with(
|
||||
['ssh', '-4', '-p22', '-i identity_path', '-f bar', 'john@1.2.3.4',
|
||||
'echo foo'],
|
||||
stdout=subprocess.PIPE)
|
||||
self.assertEqual(
|
||||
{'status': 'SUCCEEDED (0)', 'output': 'OUTPUT', 'error': 'ERROR'},
|
||||
output)
|
||||
|
||||
def test__run_script_network_not_found(self):
|
||||
addr = {'foo': 'bar'}
|
||||
output = {}
|
||||
|
||||
self.cmd._run_script('NODE_ID', addr, 'private', 'floating', 22,
|
||||
'john', False, 'identity_path', 'echo foo',
|
||||
'-f bar',
|
||||
output=output)
|
||||
self.assertEqual(
|
||||
{'status': 'FAILED',
|
||||
'error': "Node 'NODE_ID' is not attached to network 'private'."
|
||||
},
|
||||
output)
|
||||
|
||||
def test__run_script_more_than_one_network(self):
|
||||
addr = {'foo': 'bar', 'koo': 'tar'}
|
||||
output = {}
|
||||
|
||||
self.cmd._run_script('NODE_ID', addr, '', 'floating', 22, 'john',
|
||||
False, 'identity_path', 'echo foo', '-f bar',
|
||||
output=output)
|
||||
self.assertEqual(
|
||||
{'status': 'FAILED',
|
||||
'error': "Node 'NODE_ID' is attached to more than one "
|
||||
"network. Please pick the network to use."},
|
||||
output)
|
||||
|
||||
def test__run_script_no_network(self):
|
||||
addr = {}
|
||||
output = {}
|
||||
|
||||
self.cmd._run_script('NODE_ID', addr, '', 'floating', 22, 'john',
|
||||
False, 'identity_path', 'echo foo', '-f bar',
|
||||
output=output)
|
||||
|
||||
self.assertEqual(
|
||||
{'status': 'FAILED',
|
||||
'error': "Node 'NODE_ID' is not attached to any network."},
|
||||
output)
|
||||
|
||||
def test__run_script_no_matching_address(self):
|
||||
addr = {
|
||||
'private': [
|
||||
{
|
||||
'OS-EXT-IPS:type': 'fixed',
|
||||
'version': 4,
|
||||
'addr': '1.2.3.4',
|
||||
}
|
||||
]
|
||||
}
|
||||
output = {}
|
||||
|
||||
self.cmd._run_script('NODE_ID', addr, 'private', 'floating', 22,
|
||||
'john', False, 'identity_path', 'echo foo',
|
||||
'-f bar',
|
||||
output=output)
|
||||
self.assertEqual(
|
||||
{'status': 'FAILED',
|
||||
'error': "No address that matches network 'private' and "
|
||||
"type 'floating' of IPv4 has been found for node "
|
||||
"'NODE_ID'."},
|
||||
output)
|
||||
|
||||
def test__run_script_more_than_one_address(self):
|
||||
addr = {
|
||||
'private': [
|
||||
{
|
||||
'OS-EXT-IPS:type': 'fixed',
|
||||
'version': 4,
|
||||
'addr': '1.2.3.4',
|
||||
},
|
||||
{
|
||||
'OS-EXT-IPS:type': 'fixed',
|
||||
'version': 4,
|
||||
'addr': '5.6.7.8',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
output = {}
|
||||
|
||||
self.cmd._run_script('NODE_ID', addr, 'private', 'fixed', 22, 'john',
|
||||
False, 'identity_path', 'echo foo', '-f bar',
|
||||
output=output)
|
||||
self.assertEqual(
|
||||
{'status': 'FAILED',
|
||||
'error': "More than one IPv4 fixed address found."},
|
||||
output)
|
||||
|
||||
@mock.patch('threading.Thread')
|
||||
@mock.patch.object(osc_cluster.ClusterRun, '_run_script')
|
||||
def test_cluster_run(self, mock_script, mock_thread):
|
||||
arglist = [
|
||||
'--port', '22',
|
||||
'--address-type', 'fixed',
|
||||
'--network', 'private',
|
||||
'--user', 'root',
|
||||
'--identity-file', 'path-to-identity',
|
||||
'--ssh-options', '-f boo',
|
||||
'--script', 'script-file',
|
||||
'cluster1'
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
|
||||
th1 = mock.Mock()
|
||||
th2 = mock.Mock()
|
||||
mock_thread.side_effect = [th1, th2]
|
||||
fake_script = 'blah blah'
|
||||
with mock.patch('senlinclient.v1.cluster.open',
|
||||
mock.mock_open(read_data=fake_script)) as mock_open:
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.mock_client.collect_cluster_attrs.assert_called_once_with(
|
||||
'cluster1', 'details')
|
||||
mock_open.assert_called_once_with('script-file', 'r')
|
||||
mock_thread.assert_has_calls([
|
||||
mock.call(target=mock_script,
|
||||
args=('NODE_ID1', 'ADDRESS CONTENT 1', 'private',
|
||||
'fixed', 22, 'root', False, 'path-to-identity',
|
||||
'blah blah', '-f boo'),
|
||||
kwargs={'output': {}}),
|
||||
mock.call(target=mock_script,
|
||||
args=('NODE_ID2', 'ADDRESS CONTENT 2', 'private',
|
||||
'fixed', 22, 'root', False, 'path-to-identity',
|
||||
'blah blah', '-f boo'),
|
||||
kwargs={'output': {}})
|
||||
])
|
||||
th1.start.assert_called_once_with()
|
||||
th2.start.assert_called_once_with()
|
||||
th1.join.assert_called_once_with()
|
||||
th2.join.assert_called_once_with()
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
import mock
|
||||
|
||||
from openstack.cluster.v1 import cluster_policy as sdk_cluster_policy
|
||||
from openstack.cluster.v1 import cluster_policy as scp
|
||||
|
||||
from senlinclient.tests.unit.v1 import fakes
|
||||
from senlinclient.v1 import cluster_policy as osc_cluster_policy
|
||||
@@ -25,7 +25,7 @@ class TestClusterPolicy(fakes.TestClusteringv1):
|
||||
|
||||
|
||||
class TestClusterPolicyList(TestClusterPolicy):
|
||||
columns = ['policy_id', 'policy_name', 'policy_type', 'enabled']
|
||||
columns = ['policy_id', 'policy_name', 'policy_type', 'is_enabled']
|
||||
response = {"cluster_policies": [
|
||||
{
|
||||
"cluster_id": "7d85f602-a948-4a30-afd4-e84f47471c15",
|
||||
@@ -76,7 +76,7 @@ class TestClusterPolicyList(TestClusterPolicy):
|
||||
|
||||
|
||||
class TestClusterPolicyShow(TestClusterPolicy):
|
||||
get_response = {"cluster_policy": {
|
||||
response = {"cluster_policy": {
|
||||
"cluster_id": "7d85f602-a948-4a30-afd4-e84f47471c15",
|
||||
"cluster_name": "my_cluster",
|
||||
"enabled": True,
|
||||
@@ -90,8 +90,7 @@ class TestClusterPolicyShow(TestClusterPolicy):
|
||||
super(TestClusterPolicyShow, self).setUp()
|
||||
self.cmd = osc_cluster_policy.ClusterPolicyShow(self.app, None)
|
||||
self.mock_client.get_cluster_policy = mock.Mock(
|
||||
return_value=sdk_cluster_policy.ClusterPolicy(None,
|
||||
self.get_response))
|
||||
return_value=scp.ClusterPolicy(**self.response['cluster_policy']))
|
||||
|
||||
def test_cluster_policy_show(self):
|
||||
arglist = ['--policy', 'my_policy', 'my_cluster']
|
||||
|
||||
@@ -15,7 +15,7 @@ import mock
|
||||
|
||||
from openstack.cluster.v1 import event as sdk_event
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from osc_lib import exceptions as exc
|
||||
|
||||
from senlinclient.tests.unit.v1 import fakes
|
||||
from senlinclient.v1 import event as osc_event
|
||||
@@ -60,7 +60,7 @@ class TestEventList(TestEvent):
|
||||
super(TestEventList, self).setUp()
|
||||
self.cmd = osc_event.ListEvent(self.app, None)
|
||||
self.mock_client.events = mock.Mock(
|
||||
return_value=sdk_event.Event(None, {}))
|
||||
return_value=[sdk_event.Event(**self.response['events'][0])])
|
||||
|
||||
def test_event_list_defaults(self):
|
||||
arglist = []
|
||||
@@ -95,8 +95,7 @@ class TestEventList(TestEvent):
|
||||
self.assertEqual(self.columns, columns)
|
||||
|
||||
def test_event_list_sort_invalid_key(self):
|
||||
self.mock_client.events = mock.Mock(
|
||||
return_value=self.response)
|
||||
self.mock_client.events = mock.Mock(return_value=self.response)
|
||||
kwargs = copy.deepcopy(self.defaults)
|
||||
kwargs['sort'] = 'bad_key'
|
||||
arglist = ['--sort', 'bad_key']
|
||||
@@ -106,8 +105,7 @@ class TestEventList(TestEvent):
|
||||
self.cmd.take_action, parsed_args)
|
||||
|
||||
def test_event_list_sort_invalid_direction(self):
|
||||
self.mock_client.events = mock.Mock(
|
||||
return_value=self.response)
|
||||
self.mock_client.events = mock.Mock(return_value=self.response)
|
||||
kwargs = copy.deepcopy(self.defaults)
|
||||
kwargs['sort'] = 'name:bad_direction'
|
||||
arglist = ['--sort', 'name:bad_direction']
|
||||
@@ -136,7 +134,7 @@ class TestEventList(TestEvent):
|
||||
|
||||
|
||||
class TestEventShow(TestEvent):
|
||||
get_response = {"event": {
|
||||
response = {"event": {
|
||||
"action": "create",
|
||||
"cluster_id": 'null',
|
||||
"id": "2d255b9c-8f36-41a2-a137-c0175ccc29c3",
|
||||
@@ -155,7 +153,7 @@ class TestEventShow(TestEvent):
|
||||
super(TestEventShow, self).setUp()
|
||||
self.cmd = osc_event.ShowEvent(self.app, None)
|
||||
self.mock_client.get_event = mock.Mock(
|
||||
return_value=sdk_event.Event(None, self.get_response))
|
||||
return_value=sdk_event.Event(**self.response['event']))
|
||||
|
||||
def test_event_show(self):
|
||||
arglist = ['my_event']
|
||||
|
||||
@@ -16,7 +16,7 @@ import six
|
||||
|
||||
from openstack.cluster.v1 import node as sdk_node
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from osc_lib import exceptions as exc
|
||||
|
||||
from senlinclient.tests.unit.v1 import fakes
|
||||
from senlinclient.v1 import node as osc_node
|
||||
@@ -144,7 +144,7 @@ class TestNodeList(TestNode):
|
||||
|
||||
|
||||
class TestNodeShow(TestNode):
|
||||
get_response = {"node": {
|
||||
response = {"node": {
|
||||
"cluster_id": None,
|
||||
"created_at": "2015-02-10T12:03:16",
|
||||
"data": {},
|
||||
@@ -171,20 +171,20 @@ class TestNodeShow(TestNode):
|
||||
super(TestNodeShow, self).setUp()
|
||||
self.cmd = osc_node.ShowNode(self.app, None)
|
||||
self.mock_client.get_node = mock.Mock(
|
||||
return_value=sdk_node.Node(attrs=self.get_response['node']))
|
||||
return_value=sdk_node.Node(**self.response['node']))
|
||||
|
||||
def test_node_show(self):
|
||||
arglist = ['my_node']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.mock_client.get_node.assert_called_with('my_node', args=None)
|
||||
self.mock_client.get_node.assert_called_with('my_node', details=False)
|
||||
|
||||
def test_node_show_with_details(self):
|
||||
arglist = ['my_node', '--details']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.mock_client.get_node.assert_called_with(
|
||||
'my_node', args={'show_details': True})
|
||||
'my_node', details=True)
|
||||
|
||||
def test_node_show_not_found(self):
|
||||
arglist = ['my_node']
|
||||
@@ -230,9 +230,9 @@ class TestNodeCreate(TestNode):
|
||||
super(TestNodeCreate, self).setUp()
|
||||
self.cmd = osc_node.CreateNode(self.app, None)
|
||||
self.mock_client.create_node = mock.Mock(
|
||||
return_value=sdk_node.Node(attrs=self.response['node']))
|
||||
return_value=sdk_node.Node(**self.response['node']))
|
||||
self.mock_client.get_node = mock.Mock(
|
||||
return_value=sdk_node.Node(attrs=self.response['node']))
|
||||
return_value=sdk_node.Node(**self.response['node']))
|
||||
|
||||
def test_node_create_defaults(self):
|
||||
arglist = ['my_node', '--profile', 'mystack']
|
||||
@@ -305,11 +305,11 @@ class TestNodeUpdate(TestNode):
|
||||
super(TestNodeUpdate, self).setUp()
|
||||
self.cmd = osc_node.UpdateNode(self.app, None)
|
||||
self.mock_client.update_node = mock.Mock(
|
||||
return_value=sdk_node.Node(attrs=self.response['node']))
|
||||
return_value=sdk_node.Node(**self.response['node']))
|
||||
self.mock_client.get_node = mock.Mock(
|
||||
return_value=sdk_node.Node(attrs=self.response['node']))
|
||||
return_value=sdk_node.Node(**self.response['node']))
|
||||
self.mock_client.find_node = mock.Mock(
|
||||
return_value=sdk_node.Node(attrs=self.response['node']))
|
||||
return_value=sdk_node.Node(**self.response['node']))
|
||||
|
||||
def test_node_update_defaults(self):
|
||||
arglist = ['--name', 'new_node', '--metadata', 'nk1=nv1;nk2=nv2',
|
||||
@@ -335,7 +335,8 @@ class TestNodeDelete(TestNode):
|
||||
def setUp(self):
|
||||
super(TestNodeDelete, self).setUp()
|
||||
self.cmd = osc_node.DeleteNode(self.app, None)
|
||||
self.mock_client.delete_node = mock.Mock()
|
||||
mock_node = mock.Mock(location='loc/fake_action_id')
|
||||
self.mock_client.delete_node = mock.Mock(return_value=mock_node)
|
||||
|
||||
def test_node_delete(self):
|
||||
arglist = ['node1', 'node2', 'node3']
|
||||
@@ -359,10 +360,12 @@ class TestNodeDelete(TestNode):
|
||||
arglist = ['my_node']
|
||||
self.mock_client.delete_node.side_effect = sdk_exc.ResourceNotFound
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = self.assertRaises(exc.CommandError, self.cmd.take_action,
|
||||
parsed_args)
|
||||
self.assertIn('Failed to delete 1 of the 1 specified node(s).',
|
||||
str(error))
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.mock_client.delete_node.assert_has_calls(
|
||||
[mock.call('my_node', False)]
|
||||
)
|
||||
|
||||
def test_node_delete_one_found_one_not_found(self):
|
||||
arglist = ['node1', 'node2']
|
||||
@@ -370,13 +373,12 @@ class TestNodeDelete(TestNode):
|
||||
[None, sdk_exc.ResourceNotFound]
|
||||
)
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = self.assertRaises(exc.CommandError,
|
||||
self.cmd.take_action, parsed_args)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.mock_client.delete_node.assert_has_calls(
|
||||
[mock.call('node1', False), mock.call('node2', False)]
|
||||
)
|
||||
self.assertEqual('Failed to delete 1 of the 2 specified node(s).',
|
||||
str(error))
|
||||
|
||||
@mock.patch('sys.stdin', spec=six.StringIO)
|
||||
def test_node_delete_prompt_yes(self, mock_stdin):
|
||||
|
||||
@@ -16,7 +16,7 @@ import six
|
||||
|
||||
from openstack.cluster.v1 import policy as sdk_policy
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from osc_lib import exceptions as exc
|
||||
|
||||
from senlinclient.tests.unit.v1 import fakes
|
||||
from senlinclient.v1 import policy as osc_policy
|
||||
@@ -141,7 +141,7 @@ class TestPolicyList(TestPolicy):
|
||||
|
||||
|
||||
class TestPolicyShow(TestPolicy):
|
||||
get_response = {"policy": {
|
||||
response = {"policy": {
|
||||
"created_at": "2015-03-02T07:40:31",
|
||||
"data": {},
|
||||
"domain": 'null',
|
||||
@@ -170,7 +170,7 @@ class TestPolicyShow(TestPolicy):
|
||||
super(TestPolicyShow, self).setUp()
|
||||
self.cmd = osc_policy.ShowPolicy(self.app, None)
|
||||
self.mock_client.get_policy = mock.Mock(
|
||||
return_value=sdk_policy.Policy(attrs=self.get_response['policy']))
|
||||
return_value=sdk_policy.Policy(**self.response['policy']))
|
||||
|
||||
def test_policy_show(self):
|
||||
arglist = ['sp001']
|
||||
@@ -230,9 +230,9 @@ class TestPolicyCreate(TestPolicy):
|
||||
super(TestPolicyCreate, self).setUp()
|
||||
self.cmd = osc_policy.CreatePolicy(self.app, None)
|
||||
self.mock_client.create_policy = mock.Mock(
|
||||
return_value=sdk_policy.Policy(attrs=self.response['policy']))
|
||||
return_value=sdk_policy.Policy(**self.response['policy']))
|
||||
self.mock_client.get_policy = mock.Mock(
|
||||
return_value=sdk_policy.Policy(attrs=self.response['policy']))
|
||||
return_value=sdk_policy.Policy(**self.response['policy']))
|
||||
|
||||
def test_policy_create_defaults(self):
|
||||
arglist = ['my_policy', '--spec-file', self.spec_path]
|
||||
@@ -274,11 +274,11 @@ class TestPolicyUpdate(TestPolicy):
|
||||
super(TestPolicyUpdate, self).setUp()
|
||||
self.cmd = osc_policy.UpdatePolicy(self.app, None)
|
||||
self.mock_client.update_policy = mock.Mock(
|
||||
return_value=sdk_policy.Policy(attrs=self.response['policy']))
|
||||
return_value=sdk_policy.Policy(**self.response['policy']))
|
||||
self.mock_client.get_policy = mock.Mock(
|
||||
return_value=sdk_policy.Policy(attrs=self.response['policy']))
|
||||
return_value=sdk_policy.Policy(**self.response['policy']))
|
||||
self.mock_client.find_policy = mock.Mock(
|
||||
return_value=sdk_policy.Policy(attrs=self.response['policy']))
|
||||
return_value=sdk_policy.Policy(**self.response['policy']))
|
||||
|
||||
def test_policy_update_defaults(self):
|
||||
arglist = ['--name', 'new_policy', '9f779ddf']
|
||||
@@ -368,3 +368,56 @@ class TestPolicyDelete(TestPolicy):
|
||||
|
||||
mock_stdin.readline.assert_called_with()
|
||||
self.mock_client.delete_policy.assert_not_called()
|
||||
|
||||
|
||||
class TestPolicyValidate(TestPolicy):
|
||||
spec_path = 'senlinclient/tests/test_specs/deletion_policy.yaml'
|
||||
response = {"policy": {
|
||||
"created_at": None,
|
||||
"data": {},
|
||||
"domain": 'null',
|
||||
"id": None,
|
||||
"name": "validated_policy",
|
||||
"project": "5f1cc92b578e4e25a3b284179cf20a9b",
|
||||
"spec": {
|
||||
"description": "A policy for choosing victim node(s) from a "
|
||||
"cluster for deletion.",
|
||||
"properties": {
|
||||
"criteria": "OLDEST_FIRST",
|
||||
"destroy_after_deletion": True,
|
||||
"grace_period": 60,
|
||||
"reduce_desired_capacity": False
|
||||
},
|
||||
"type": "senlin.policy.deletion",
|
||||
"version": 1.0
|
||||
},
|
||||
"type": "senlin.policy.deletion-1.0",
|
||||
"updated_at": 'null',
|
||||
"user": "2d7aca950f3e465d8ef0c81720faf6ff"
|
||||
}}
|
||||
defaults = {
|
||||
"spec": {
|
||||
"version": 1,
|
||||
"type": "senlin.policy.deletion",
|
||||
"description": "A policy for choosing victim node(s) from a "
|
||||
"cluster for deletion.",
|
||||
"properties": {
|
||||
"destroy_after_deletion": True,
|
||||
"grace_period": 60,
|
||||
"reduce_desired_capacity": False,
|
||||
"criteria": "OLDEST_FIRST"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestPolicyValidate, self).setUp()
|
||||
self.cmd = osc_policy.ValidatePolicy(self.app, None)
|
||||
self.mock_client.validate_policy = mock.Mock(
|
||||
return_value=sdk_policy.Policy(**self.response['policy']))
|
||||
|
||||
def test_policy_validate(self):
|
||||
arglist = ['--spec-file', self.spec_path]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.mock_client.validate_policy.assert_called_with(**self.defaults)
|
||||
|
||||
@@ -14,7 +14,7 @@ import mock
|
||||
|
||||
from openstack.cluster.v1 import policy_type as sdk_policy_type
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from osc_lib import exceptions as exc
|
||||
|
||||
from senlinclient.tests.unit.v1 import fakes
|
||||
from senlinclient.v1 import policy_type as osc_policy_type
|
||||
@@ -29,15 +29,9 @@ class TestPolicyType(fakes.TestClusteringv1):
|
||||
class TestPolicyTypeList(TestPolicyType):
|
||||
expected_columns = ['name']
|
||||
list_response = [
|
||||
sdk_policy_type.PolicyType({'name': 'BBB',
|
||||
'schema': {
|
||||
'foo': 'bar'}}),
|
||||
sdk_policy_type.PolicyType({'name': 'AAA',
|
||||
'schema': {
|
||||
'foo': 'bar'}}),
|
||||
sdk_policy_type.PolicyType({'name': 'CCC',
|
||||
'schema': {
|
||||
'foo': 'bar'}}),
|
||||
sdk_policy_type.PolicyType(name='BBB', schema={'foo': 'bar'}),
|
||||
sdk_policy_type.PolicyType(name='AAA', schema={'foo': 'bar'}),
|
||||
sdk_policy_type.PolicyType(name='CCC', schema={'foo': 'bar'}),
|
||||
]
|
||||
expected_rows = [
|
||||
['AAA'],
|
||||
@@ -71,7 +65,7 @@ class TestPolicyTypeShow(TestPolicyType):
|
||||
super(TestPolicyTypeShow, self).setUp()
|
||||
self.cmd = osc_policy_type.PolicyTypeShow(self.app, None)
|
||||
self.mock_client.get_policy_type = mock.Mock(
|
||||
return_value=sdk_policy_type.PolicyType(self.response)
|
||||
return_value=sdk_policy_type.PolicyType(**self.response)
|
||||
)
|
||||
|
||||
def test_policy_type_show(self):
|
||||
|
||||
@@ -16,8 +16,8 @@ import six
|
||||
|
||||
from openstack.cluster.v1 import profile as sdk_profile
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from openstackclient.common import utils
|
||||
from osc_lib import exceptions as exc
|
||||
from osc_lib import utils
|
||||
|
||||
from senlinclient.tests.unit.v1 import fakes
|
||||
from senlinclient.v1 import profile as osc_profile
|
||||
@@ -30,7 +30,7 @@ class TestProfile(fakes.TestClusteringv1):
|
||||
|
||||
|
||||
class TestProfileShow(TestProfile):
|
||||
get_response = {"profile": {
|
||||
response = {"profile": {
|
||||
"created_at": "2015-03-01T14:28:25",
|
||||
"domain": 'false',
|
||||
"id": "7fa885cd-fa39-4531-a42d-780af95c84a4",
|
||||
@@ -92,8 +92,7 @@ class TestProfileShow(TestProfile):
|
||||
super(TestProfileShow, self).setUp()
|
||||
self.cmd = osc_profile.ShowProfile(self.app, None)
|
||||
self.mock_client.get_profile = mock.Mock(
|
||||
return_value=sdk_profile.Profile(
|
||||
attrs=self.get_response['profile']))
|
||||
return_value=sdk_profile.Profile(**self.response['profile']))
|
||||
utils.get_dict_properties = mock.Mock(return_value='')
|
||||
|
||||
def test_profile_show(self):
|
||||
@@ -305,13 +304,15 @@ class TestProfileCreate(TestProfile):
|
||||
"updated_at": None,
|
||||
"user": "2d7aca950f3e465d8ef0c81720faf6ff"}}
|
||||
|
||||
defaults = {"spec": {
|
||||
"version": 1.0,
|
||||
"type": "os.nova.server",
|
||||
"properties": {
|
||||
"flavor": 1,
|
||||
"name": "cirros_server",
|
||||
"image": "cirros-0.3.4-x86_64-uec"}
|
||||
defaults = {
|
||||
"spec": {
|
||||
"version": 1.0,
|
||||
"type": "os.nova.server",
|
||||
"properties": {
|
||||
"flavor": 1,
|
||||
"name": "cirros_server",
|
||||
"image": "cirros-0.3.4-x86_64-uec"
|
||||
},
|
||||
},
|
||||
"name": "my_profile",
|
||||
"metadata": {}
|
||||
@@ -321,9 +322,9 @@ class TestProfileCreate(TestProfile):
|
||||
super(TestProfileCreate, self).setUp()
|
||||
self.cmd = osc_profile.CreateProfile(self.app, None)
|
||||
self.mock_client.create_profile = mock.Mock(
|
||||
return_value=sdk_profile.Profile(attrs=self.response['profile']))
|
||||
return_value=sdk_profile.Profile(**self.response['profile']))
|
||||
self.mock_client.get_profile = mock.Mock(
|
||||
return_value=sdk_profile.Profile(attrs=self.response['profile']))
|
||||
return_value=sdk_profile.Profile(**self.response['profile']))
|
||||
utils.get_dict_properties = mock.Mock(return_value='')
|
||||
|
||||
def test_profile_create_defaults(self):
|
||||
@@ -376,11 +377,11 @@ class TestProfileUpdate(TestProfile):
|
||||
super(TestProfileUpdate, self).setUp()
|
||||
self.cmd = osc_profile.UpdateProfile(self.app, None)
|
||||
self.mock_client.update_profile = mock.Mock(
|
||||
return_value=sdk_profile.Profile(attrs=self.response['profile']))
|
||||
return_value=sdk_profile.Profile(**self.response['profile']))
|
||||
self.mock_client.get_profile = mock.Mock(
|
||||
return_value=sdk_profile.Profile(attrs=self.response['profile']))
|
||||
return_value=sdk_profile.Profile(**self.response['profile']))
|
||||
self.mock_client.find_profile = mock.Mock(
|
||||
return_value=sdk_profile.Profile(attrs=self.response['profile']))
|
||||
return_value=sdk_profile.Profile(**self.response['profile']))
|
||||
utils.get_dict_properties = mock.Mock(return_value='')
|
||||
|
||||
def test_profile_update_defaults(self):
|
||||
@@ -401,3 +402,49 @@ class TestProfileUpdate(TestProfile):
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
self.assertIn('Profile not found: c6b8b252', str(error))
|
||||
|
||||
|
||||
class TestProfileValidate(TestProfile):
|
||||
|
||||
spec_path = 'senlinclient/tests/test_specs/nova_server.yaml'
|
||||
response = {"profile": {
|
||||
"created_at": None,
|
||||
"domain": None,
|
||||
"id": None,
|
||||
"metadata": None,
|
||||
"name": "validated_profile",
|
||||
"project": "5f1cc92b578e4e25a3b284179cf20a9b",
|
||||
"spec": {"properties": {
|
||||
"flavor": 1,
|
||||
"image": "cirros-0.3.4-x86_64-uec",
|
||||
"name": "cirros_server"},
|
||||
"type": "os.nova.server",
|
||||
"version": 1.0},
|
||||
"type": "os.nova.server-1.0",
|
||||
"updated_at": None,
|
||||
"user": "2d7aca950f3e465d8ef0c81720faf6ff"}}
|
||||
|
||||
defaults = {
|
||||
"spec": {
|
||||
"version": 1.0,
|
||||
"type": "os.nova.server",
|
||||
"properties": {
|
||||
"flavor": 1,
|
||||
"name": "cirros_server",
|
||||
"image": "cirros-0.3.4-x86_64-uec"
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestProfileValidate, self).setUp()
|
||||
self.cmd = osc_profile.ValidateProfile(self.app, None)
|
||||
self.mock_client.validate_profile = mock.Mock(
|
||||
return_value=sdk_profile.Profile(**self.response['profile']))
|
||||
utils.get_dict_properties = mock.Mock(return_value='')
|
||||
|
||||
def test_profile_validate(self):
|
||||
arglist = ['--spec-file', self.spec_path]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.mock_client.validate_profile.assert_called_with(**self.defaults)
|
||||
|
||||
@@ -14,7 +14,7 @@ import mock
|
||||
|
||||
from openstack.cluster.v1 import profile_type as sdk_profile_type
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from osc_lib import exceptions as exc
|
||||
|
||||
from senlinclient.tests.unit.v1 import fakes
|
||||
from senlinclient.v1 import profile_type as osc_profile_type
|
||||
@@ -29,18 +29,9 @@ class TestProfileType(fakes.TestClusteringv1):
|
||||
class TestProfileTypeList(TestProfileType):
|
||||
expected_columns = ['name']
|
||||
list_response = [
|
||||
sdk_profile_type.ProfileType({'name': 'BBB',
|
||||
'schema': {
|
||||
'foo': 'bar'}}
|
||||
),
|
||||
sdk_profile_type.ProfileType({'name': 'AAA',
|
||||
'schema': {
|
||||
'foo': 'bar'}}
|
||||
),
|
||||
sdk_profile_type.ProfileType({'name': 'CCC',
|
||||
'schema': {
|
||||
'foo': 'bar'}}
|
||||
),
|
||||
sdk_profile_type.ProfileType(name='BBB', schema={'foo': 'bar'}),
|
||||
sdk_profile_type.ProfileType(name='AAA', schema={'foo': 'bar'}),
|
||||
sdk_profile_type.ProfileType(name='CCC', schema={'foo': 'bar'}),
|
||||
]
|
||||
expected_rows = [
|
||||
['AAA'],
|
||||
@@ -74,7 +65,7 @@ class TestProfileTypeShow(TestProfileType):
|
||||
super(TestProfileTypeShow, self).setUp()
|
||||
self.cmd = osc_profile_type.ProfileTypeShow(self.app, None)
|
||||
self.mock_client.get_profile_type = mock.Mock(
|
||||
return_value=sdk_profile_type.ProfileType(self.response)
|
||||
return_value=sdk_profile_type.ProfileType(**self.response)
|
||||
)
|
||||
|
||||
def test_profile_type_show(self):
|
||||
|
||||
@@ -16,8 +16,9 @@ import six
|
||||
|
||||
from openstack.cluster.v1 import receiver as sdk_receiver
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from osc_lib import exceptions as exc
|
||||
|
||||
from senlinclient.common.i18n import _
|
||||
from senlinclient.tests.unit.v1 import fakes
|
||||
from senlinclient.v1 import receiver as osc_receiver
|
||||
|
||||
@@ -67,8 +68,7 @@ class TestReceiverList(TestReceiver):
|
||||
def setUp(self):
|
||||
super(TestReceiverList, self).setUp()
|
||||
self.cmd = osc_receiver.ListReceiver(self.app, None)
|
||||
self.mock_client.receivers = mock.Mock(
|
||||
return_value=self.response)
|
||||
self.mock_client.receivers = mock.Mock(return_value=self.response)
|
||||
|
||||
def test_receiver_list_defaults(self):
|
||||
arglist = []
|
||||
@@ -172,9 +172,8 @@ class TestReceiverShow(TestReceiver):
|
||||
def setUp(self):
|
||||
super(TestReceiverShow, self).setUp()
|
||||
self.cmd = osc_receiver.ShowReceiver(self.app, None)
|
||||
self.mock_client.get_receiver = mock.Mock(
|
||||
return_value=sdk_receiver.Receiver(
|
||||
attrs=self.get_response['receiver']))
|
||||
x_receiver = sdk_receiver.Receiver(**self.get_response['receiver'])
|
||||
self.mock_client.get_receiver = mock.Mock(return_value=x_receiver)
|
||||
|
||||
def test_receiver_show(self):
|
||||
arglist = ['my_receiver']
|
||||
@@ -231,13 +230,11 @@ class TestReceiverCreate(TestReceiver):
|
||||
super(TestReceiverCreate, self).setUp()
|
||||
self.cmd = osc_receiver.CreateReceiver(self.app, None)
|
||||
self.mock_client.create_receiver = mock.Mock(
|
||||
return_value=sdk_receiver.Receiver(
|
||||
attrs=self.response['receiver']))
|
||||
return_value=sdk_receiver.Receiver(**self.response['receiver']))
|
||||
self.mock_client.get_receiver = mock.Mock(
|
||||
return_value=sdk_receiver.Receiver(
|
||||
attrs=self.response['receiver']))
|
||||
return_value=sdk_receiver.Receiver(**self.response['receiver']))
|
||||
|
||||
def test_receiver_create(self):
|
||||
def test_receiver_create_webhook(self):
|
||||
arglist = ['my_receiver', '--action', 'CLUSTER_SCALE_OUT',
|
||||
'--cluster', 'my_cluster', '--params', 'count=1',
|
||||
'--type', 'webhook']
|
||||
@@ -245,6 +242,26 @@ class TestReceiverCreate(TestReceiver):
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.mock_client.create_receiver.assert_called_with(**self.args)
|
||||
|
||||
def test_receiver_create_webhook_failed(self):
|
||||
arglist = ['my_receiver', '--action', 'CLUSTER_SCALE_OUT',
|
||||
'--params', 'count=1', '--type', 'webhook']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = self.assertRaises(exc.CommandError, self.cmd.take_action,
|
||||
parsed_args)
|
||||
self.assertIn(_('cluster and action parameters are required to create '
|
||||
'webhook type of receiver'), str(error))
|
||||
|
||||
def test_receiver_create_non_webhook(self):
|
||||
arglist = ['my_receiver', '--params', 'count=1',
|
||||
'--type', 'foo']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
args = copy.deepcopy(self.args)
|
||||
args['type'] = 'foo'
|
||||
args['cluster_id'] = None
|
||||
args['action'] = None
|
||||
self.mock_client.create_receiver.assert_called_with(**args)
|
||||
|
||||
|
||||
class TestReceiverDelete(TestReceiver):
|
||||
def setUp(self):
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import subprocess
|
||||
|
||||
import mock
|
||||
import six
|
||||
import testtools
|
||||
@@ -66,8 +68,9 @@ class ShellTest(testtools.TestCase):
|
||||
'api': utils.json_formatter,
|
||||
'engine': utils.json_formatter,
|
||||
}
|
||||
mock_print.assert_called_once_with(result, formatters=formatters)
|
||||
self.assertTrue(service.get_build_info.called)
|
||||
mock_print.assert_called_once_with(result.to_dict(),
|
||||
formatters=formatters)
|
||||
service.get_build_info.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(utils, 'print_list')
|
||||
def test_do_profile_type_list(self, mock_print):
|
||||
@@ -297,6 +300,48 @@ class ShellTest(testtools.TestCase):
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
service.delete_profile.assert_called_with('profile2', False)
|
||||
|
||||
@mock.patch.object(utils, 'process_stack_spec')
|
||||
@mock.patch.object(utils, 'get_spec_content')
|
||||
def test_do_profile_validate(self, mock_get, mock_proc):
|
||||
args = self._make_args({'spec_file': mock.Mock()})
|
||||
spec = copy.deepcopy(self.profile_spec)
|
||||
mock_get.return_value = spec
|
||||
params = {
|
||||
'spec': spec,
|
||||
}
|
||||
service = mock.Mock()
|
||||
profile = mock.Mock()
|
||||
profile.to_dict.return_value = {}
|
||||
service.validate_profile.return_value = profile
|
||||
|
||||
sh.do_profile_validate(service, args)
|
||||
|
||||
service.validate_profile.assert_called_once_with(**params)
|
||||
|
||||
# Miss 'type' key in spec file
|
||||
del spec['type']
|
||||
ex = self.assertRaises(exc.CommandError,
|
||||
sh.do_profile_validate,
|
||||
service, args)
|
||||
self.assertEqual(_("Missing 'type' key in spec file."),
|
||||
six.text_type(ex))
|
||||
# Miss 'version' key in spec file
|
||||
spec['type'] = 'os.heat.stack'
|
||||
del spec['version']
|
||||
ex = self.assertRaises(exc.CommandError,
|
||||
sh.do_profile_validate,
|
||||
service, args)
|
||||
self.assertEqual(_("Missing 'version' key in spec file."),
|
||||
six.text_type(ex))
|
||||
# Miss 'properties' key in spec file
|
||||
spec['version'] = 1.0
|
||||
del spec['properties']
|
||||
ex = self.assertRaises(exc.CommandError,
|
||||
sh.do_profile_validate,
|
||||
service, args)
|
||||
self.assertEqual(_("Missing 'properties' key in spec file."),
|
||||
six.text_type(ex))
|
||||
|
||||
@mock.patch.object(utils, 'print_list')
|
||||
def test_do_policy_type_list(self, mock_print):
|
||||
service = mock.Mock()
|
||||
@@ -425,7 +470,7 @@ class ShellTest(testtools.TestCase):
|
||||
receiver_id='receiver_id')
|
||||
|
||||
@mock.patch.object(sh, '_show_receiver')
|
||||
def test_do_receiver_create(self, mock_show):
|
||||
def test_do_receiver_create_webhook(self, mock_show):
|
||||
service = mock.Mock()
|
||||
args = {
|
||||
'name': 'receiver1',
|
||||
@@ -449,6 +494,47 @@ class ShellTest(testtools.TestCase):
|
||||
service.create_receiver.assert_called_once_with(**params)
|
||||
mock_show.assert_called_once_with(service, 'FAKE_ID')
|
||||
|
||||
def test_do_receiver_create_webhook_failed(self):
|
||||
service = mock.Mock()
|
||||
args = {
|
||||
'name': 'receiver1',
|
||||
'type': 'webhook',
|
||||
'cluster': None,
|
||||
'action': None,
|
||||
'params': {}
|
||||
}
|
||||
args = self._make_args(args)
|
||||
ex = self.assertRaises(exc.CommandError,
|
||||
sh.do_receiver_create, service, args)
|
||||
msg = _("cluster and action parameters are required to create webhook"
|
||||
" type of receiver.")
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
|
||||
@mock.patch.object(sh, '_show_receiver')
|
||||
def test_do_receiver_create_non_webhook(self, mock_show):
|
||||
service = mock.Mock()
|
||||
args = {
|
||||
'name': 'receiver1',
|
||||
'type': 'foo',
|
||||
'cluster': None,
|
||||
'action': None,
|
||||
'params': {}
|
||||
}
|
||||
args = self._make_args(args)
|
||||
params = {
|
||||
'name': 'receiver1',
|
||||
'type': 'foo',
|
||||
'cluster_id': None,
|
||||
'action': None,
|
||||
'params': {}
|
||||
}
|
||||
receiver = mock.Mock()
|
||||
receiver.id = 'FAKE_ID'
|
||||
service.create_receiver.return_value = receiver
|
||||
sh.do_receiver_create(service, args)
|
||||
service.create_receiver.assert_called_once_with(**params)
|
||||
mock_show.assert_called_once_with(service, 'FAKE_ID')
|
||||
|
||||
def test_do_receiver_delete(self):
|
||||
service = mock.Mock()
|
||||
args = {'id': ['FAKE']}
|
||||
@@ -598,6 +684,25 @@ class ShellTest(testtools.TestCase):
|
||||
msg = _("Failed to delete some of the specified policy(s).")
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
|
||||
@mock.patch.object(utils, 'get_spec_content')
|
||||
def test_do_policy_validate(self, mock_get):
|
||||
service = mock.Mock()
|
||||
spec = mock.Mock()
|
||||
mock_get.return_value = spec
|
||||
args = {
|
||||
'spec_file': 'policy_file',
|
||||
}
|
||||
args = self._make_args({'spec_file': 'policy_file'})
|
||||
attrs = {
|
||||
'spec': spec,
|
||||
}
|
||||
policy = mock.Mock()
|
||||
policy.to_dict.return_value = {}
|
||||
service.validate_policy.return_value = policy
|
||||
sh.do_policy_validate(service, args)
|
||||
mock_get.assert_called_once_with(args.spec_file)
|
||||
service.validate_policy.assert_called_once_with(**attrs)
|
||||
|
||||
@mock.patch.object(utils, 'print_list')
|
||||
def test_do_cluster_list(self, mock_print):
|
||||
service = mock.Mock()
|
||||
@@ -636,7 +741,7 @@ class ShellTest(testtools.TestCase):
|
||||
service.get_cluster.return_value = cluster
|
||||
formatters = {
|
||||
'metadata': utils.json_formatter,
|
||||
'nodes': utils.list_formatter,
|
||||
'node_ids': utils.list_formatter,
|
||||
}
|
||||
cluster_dict = mock.Mock()
|
||||
cluster.to_dict.return_value = cluster_dict
|
||||
@@ -675,16 +780,174 @@ class ShellTest(testtools.TestCase):
|
||||
sh.do_cluster_delete(service, args)
|
||||
service.delete_cluster.assert_called_once_with('CID', False)
|
||||
|
||||
def test_do_cluster_delete_not_found(self):
|
||||
service = mock.Mock()
|
||||
args = {'id': ['cluster_id']}
|
||||
args = self._make_args(args)
|
||||
@mock.patch('subprocess.Popen')
|
||||
def test__run_script(self, mock_proc):
|
||||
x_proc = mock.Mock(returncode=0)
|
||||
x_stdout = 'OUTPUT'
|
||||
x_stderr = 'ERROR'
|
||||
x_proc.communicate.return_value = (x_stdout, x_stderr)
|
||||
mock_proc.return_value = x_proc
|
||||
|
||||
service.delete_cluster.side_effect = oexc.ResourceNotFound
|
||||
ex = self.assertRaises(exc.CommandError,
|
||||
sh.do_cluster_delete, service, args)
|
||||
msg = _('Failed to delete some of the specified clusters.')
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
addr = {
|
||||
'private': [
|
||||
{
|
||||
'OS-EXT-IPS:type': 'floating',
|
||||
'version': 4,
|
||||
'addr': '1.2.3.4',
|
||||
}
|
||||
]
|
||||
}
|
||||
output = {}
|
||||
|
||||
sh._run_script('NODE_ID', addr, 'private', 'floating', 22, 'john',
|
||||
False, 'identity_path', 'echo foo', '-f bar',
|
||||
output=output)
|
||||
mock_proc.assert_called_once_with(
|
||||
['ssh', '-4', '-p22', '-i identity_path', '-f bar', 'john@1.2.3.4',
|
||||
'echo foo'],
|
||||
stdout=subprocess.PIPE)
|
||||
self.assertEqual(
|
||||
{'status': 'SUCCEEDED (0)', 'output': 'OUTPUT', 'error': 'ERROR'},
|
||||
output)
|
||||
|
||||
def test__run_script_network_not_found(self):
|
||||
addr = {'foo': 'bar'}
|
||||
output = {}
|
||||
|
||||
sh._run_script('NODE_ID', addr, 'private', 'floating', 22, 'john',
|
||||
False, 'identity_path', 'echo foo', '-f bar',
|
||||
output=output)
|
||||
self.assertEqual(
|
||||
{'status': 'FAILED',
|
||||
'reason': "Node 'NODE_ID' is not attached to network 'private'."
|
||||
},
|
||||
output)
|
||||
|
||||
def test__run_script_more_than_one_network(self):
|
||||
addr = {'foo': 'bar', 'koo': 'tar'}
|
||||
output = {}
|
||||
|
||||
sh._run_script('NODE_ID', addr, '', 'floating', 22, 'john',
|
||||
False, 'identity_path', 'echo foo', '-f bar',
|
||||
output=output)
|
||||
self.assertEqual(
|
||||
{'status': 'FAILED',
|
||||
'reason': "Node 'NODE_ID' is attached to more than one "
|
||||
"network. Please pick the network to use."},
|
||||
output)
|
||||
|
||||
def test__run_script_no_network(self):
|
||||
addr = {}
|
||||
output = {}
|
||||
|
||||
sh._run_script('NODE_ID', addr, '', 'floating', 22, 'john',
|
||||
False, 'identity_path', 'echo foo', '-f bar',
|
||||
output=output)
|
||||
|
||||
self.assertEqual(
|
||||
{'status': 'FAILED',
|
||||
'reason': "Node 'NODE_ID' is not attached to any network."},
|
||||
output)
|
||||
|
||||
def test__run_script_no_matching_address(self):
|
||||
addr = {
|
||||
'private': [
|
||||
{
|
||||
'OS-EXT-IPS:type': 'fixed',
|
||||
'version': 4,
|
||||
'addr': '1.2.3.4',
|
||||
}
|
||||
]
|
||||
}
|
||||
output = {}
|
||||
|
||||
sh._run_script('NODE_ID', addr, 'private', 'floating', 22, 'john',
|
||||
False, 'identity_path', 'echo foo', '-f bar',
|
||||
output=output)
|
||||
self.assertEqual(
|
||||
{'status': 'FAILED',
|
||||
'reason': "No address that would match network 'private' and "
|
||||
"type 'floating' of IPv4 has been found for node "
|
||||
"'NODE_ID'."},
|
||||
output)
|
||||
|
||||
def test__run_script_more_than_one_address(self):
|
||||
addr = {
|
||||
'private': [
|
||||
{
|
||||
'OS-EXT-IPS:type': 'fixed',
|
||||
'version': 4,
|
||||
'addr': '1.2.3.4',
|
||||
},
|
||||
{
|
||||
'OS-EXT-IPS:type': 'fixed',
|
||||
'version': 4,
|
||||
'addr': '5.6.7.8',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
output = {}
|
||||
|
||||
sh._run_script('NODE_ID', addr, 'private', 'fixed', 22, 'john',
|
||||
False, 'identity_path', 'echo foo', '-f bar',
|
||||
output=output)
|
||||
self.assertEqual(
|
||||
{'status': 'FAILED',
|
||||
'reason': "More than one IPv4 fixed address found."},
|
||||
output)
|
||||
|
||||
@mock.patch('threading.Thread')
|
||||
@mock.patch.object(sh, '_run_script')
|
||||
def test_do_cluster_run(self, mock_run, mock_thread):
|
||||
service = mock.Mock()
|
||||
args = {
|
||||
'script': 'script_name',
|
||||
'network': 'network_name',
|
||||
'address_type': 'fixed',
|
||||
'port': 22,
|
||||
'user': 'root',
|
||||
'ipv6': False,
|
||||
'identity_file': 'identity_filename',
|
||||
'ssh_options': '-f oo',
|
||||
}
|
||||
args = self._make_args(args)
|
||||
args.id = 'CID'
|
||||
addr1 = {'addresses': 'ADDR CONTENT 1'}
|
||||
addr2 = {'addresses': 'ADDR CONTENT 2'}
|
||||
attributes = [
|
||||
mock.Mock(node_id='NODE1', attr_value=addr1),
|
||||
mock.Mock(node_id='NODE2', attr_value=addr2)
|
||||
]
|
||||
service.collect_cluster_attrs.return_value = attributes
|
||||
|
||||
th1 = mock.Mock()
|
||||
th2 = mock.Mock()
|
||||
mock_thread.side_effect = [th1, th2]
|
||||
fake_script = 'blah blah'
|
||||
with mock.patch('senlinclient.v1.shell.open',
|
||||
mock.mock_open(read_data=fake_script)) as mock_open:
|
||||
sh.do_cluster_run(service, args)
|
||||
|
||||
service.collect_cluster_attrs.assert_called_once_with(
|
||||
args.id, 'details')
|
||||
mock_open.assert_called_once_with('script_name', 'r')
|
||||
mock_thread.assert_has_calls([
|
||||
mock.call(target=mock_run,
|
||||
args=('NODE1', 'ADDR CONTENT 1', 'network_name',
|
||||
'fixed', 22, 'root', False, 'identity_filename',
|
||||
'blah blah', '-f oo'),
|
||||
kwargs={'output': {}}),
|
||||
mock.call(target=mock_run,
|
||||
args=('NODE2', 'ADDR CONTENT 2', 'network_name',
|
||||
'fixed', 22, 'root', False, 'identity_filename',
|
||||
'blah blah', '-f oo'),
|
||||
kwargs={'output': {}})
|
||||
])
|
||||
th1.start.assert_called_once_with()
|
||||
th2.start.assert_called_once_with()
|
||||
th1.join.assert_called_once_with()
|
||||
th2.join.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(sh, '_show_cluster')
|
||||
def test_do_cluster_update(self, mock_show):
|
||||
@@ -819,7 +1082,7 @@ class ShellTest(testtools.TestCase):
|
||||
sh.do_cluster_resize,
|
||||
service, args)
|
||||
msg = _('Cluster capacity must be larger than '
|
||||
' or equal to zero.')
|
||||
'or equal to zero.')
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
|
||||
# adjustment
|
||||
@@ -947,7 +1210,7 @@ class ShellTest(testtools.TestCase):
|
||||
|
||||
@mock.patch.object(utils, 'print_list')
|
||||
def test_do_cluster_policy_list(self, mock_print):
|
||||
fields = ['policy_id', 'policy_name', 'policy_type', 'enabled']
|
||||
fields = ['policy_id', 'policy_name', 'policy_type', 'is_enabled']
|
||||
service = mock.Mock()
|
||||
args = {
|
||||
'id': 'C1',
|
||||
@@ -1056,6 +1319,65 @@ class ShellTest(testtools.TestCase):
|
||||
|
||||
service.recover_cluster.assert_called_once_with('cluster1')
|
||||
|
||||
def test_do_cluster_collect(self):
|
||||
service = mock.Mock()
|
||||
args = self._make_args({
|
||||
'path': 'path.to.attr',
|
||||
'list': False,
|
||||
'full_id': False,
|
||||
'id': 'cluster1'
|
||||
})
|
||||
service.collect_cluster_attrs = mock.Mock(
|
||||
return_value=[mock.Mock(node_id='FAKE1', attr_value='VALUE1')]
|
||||
)
|
||||
|
||||
sh.do_cluster_collect(service, args)
|
||||
|
||||
service.collect_cluster_attrs.assert_called_once_with(
|
||||
'cluster1', 'path.to.attr')
|
||||
|
||||
@mock.patch.object(utils, 'print_list')
|
||||
def test_do_cluster_collect_as_list(self, mock_print):
|
||||
service = mock.Mock()
|
||||
args = self._make_args({
|
||||
'path': 'path.to.attr',
|
||||
'list': True,
|
||||
'full_id': True,
|
||||
'id': 'cluster1'
|
||||
})
|
||||
attrs = [mock.Mock(node_id='FAKE1', attr_value='VALUE1')]
|
||||
fields = ['node_id', 'attr_value']
|
||||
formatters = {'attr_value': utils.json_formatter}
|
||||
service.collect_cluster_attrs = mock.Mock(return_value=attrs)
|
||||
|
||||
sh.do_cluster_collect(service, args)
|
||||
|
||||
service.collect_cluster_attrs.assert_called_once_with(
|
||||
'cluster1', 'path.to.attr')
|
||||
mock_print.assert_called_once_with(attrs, fields,
|
||||
formatters=formatters)
|
||||
|
||||
@mock.patch.object(utils, 'print_list')
|
||||
def test_do_cluster_collect_as_list_with_shortid(self, mock_print):
|
||||
service = mock.Mock()
|
||||
args = self._make_args({
|
||||
'path': 'path.to.attr',
|
||||
'list': True,
|
||||
'full_id': False,
|
||||
'id': 'cluster1'
|
||||
})
|
||||
attrs = [mock.Mock(node_id='FAKE1', attr_value='VALUE1')]
|
||||
fields = ['node_id', 'attr_value']
|
||||
formatters = {'node_id': mock.ANY, 'attr_value': utils.json_formatter}
|
||||
service.collect_cluster_attrs = mock.Mock(return_value=attrs)
|
||||
|
||||
sh.do_cluster_collect(service, args)
|
||||
|
||||
service.collect_cluster_attrs.assert_called_once_with(
|
||||
'cluster1', 'path.to.attr')
|
||||
mock_print.assert_called_once_with(attrs, fields,
|
||||
formatters=formatters)
|
||||
|
||||
@mock.patch.object(utils, 'print_list')
|
||||
def test_do_node_list(self, mock_print):
|
||||
service = mock.Mock()
|
||||
@@ -1104,7 +1426,7 @@ class ShellTest(testtools.TestCase):
|
||||
|
||||
sh._show_node(service, node_id, show_details=False)
|
||||
|
||||
service.get_node.assert_called_once_with(node_id, args=None)
|
||||
service.get_node.assert_called_once_with(node_id, details=False)
|
||||
mock_print.assert_called_once_with(data, formatters=formatters)
|
||||
|
||||
@mock.patch.object(sh, '_show_node')
|
||||
@@ -1152,17 +1474,6 @@ class ShellTest(testtools.TestCase):
|
||||
|
||||
service.delete_node.assert_called_once_with('node1', False)
|
||||
|
||||
def test_do_node_delete_not_found(self):
|
||||
service = mock.Mock()
|
||||
ex = oexc.ResourceNotFound
|
||||
service.delete_node.side_effect = ex
|
||||
|
||||
args = self._make_args({'id': ['node1']})
|
||||
ex = self.assertRaises(exc.CommandError,
|
||||
sh.do_node_delete, service, args)
|
||||
msg = _('Failed to delete some of the specified nodes.')
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
|
||||
def test_do_node_check(self):
|
||||
service = mock.Mock()
|
||||
args = self._make_args({'id': ['node1']})
|
||||
@@ -1232,7 +1543,7 @@ class ShellTest(testtools.TestCase):
|
||||
def test_do_event_list(self, mock_print):
|
||||
service = mock.Mock()
|
||||
fields = ['id', 'timestamp', 'obj_type', 'obj_id', 'obj_name',
|
||||
'action', 'status', 'status_reason', 'level']
|
||||
'action', 'status', 'level', 'cluster_id']
|
||||
args = {
|
||||
'sort': 'timestamp:asc',
|
||||
'limit': 20,
|
||||
|
||||
@@ -13,19 +13,17 @@
|
||||
"""Clustering v1 action implementations"""
|
||||
|
||||
import logging
|
||||
import six
|
||||
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from openstackclient.common import utils
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions as exc
|
||||
from osc_lib import utils
|
||||
|
||||
from senlinclient.common.i18n import _
|
||||
from senlinclient.common import utils as senlin_utils
|
||||
|
||||
|
||||
class ListAction(lister.Lister):
|
||||
class ListAction(command.Lister):
|
||||
"""List actions."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ListAction")
|
||||
@@ -104,12 +102,13 @@ class ListAction(lister.Lister):
|
||||
|
||||
return (
|
||||
columns,
|
||||
(utils.get_item_properties(a, columns, formatters=formatters)
|
||||
(utils.get_item_properties(a.to_dict(), columns,
|
||||
formatters=formatters)
|
||||
for a in actions)
|
||||
)
|
||||
|
||||
|
||||
class ShowAction(show.ShowOne):
|
||||
class ShowAction(command.ShowOne):
|
||||
"""Show detailed info about the specified action."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ShowAction")
|
||||
@@ -141,6 +140,7 @@ class ShowAction(show.ShowOne):
|
||||
'depends_on': senlin_utils.list_formatter,
|
||||
'depended_by': senlin_utils.list_formatter,
|
||||
}
|
||||
columns = sorted(list(six.iterkeys(action)))
|
||||
return columns, utils.get_dict_properties(action.to_dict(), columns,
|
||||
data = action.to_dict()
|
||||
columns = sorted(data.keys())
|
||||
return columns, utils.get_dict_properties(data, columns,
|
||||
formatters=formatters)
|
||||
|
||||
@@ -13,15 +13,14 @@
|
||||
"""Clustering v1 build_info action implementations"""
|
||||
|
||||
import logging
|
||||
import six
|
||||
|
||||
from cliff import show
|
||||
from openstackclient.common import utils
|
||||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
|
||||
from senlinclient.common import utils as senlin_utils
|
||||
|
||||
|
||||
class BuildInfo(show.ShowOne):
|
||||
class BuildInfo(command.ShowOne):
|
||||
"""Retrieve build information."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".BuildInfo")
|
||||
@@ -40,6 +39,10 @@ class BuildInfo(show.ShowOne):
|
||||
'api': senlin_utils.json_formatter,
|
||||
'engine': senlin_utils.json_formatter,
|
||||
}
|
||||
columns = sorted(list(six.iterkeys(result)))
|
||||
return columns, utils.get_dict_properties(result.to_dict(), columns,
|
||||
data = {
|
||||
'api': result.api,
|
||||
'engine': result.engine,
|
||||
}
|
||||
columns = ['api', 'engine']
|
||||
return columns, utils.get_dict_properties(data, columns,
|
||||
formatters=formatters)
|
||||
|
||||
@@ -15,8 +15,9 @@ from senlinclient.common import sdk
|
||||
|
||||
class Client(object):
|
||||
|
||||
def __init__(self, preferences=None, user_agent=None, **kwargs):
|
||||
self.conn = sdk.create_connection(preferences, user_agent, **kwargs)
|
||||
def __init__(self, prof=None, user_agent=None, **kwargs):
|
||||
self.conn = sdk.create_connection(prof=prof, user_agent=user_agent,
|
||||
**kwargs)
|
||||
self.service = self.conn.cluster
|
||||
|
||||
######################################################################
|
||||
@@ -83,6 +84,14 @@ class Client(object):
|
||||
"""
|
||||
return self.service.delete_profile(profile, ignore_missing)
|
||||
|
||||
def validate_profile(self, **attrs):
|
||||
"""Validate a profile spec
|
||||
|
||||
Doc link:
|
||||
http://developer.openstack.org/api-ref-clustering-v1.html#validateProfile
|
||||
"""
|
||||
return self.service.validate_profile(**attrs)
|
||||
|
||||
def policy_types(self, **query):
|
||||
"""List policy types
|
||||
|
||||
@@ -141,6 +150,14 @@ class Client(object):
|
||||
"""
|
||||
return self.service.delete_policy(policy, ignore_missing)
|
||||
|
||||
def validate_policy(self, **attrs):
|
||||
"""validate a policy spec
|
||||
|
||||
Doc link:
|
||||
http://developer.openstack.org/api-ref-clustering-v1.html#validatePolicy
|
||||
"""
|
||||
return self.service.validate_policy(**attrs)
|
||||
|
||||
def clusters(self, **queries):
|
||||
"""List clusters
|
||||
|
||||
@@ -263,6 +280,14 @@ class Client(object):
|
||||
"""
|
||||
return self.service.cluster_update_policy(cluster, policy, **attrs)
|
||||
|
||||
def cluster_collect(self, cluster, path):
|
||||
"""Resize cluster
|
||||
|
||||
Doc link:
|
||||
http://developer.openstack.org/api-ref-clustering-v1.html#clusterAction
|
||||
"""
|
||||
return self.service.cluster_collect(cluster, path)
|
||||
|
||||
def check_cluster(self, cluster, **params):
|
||||
"""Check cluster's health status
|
||||
|
||||
@@ -295,13 +320,13 @@ class Client(object):
|
||||
"""
|
||||
return self.service.create_node(**attrs)
|
||||
|
||||
def get_node(self, node, args=None):
|
||||
def get_node(self, node, details=False):
|
||||
"""Show node details
|
||||
|
||||
Doc link:
|
||||
http://developer.openstack.org/api-ref-clustering-v1.html#showNode
|
||||
"""
|
||||
return self.service.get_node(node, args=args)
|
||||
return self.service.get_node(node, details=details)
|
||||
|
||||
def update_node(self, node, **attrs):
|
||||
"""Update node
|
||||
|
||||
@@ -13,22 +13,23 @@
|
||||
"""Clustering v1 cluster action implementations"""
|
||||
|
||||
import logging
|
||||
import six
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
from cliff import command
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from openstackclient.common import utils
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions as exc
|
||||
from osc_lib import utils
|
||||
import six
|
||||
|
||||
from senlinclient.common.i18n import _
|
||||
from senlinclient.common.i18n import _LI
|
||||
from senlinclient.common import utils as senlin_utils
|
||||
|
||||
|
||||
class ListCluster(lister.Lister):
|
||||
class ListCluster(command.Lister):
|
||||
"""List the user's clusters."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ListCluster")
|
||||
@@ -106,7 +107,7 @@ class ListCluster(lister.Lister):
|
||||
)
|
||||
|
||||
|
||||
class ShowCluster(show.ShowOne):
|
||||
class ShowCluster(command.ShowOne):
|
||||
"""Show details of the cluster."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ShowCluster")
|
||||
@@ -135,26 +136,21 @@ def _show_cluster(senlin_client, cluster_id):
|
||||
|
||||
formatters = {
|
||||
'metadata': senlin_utils.json_formatter,
|
||||
'nodes': senlin_utils.list_formatter
|
||||
'node_ids': senlin_utils.list_formatter
|
||||
}
|
||||
columns = sorted(list(six.iterkeys(cluster)))
|
||||
return columns, utils.get_dict_properties(cluster.to_dict(), columns,
|
||||
data = cluster.to_dict()
|
||||
columns = sorted(data.keys())
|
||||
return columns, utils.get_dict_properties(data, columns,
|
||||
formatters=formatters)
|
||||
|
||||
|
||||
class CreateCluster(show.ShowOne):
|
||||
class CreateCluster(command.ShowOne):
|
||||
"""Create the cluster."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".CreateCluster")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateCluster, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--profile',
|
||||
metavar='<profile>',
|
||||
required=True,
|
||||
help=_('Profile Id used for this cluster')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--min-size',
|
||||
metavar='<min-size>',
|
||||
@@ -188,6 +184,12 @@ class CreateCluster(show.ShowOne):
|
||||
'key-value pairs separated by a semicolon.'),
|
||||
action='append'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--profile',
|
||||
metavar='<profile>',
|
||||
required=True,
|
||||
help=_('Profile Id used for this cluster')
|
||||
)
|
||||
parser.add_argument(
|
||||
'name',
|
||||
metavar='<cluster-name>',
|
||||
@@ -215,7 +217,7 @@ class CreateCluster(show.ShowOne):
|
||||
return _show_cluster(senlin_client, cluster.id)
|
||||
|
||||
|
||||
class UpdateCluster(show.ShowOne):
|
||||
class UpdateCluster(command.ShowOne):
|
||||
"""Update the cluster."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".UpdateCluster")
|
||||
@@ -309,20 +311,16 @@ class DeleteCluster(command.Command):
|
||||
self.log.info(_LI('Ctrl-d detected'))
|
||||
return
|
||||
|
||||
failure_count = 0
|
||||
|
||||
result = {}
|
||||
for cid in parsed_args.cluster:
|
||||
try:
|
||||
senlin_client.delete_cluster(cid, False)
|
||||
cluster = senlin_client.delete_cluster(cid, False)
|
||||
result[cid] = ('OK', cluster.location.split('/')[-1])
|
||||
except Exception as ex:
|
||||
failure_count += 1
|
||||
print(ex)
|
||||
if failure_count:
|
||||
raise exc.CommandError(_('Failed to delete %(count)s of the '
|
||||
'%(total)s specified cluster(s).') %
|
||||
{'count': failure_count,
|
||||
'total': len(parsed_args.cluster)})
|
||||
print('Request accepted')
|
||||
result[cid] = ('ERROR', six.text_type(ex))
|
||||
|
||||
for rid, res in result.items():
|
||||
senlin_utils.print_action_result(rid, res)
|
||||
|
||||
|
||||
class ResizeCluster(command.Command):
|
||||
@@ -405,11 +403,6 @@ class ResizeCluster(command.Command):
|
||||
raise exc.CommandError(_("Only one of 'capacity', 'adjustment' and"
|
||||
" 'percentage' can be specified."))
|
||||
|
||||
if sum(v is None for v in (capacity, adjustment, percentage)) == 3:
|
||||
raise exc.CommandError(_("At least one of 'capacity', "
|
||||
"'adjustment' and 'percentage' "
|
||||
"should be specified."))
|
||||
|
||||
action_args['adjustment_type'] = None
|
||||
action_args['number'] = None
|
||||
|
||||
@@ -527,12 +520,6 @@ class ClusterPolicyAttach(command.Command):
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ClusterPolicyAttach, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--policy',
|
||||
metavar='<policy>',
|
||||
required=True,
|
||||
help=_('ID or name of policy to be attached')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--enabled',
|
||||
default=True,
|
||||
@@ -540,6 +527,12 @@ class ClusterPolicyAttach(command.Command):
|
||||
help=_('Whether the policy should be enabled once attached. '
|
||||
'Default to True')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--policy',
|
||||
metavar='<policy>',
|
||||
required=True,
|
||||
help=_('ID or name of policy to be attached')
|
||||
)
|
||||
parser.add_argument(
|
||||
'cluster',
|
||||
metavar='<cluster>',
|
||||
@@ -589,7 +582,7 @@ class ClusterPolicyDetach(command.Command):
|
||||
print('Request accepted by action: %s' % resp['action'])
|
||||
|
||||
|
||||
class ClusterNodeList(lister.Lister):
|
||||
class ClusterNodeList(command.Lister):
|
||||
"""List nodes from cluster."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ClusterNodeList")
|
||||
@@ -772,3 +765,246 @@ class RecoverCluster(command.Command):
|
||||
print('Cluster recover request on cluster %(cid)s is accepted by '
|
||||
'action %(action)s.'
|
||||
% {'cid': cid, 'action': resp['action']})
|
||||
|
||||
|
||||
class ClusterCollect(command.Lister):
|
||||
"""Recover the cluster(s)."""
|
||||
log = logging.getLogger(__name__ + ".ClusterCollect")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ClusterCollect, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--full-id',
|
||||
default=False,
|
||||
action="store_true",
|
||||
help=_('Print full IDs in list')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--path',
|
||||
metavar='<path>',
|
||||
required=True,
|
||||
help=_('JSON path expression for attribute to be collected')
|
||||
)
|
||||
parser.add_argument(
|
||||
'cluster',
|
||||
metavar='<cluster>',
|
||||
help=_('ID or name of cluster(s) to operate on.')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
senlin_client = self.app.client_manager.clustering
|
||||
attrs = senlin_client.collect_cluster_attrs(parsed_args.cluster,
|
||||
parsed_args.path)
|
||||
columns = ['node_id', 'attr_value']
|
||||
formatters = {}
|
||||
if not parsed_args.full_id:
|
||||
formatters = {
|
||||
'node_id': lambda x: x[:8]
|
||||
}
|
||||
return (columns,
|
||||
(utils.get_item_properties(a, columns, formatters=formatters)
|
||||
for a in attrs))
|
||||
|
||||
|
||||
class ClusterRun(command.Command):
|
||||
"""Run scripts on cluster."""
|
||||
log = logging.getLogger(__name__ + ".ClusterRun")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ClusterRun, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--port',
|
||||
metavar='<port>',
|
||||
type=int,
|
||||
default=22,
|
||||
help=_('The TCP port to use for SSH connection')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--address-type',
|
||||
metavar='<address_type>',
|
||||
default='floating',
|
||||
help=_("The type of IP address to use. Possible values include "
|
||||
"'fixed' and 'floating' (the default)")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--network',
|
||||
metavar='<network>',
|
||||
default='',
|
||||
help=_("The network to use for SSH connection")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--ipv6',
|
||||
action="store_true",
|
||||
default=False,
|
||||
help=_("Whether the IPv6 address should be used for SSH. Default "
|
||||
"to use IPv4 address.")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--user',
|
||||
metavar='<user>',
|
||||
default='root',
|
||||
help=_("The login name to use for SSH connection. Default to "
|
||||
"'root'.")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--identity-file',
|
||||
metavar='<identity_file>',
|
||||
help=_("The private key file to use, same as the '-i' SSH option")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--ssh-options',
|
||||
metavar='<ssh_options>',
|
||||
default="",
|
||||
help=_("Extra options to pass to SSH. See: man ssh.")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--script',
|
||||
metavar='<script>',
|
||||
required=True,
|
||||
help=_("Path name of the script file to run")
|
||||
)
|
||||
parser.add_argument(
|
||||
'cluster',
|
||||
metavar='<cluster>',
|
||||
help=_('ID or name of cluster(s) to operate on.')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, args):
|
||||
self.log.debug("take_action(%s)", args)
|
||||
service = self.app.client_manager.clustering
|
||||
|
||||
if '@' in args.cluster:
|
||||
user, cluster = args.cluster.split('@', 1)
|
||||
args.user = user
|
||||
args.cluster = cluster
|
||||
|
||||
try:
|
||||
attributes = service.collect_cluster_attrs(args.cluster, 'details')
|
||||
except sdk_exc.ResourceNotFound:
|
||||
raise exc.CommandError(_("Cluster not found: %s") % args.cluster)
|
||||
|
||||
script = None
|
||||
try:
|
||||
f = open(args.script, 'r')
|
||||
script = f.read()
|
||||
except Exception:
|
||||
raise exc.CommandError(_("Cound not open script file: %s") %
|
||||
args.script)
|
||||
|
||||
tasks = dict()
|
||||
for attr in attributes:
|
||||
node_id = attr.node_id
|
||||
addr = attr.attr_value['addresses']
|
||||
|
||||
output = dict()
|
||||
th = threading.Thread(
|
||||
target=self._run_script,
|
||||
args=(node_id, addr, args.network, args.address_type,
|
||||
args.port, args.user, args.ipv6, args.identity_file,
|
||||
script, args.ssh_options),
|
||||
kwargs={'output': output})
|
||||
th.start()
|
||||
tasks[th] = (node_id, output)
|
||||
|
||||
for t in tasks:
|
||||
t.join()
|
||||
|
||||
for t in tasks:
|
||||
node_id, result = tasks[t]
|
||||
print("node: %s" % node_id)
|
||||
print("status: %s" % result.get('status'))
|
||||
if "reason" in result:
|
||||
print("reason: %s" % result.get('reason'))
|
||||
if "output" in result:
|
||||
print("output:\n%s" % result.get('output'))
|
||||
if "error" in result:
|
||||
print("error:\n%s" % result.get('error'))
|
||||
|
||||
def _run_script(self, node_id, addr, net, addr_type, port, user, ipv6,
|
||||
identity_file, script, options, output=None):
|
||||
version = 6 if ipv6 else 4
|
||||
|
||||
# Select the network to use.
|
||||
if net:
|
||||
addresses = addr.get(net)
|
||||
if not addresses:
|
||||
output['status'] = _('FAILED')
|
||||
output['error'] = _("Node '%(node)s' is not attached to "
|
||||
"network '%(net)s'.") % {'node': node_id,
|
||||
'net': net}
|
||||
return
|
||||
else:
|
||||
# network not specified
|
||||
if len(addr) > 1:
|
||||
output['status'] = _('FAILED')
|
||||
output['error'] = _("Node '%(node)s' is attached to more "
|
||||
"than one network. Please pick the "
|
||||
"network to use.") % {'node': node_id}
|
||||
return
|
||||
elif not addr:
|
||||
output['status'] = _('FAILED')
|
||||
output['error'] = _("Node '%(node)s' is not attached to any "
|
||||
"network.") % {'node': node_id}
|
||||
return
|
||||
else:
|
||||
addresses = list(six.itervalues(addr))[0]
|
||||
|
||||
# Select the address in the selected network.
|
||||
# If the extension is not present, we assume the address to be
|
||||
# floating.
|
||||
matching_addresses = []
|
||||
for a in addresses:
|
||||
a_type = a.get('OS-EXT-IPS:type', 'floating')
|
||||
a_version = a.get('version')
|
||||
if (a_version == version and a_type == addr_type):
|
||||
matching_addresses.append(a.get('addr'))
|
||||
|
||||
if not matching_addresses:
|
||||
output['status'] = _('FAILED')
|
||||
output['error'] = _("No address that matches network '%(net)s' "
|
||||
"and type '%(type)s' of IPv%(ver)s has been "
|
||||
"found for node '%(node)s'."
|
||||
) % {'net': net, 'type': addr_type,
|
||||
'ver': version, 'node': node_id}
|
||||
return
|
||||
|
||||
if len(matching_addresses) > 1:
|
||||
output['status'] = _('FAILED')
|
||||
output['error'] = _("More than one IPv%(ver)s %(type)s address "
|
||||
"found.") % {'ver': version,
|
||||
'type': addr_type}
|
||||
return
|
||||
|
||||
ip_address = str(matching_addresses[0])
|
||||
identity = '-i %s' % identity_file if identity_file else ''
|
||||
|
||||
cmd = [
|
||||
'ssh',
|
||||
'-%d' % version,
|
||||
'-p%d' % port,
|
||||
identity,
|
||||
options,
|
||||
'%s@%s' % (user, ip_address),
|
||||
'%s' % script
|
||||
]
|
||||
|
||||
self.log.debug("%s" % cmd)
|
||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||
(stdout, stderr) = proc.communicate()
|
||||
|
||||
while proc.returncode is None:
|
||||
time.sleep(1)
|
||||
|
||||
if proc.returncode == 0:
|
||||
output['status'] = _('SUCCEEDED (0)')
|
||||
output['output'] = stdout
|
||||
if stderr:
|
||||
output['error'] = stderr
|
||||
else:
|
||||
output['status'] = _('FAILED (%d)') % proc.returncode
|
||||
output['output'] = stdout
|
||||
if stderr:
|
||||
output['error'] = stderr
|
||||
|
||||
@@ -13,18 +13,15 @@
|
||||
"""Clustering v1 cluster policy action implementations"""
|
||||
|
||||
import logging
|
||||
import six
|
||||
|
||||
from cliff import command
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
from openstackclient.common import utils
|
||||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
|
||||
from senlinclient.common.i18n import _
|
||||
from senlinclient.common import utils as senlin_utils
|
||||
|
||||
|
||||
class ClusterPolicyList(lister.Lister):
|
||||
class ClusterPolicyList(command.Lister):
|
||||
"""List policies from cluster."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ClusterPolicyList")
|
||||
@@ -66,7 +63,7 @@ class ClusterPolicyList(lister.Lister):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
senlin_client = self.app.client_manager.clustering
|
||||
|
||||
columns = ['policy_id', 'policy_name', 'policy_type', 'enabled']
|
||||
columns = ['policy_id', 'policy_name', 'policy_type', 'is_enabled']
|
||||
cluster = senlin_client.get_cluster(parsed_args.cluster)
|
||||
queries = {
|
||||
'sort': parsed_args.sort,
|
||||
@@ -83,12 +80,13 @@ class ClusterPolicyList(lister.Lister):
|
||||
}
|
||||
return (
|
||||
columns,
|
||||
(utils.get_item_properties(p, columns, formatters=formatters)
|
||||
(utils.get_item_properties(p, columns,
|
||||
formatters=formatters)
|
||||
for p in policies)
|
||||
)
|
||||
|
||||
|
||||
class ClusterPolicyShow(show.ShowOne):
|
||||
class ClusterPolicyShow(command.ShowOne):
|
||||
"""Show a specific policy that is bound to the specified cluster."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ClusterPolicyShow")
|
||||
@@ -113,8 +111,9 @@ class ClusterPolicyShow(show.ShowOne):
|
||||
senlin_client = self.app.client_manager.clustering
|
||||
policy = senlin_client.get_cluster_policy(parsed_args.policy,
|
||||
parsed_args.cluster)
|
||||
columns = sorted(list(six.iterkeys(policy)))
|
||||
return columns, utils.get_dict_properties(policy.to_dict(), columns)
|
||||
data = policy.to_dict()
|
||||
columns = sorted(data.keys())
|
||||
return columns, utils.get_dict_properties(data, columns)
|
||||
|
||||
|
||||
class ClusterPolicyUpdate(command.Command):
|
||||
|
||||
@@ -13,19 +13,17 @@
|
||||
"""Clustering v1 event action implementations"""
|
||||
|
||||
import logging
|
||||
import six
|
||||
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from openstackclient.common import utils
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions as exc
|
||||
from osc_lib import utils
|
||||
|
||||
from senlinclient.common.i18n import _
|
||||
from senlinclient.common import utils as senlin_utils
|
||||
|
||||
|
||||
class ListEvent(lister.Lister):
|
||||
class ListEvent(command.Lister):
|
||||
"""List events."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ListEvent")
|
||||
@@ -99,14 +97,13 @@ class ListEvent(lister.Lister):
|
||||
formatters['obj_id'] = lambda x: x[:8] if x else ''
|
||||
|
||||
events = senlin_client.events(**queries)
|
||||
return (
|
||||
columns,
|
||||
(utils.get_item_properties(e, columns, formatters=formatters)
|
||||
for e in events)
|
||||
)
|
||||
return (columns,
|
||||
(utils.get_item_properties(e.to_dict(), columns,
|
||||
formatters=formatters)
|
||||
for e in events))
|
||||
|
||||
|
||||
class ShowEvent(show.ShowOne):
|
||||
class ShowEvent(command.ShowOne):
|
||||
"""Describe the event."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ShowEvent")
|
||||
@@ -129,5 +126,6 @@ class ShowEvent(show.ShowOne):
|
||||
except sdk_exc.ResourceNotFound:
|
||||
raise exc.CommandError(_("Event not found: %s")
|
||||
% parsed_args.event)
|
||||
columns = sorted(list(six.iterkeys(event)))
|
||||
return columns, utils.get_dict_properties(event.to_dict(), columns)
|
||||
data = event.to_dict()
|
||||
columns = sorted(data.keys())
|
||||
return columns, utils.get_dict_properties(data, columns)
|
||||
|
||||
@@ -13,22 +13,20 @@
|
||||
"""Clustering v1 node action implementations"""
|
||||
|
||||
import logging
|
||||
import six
|
||||
import sys
|
||||
|
||||
from cliff import command
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from openstackclient.common import utils
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions as exc
|
||||
from osc_lib import utils
|
||||
import six
|
||||
|
||||
from senlinclient.common.i18n import _
|
||||
from senlinclient.common.i18n import _LI
|
||||
from senlinclient.common import utils as senlin_utils
|
||||
|
||||
|
||||
class ListNode(lister.Lister):
|
||||
class ListNode(command.Lister):
|
||||
"""Show list of nodes."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ListNode")
|
||||
@@ -118,7 +116,7 @@ class ListNode(lister.Lister):
|
||||
)
|
||||
|
||||
|
||||
class ShowNode(show.ShowOne):
|
||||
class ShowNode(command.ShowOne):
|
||||
"""Show detailed info about the specified node."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ShowNode")
|
||||
@@ -148,9 +146,8 @@ class ShowNode(show.ShowOne):
|
||||
def _show_node(senlin_client, node_id, show_details=False):
|
||||
"""Show detailed info about the specified node."""
|
||||
|
||||
args = {'show_details': True} if show_details else None
|
||||
try:
|
||||
node = senlin_client.get_node(node_id, args=args)
|
||||
node = senlin_client.get_node(node_id, details=show_details)
|
||||
except sdk_exc.ResourceNotFound:
|
||||
raise exc.CommandError(_('Node not found: %s') % node_id)
|
||||
|
||||
@@ -158,28 +155,22 @@ def _show_node(senlin_client, node_id, show_details=False):
|
||||
'metadata': senlin_utils.json_formatter,
|
||||
'data': senlin_utils.json_formatter,
|
||||
}
|
||||
if show_details and node:
|
||||
data = node.to_dict()
|
||||
if show_details and data['details']:
|
||||
formatters['details'] = senlin_utils.nested_dict_formatter(
|
||||
list(node['details'].keys()), ['property', 'value'])
|
||||
|
||||
columns = sorted(list(six.iterkeys(node)))
|
||||
return columns, utils.get_dict_properties(node.to_dict(), columns,
|
||||
list(data['details'].keys()), ['property', 'value'])
|
||||
columns = sorted(data.keys())
|
||||
return columns, utils.get_dict_properties(data, columns,
|
||||
formatters=formatters)
|
||||
|
||||
|
||||
class CreateNode(show.ShowOne):
|
||||
class CreateNode(command.ShowOne):
|
||||
"""Create the node."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".CreateNode")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateNode, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--profile',
|
||||
metavar='<profile>',
|
||||
required=True,
|
||||
help=_('Profile Id or Name used for this node')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--cluster',
|
||||
metavar='<cluster>',
|
||||
@@ -198,6 +189,12 @@ class CreateNode(show.ShowOne):
|
||||
'key-value pairs separated by a semicolon'),
|
||||
action='append'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--profile',
|
||||
metavar='<profile>',
|
||||
required=True,
|
||||
help=_('Profile Id or Name used for this node')
|
||||
)
|
||||
parser.add_argument(
|
||||
'name',
|
||||
metavar='<node-name>',
|
||||
@@ -221,7 +218,7 @@ class CreateNode(show.ShowOne):
|
||||
return _show_node(senlin_client, node.id)
|
||||
|
||||
|
||||
class UpdateNode(show.ShowOne):
|
||||
class UpdateNode(command.ShowOne):
|
||||
"""Update the node."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".UpdateNode")
|
||||
@@ -318,20 +315,16 @@ class DeleteNode(command.Command):
|
||||
self.log.info(_LI('Ctrl-d detected'))
|
||||
return
|
||||
|
||||
failure_count = 0
|
||||
|
||||
result = {}
|
||||
for nid in parsed_args.node:
|
||||
try:
|
||||
senlin_client.delete_node(nid, False)
|
||||
node = senlin_client.delete_node(nid, False)
|
||||
result[nid] = ('OK', node.location.split('/')[-1])
|
||||
except Exception as ex:
|
||||
failure_count += 1
|
||||
print(ex)
|
||||
if failure_count:
|
||||
raise exc.CommandError(_('Failed to delete %(count)s of the '
|
||||
'%(total)s specified node(s).') %
|
||||
{'count': failure_count,
|
||||
'total': len(parsed_args.node)})
|
||||
print('Request accepted')
|
||||
result[nid] = ('ERROR', six.text_type(ex))
|
||||
|
||||
for rid, res in result.items():
|
||||
senlin_utils.print_action_result(rid, res)
|
||||
|
||||
|
||||
class CheckNode(command.Command):
|
||||
|
||||
@@ -15,19 +15,17 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from cliff import command
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from openstackclient.common import utils
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions as exc
|
||||
from osc_lib import utils
|
||||
|
||||
from senlinclient.common.i18n import _
|
||||
from senlinclient.common.i18n import _LI
|
||||
from senlinclient.common import utils as senlin_utils
|
||||
|
||||
|
||||
class ListPolicy(lister.Lister):
|
||||
class ListPolicy(command.Lister):
|
||||
"""List policies that meet the criteria."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ListPolicy")
|
||||
@@ -105,7 +103,7 @@ class ListPolicy(lister.Lister):
|
||||
)
|
||||
|
||||
|
||||
class ShowPolicy(show.ShowOne):
|
||||
class ShowPolicy(command.ShowOne):
|
||||
"""Show the policy details."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ShowPolicy")
|
||||
@@ -151,7 +149,7 @@ def _show_policy(senlin_client, policy_id):
|
||||
formatters=formatters)
|
||||
|
||||
|
||||
class CreatePolicy(show.ShowOne):
|
||||
class CreatePolicy(command.ShowOne):
|
||||
"""Create a policy."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".CreatePolicy")
|
||||
@@ -185,7 +183,7 @@ class CreatePolicy(show.ShowOne):
|
||||
return _show_policy(senlin_client, policy.id)
|
||||
|
||||
|
||||
class UpdatePolicy(show.ShowOne):
|
||||
class UpdatePolicy(command.ShowOne):
|
||||
"""Update a policy."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".UpdatePolicy")
|
||||
@@ -271,3 +269,47 @@ class DeletePolicy(command.Command):
|
||||
{'count': failure_count,
|
||||
'total': len(parsed_args.policy)})
|
||||
print('Policy deleted: %s' % parsed_args.policy)
|
||||
|
||||
|
||||
class ValidatePolicy(command.ShowOne):
|
||||
"""Validate a policy."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ValidatePolicy")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ValidatePolicy, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--spec-file',
|
||||
metavar='<spec-file>',
|
||||
required=True,
|
||||
help=_('The spec file used to create the policy')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
|
||||
senlin_client = self.app.client_manager.clustering
|
||||
spec = senlin_utils.get_spec_content(parsed_args.spec_file)
|
||||
attrs = {
|
||||
'spec': spec,
|
||||
}
|
||||
|
||||
policy = senlin_client.validate_policy(**attrs)
|
||||
formatters = {
|
||||
'spec': senlin_utils.json_formatter
|
||||
}
|
||||
columns = [
|
||||
'created_at',
|
||||
'data',
|
||||
'domain',
|
||||
'id',
|
||||
'name',
|
||||
'project',
|
||||
'spec',
|
||||
'type',
|
||||
'updated_at',
|
||||
'user'
|
||||
]
|
||||
return columns, utils.get_dict_properties(policy.to_dict(), columns,
|
||||
formatters=formatters)
|
||||
|
||||
@@ -13,16 +13,16 @@
|
||||
"""Clustering v1 policy type action implementations"""
|
||||
|
||||
import logging
|
||||
import six
|
||||
|
||||
from cliff import lister
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions as exc
|
||||
|
||||
from senlinclient.common import format_utils
|
||||
from senlinclient.common.i18n import _
|
||||
|
||||
|
||||
class PolicyTypeList(lister.Lister):
|
||||
class PolicyTypeList(command.Lister):
|
||||
"""List the available policy types."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".PolicyTypeList")
|
||||
@@ -63,6 +63,7 @@ class PolicyTypeShow(format_utils.YamlFormat):
|
||||
except sdk_exc.ResourceNotFound:
|
||||
raise exc.CommandError(_('Policy Type not found: %s')
|
||||
% parsed_args.type_name)
|
||||
rows = list(six.itervalues(res))
|
||||
columns = list(six.iterkeys(res))
|
||||
data = res.to_dict()
|
||||
rows = data.values()
|
||||
columns = data.keys()
|
||||
return columns, rows
|
||||
|
||||
@@ -15,19 +15,17 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from cliff import command
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from openstackclient.common import utils
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions as exc
|
||||
from osc_lib import utils
|
||||
|
||||
from senlinclient.common.i18n import _
|
||||
from senlinclient.common.i18n import _LI
|
||||
from senlinclient.common import utils as senlin_utils
|
||||
|
||||
|
||||
class ShowProfile(show.ShowOne):
|
||||
class ShowProfile(command.ShowOne):
|
||||
"""Show profile details."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ShowProfile")
|
||||
@@ -76,7 +74,7 @@ def _show_profile(senlin_client, profile_id):
|
||||
formatters=formatters)
|
||||
|
||||
|
||||
class ListProfile(lister.Lister):
|
||||
class ListProfile(command.Lister):
|
||||
"""List profiles that meet the criteria."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ListProfile")
|
||||
@@ -207,19 +205,13 @@ class DeleteProfile(command.Command):
|
||||
print('Profile deleted: %s' % parsed_args.profile)
|
||||
|
||||
|
||||
class CreateProfile(show.ShowOne):
|
||||
class CreateProfile(command.ShowOne):
|
||||
"""Create a profile."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".CreateProfile")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateProfile, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--spec-file',
|
||||
metavar='<spec-file>',
|
||||
required=True,
|
||||
help=_('The spec file used to create the profile')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--metadata',
|
||||
metavar='<key1=value1;key2=value2...>',
|
||||
@@ -228,6 +220,12 @@ class CreateProfile(show.ShowOne):
|
||||
'key-value pairs separated by a semicolon'),
|
||||
action='append'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--spec-file',
|
||||
metavar='<spec-file>',
|
||||
required=True,
|
||||
help=_('The spec file used to create the profile')
|
||||
)
|
||||
parser.add_argument(
|
||||
'name',
|
||||
metavar='<profile-name>',
|
||||
@@ -264,7 +262,7 @@ class CreateProfile(show.ShowOne):
|
||||
return _show_profile(senlin_client, profile_id=profile.id)
|
||||
|
||||
|
||||
class UpdateProfile(show.ShowOne):
|
||||
class UpdateProfile(command.ShowOne):
|
||||
"""Update a profile."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".UpdateProfile")
|
||||
@@ -309,3 +307,65 @@ class UpdateProfile(show.ShowOne):
|
||||
parsed_args.profile)
|
||||
senlin_client.update_profile(profile.id, **params)
|
||||
return _show_profile(senlin_client, profile_id=profile.id)
|
||||
|
||||
|
||||
class ValidateProfile(command.ShowOne):
|
||||
"""Validate a profile."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ValidateProfile")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ValidateProfile, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--spec-file',
|
||||
metavar='<spec-file>',
|
||||
required=True,
|
||||
help=_('The spec file used to create the profile')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
senlin_client = self.app.client_manager.clustering
|
||||
|
||||
spec = senlin_utils.get_spec_content(parsed_args.spec_file)
|
||||
type_name = spec.get('type', None)
|
||||
type_version = spec.get('version', None)
|
||||
properties = spec.get('properties', None)
|
||||
if type_name is None:
|
||||
raise exc.CommandError(_("Missing 'type' key in spec file."))
|
||||
if type_version is None:
|
||||
raise exc.CommandError(_("Missing 'version' key in spec file."))
|
||||
if properties is None:
|
||||
raise exc.CommandError(_("Missing 'properties' key in spec file."))
|
||||
|
||||
if type_name == 'os.heat.stack':
|
||||
stack_properties = senlin_utils.process_stack_spec(properties)
|
||||
spec['properties'] = stack_properties
|
||||
|
||||
params = {
|
||||
'spec': spec,
|
||||
}
|
||||
|
||||
profile = senlin_client.validate_profile(**params)
|
||||
|
||||
formatters = {}
|
||||
formatters['metadata'] = senlin_utils.json_formatter
|
||||
formatters['spec'] = senlin_utils.nested_dict_formatter(
|
||||
['type', 'version', 'properties'],
|
||||
['property', 'value'])
|
||||
|
||||
columns = [
|
||||
'created_at',
|
||||
'domain',
|
||||
'id',
|
||||
'metadata',
|
||||
'name',
|
||||
'project',
|
||||
'spec',
|
||||
'type',
|
||||
'updated_at',
|
||||
'user'
|
||||
]
|
||||
return columns, utils.get_dict_properties(profile.to_dict(), columns,
|
||||
formatters=formatters)
|
||||
|
||||
@@ -13,16 +13,15 @@
|
||||
"""Clustering v1 profile type action implementations"""
|
||||
|
||||
import logging
|
||||
import six
|
||||
|
||||
from cliff import lister
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions as exc
|
||||
from senlinclient.common import format_utils
|
||||
from senlinclient.common.i18n import _
|
||||
|
||||
|
||||
class ProfileTypeList(lister.Lister):
|
||||
class ProfileTypeList(command.Lister):
|
||||
"""List the available profile types."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ProfileTypeList")
|
||||
@@ -63,6 +62,7 @@ class ProfileTypeShow(format_utils.YamlFormat):
|
||||
except sdk_exc.ResourceNotFound:
|
||||
raise exc.CommandError(_('Profile Type not found: %s')
|
||||
% parsed_args.type_name)
|
||||
rows = list(six.itervalues(res))
|
||||
columns = list(six.iterkeys(res))
|
||||
data = res.to_dict()
|
||||
rows = data.values()
|
||||
columns = data.keys()
|
||||
return columns, rows
|
||||
|
||||
@@ -13,22 +13,19 @@
|
||||
"""Clustering v1 receiver action implementations"""
|
||||
|
||||
import logging
|
||||
import six
|
||||
import sys
|
||||
|
||||
from cliff import command
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
from openstack import exceptions as sdk_exc
|
||||
from openstackclient.common import exceptions as exc
|
||||
from openstackclient.common import utils
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions as exc
|
||||
from osc_lib import utils
|
||||
|
||||
from senlinclient.common.i18n import _
|
||||
from senlinclient.common.i18n import _LI
|
||||
from senlinclient.common import utils as senlin_utils
|
||||
|
||||
|
||||
class ListReceiver(lister.Lister):
|
||||
class ListReceiver(command.Lister):
|
||||
"""List receivers that meet the criteria."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ListReceiver")
|
||||
@@ -110,7 +107,7 @@ class ListReceiver(lister.Lister):
|
||||
)
|
||||
|
||||
|
||||
class ShowReceiver(show.ShowOne):
|
||||
class ShowReceiver(command.ShowOne):
|
||||
"""Show the receiver details."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ShowReceiver")
|
||||
@@ -142,12 +139,13 @@ def _show_receiver(senlin_client, receiver_id):
|
||||
'params': senlin_utils.json_formatter,
|
||||
'channel': senlin_utils.json_formatter,
|
||||
}
|
||||
columns = sorted(list(six.iterkeys(receiver)))
|
||||
return columns, utils.get_dict_properties(receiver.to_dict(), columns,
|
||||
data = receiver.to_dict()
|
||||
columns = sorted(data.keys())
|
||||
return columns, utils.get_dict_properties(data, columns,
|
||||
formatters=formatters)
|
||||
|
||||
|
||||
class CreateReceiver(show.ShowOne):
|
||||
class CreateReceiver(command.ShowOne):
|
||||
"""Create a receiver."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".CreateReceiver")
|
||||
@@ -160,18 +158,6 @@ class CreateReceiver(show.ShowOne):
|
||||
default='webhook',
|
||||
help=_('Type of the receiver to create')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--cluster',
|
||||
metavar='<cluster>',
|
||||
required=True,
|
||||
help=_('Targeted cluster for this receiver')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--action',
|
||||
metavar='<action>',
|
||||
required=True,
|
||||
help=_('Name or ID of the targeted action to be triggered')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--params',
|
||||
metavar='<key1=value1;key2=value2...>',
|
||||
@@ -179,6 +165,18 @@ class CreateReceiver(show.ShowOne):
|
||||
'action when the receiver is triggered'),
|
||||
action='append'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--cluster',
|
||||
metavar='<cluster>',
|
||||
help=_('Targeted cluster for this receiver. Required if '
|
||||
'receiver type is webhook')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--action',
|
||||
metavar='<action>',
|
||||
help=_('Name or ID of the targeted action to be triggered. '
|
||||
'Required if receiver type is webhook')
|
||||
)
|
||||
parser.add_argument(
|
||||
'name',
|
||||
metavar='<name>',
|
||||
@@ -188,6 +186,11 @@ class CreateReceiver(show.ShowOne):
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
if parsed_args.type == 'webhook':
|
||||
if (not parsed_args.cluster or not parsed_args.action):
|
||||
msg = _('cluster and action parameters are required to create '
|
||||
'webhook type of receiver.')
|
||||
raise exc.CommandError(msg)
|
||||
|
||||
senlin_client = self.app.client_manager.clustering
|
||||
params = {
|
||||
|
||||
@@ -11,8 +11,13 @@
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
|
||||
from openstack import exceptions as sdk_exc
|
||||
import six
|
||||
|
||||
from senlinclient.common import exc
|
||||
from senlinclient.common.i18n import _
|
||||
from senlinclient.common.i18n import _LW
|
||||
@@ -22,11 +27,10 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def show_deprecated(deprecated, recommended):
|
||||
logger.warning(_LW('"%(old)s" is deprecated, '
|
||||
'please use "%(new)s" instead.'),
|
||||
{'old': deprecated,
|
||||
'new': recommended}
|
||||
)
|
||||
logger.warning(
|
||||
_LW('"%(old)s" is deprecated and will be removed by Apr 2017, '
|
||||
'please use "%(new)s" instead.'),
|
||||
{'old': deprecated, 'new': recommended})
|
||||
|
||||
|
||||
def do_build_info(service, args=None):
|
||||
@@ -36,7 +40,7 @@ def do_build_info(service, args=None):
|
||||
:param args: Additional command line arguments, if any.
|
||||
"""
|
||||
show_deprecated('senlin build-info', 'openstack cluster build info')
|
||||
result = service.get_build_info()
|
||||
result = service.get_build_info().to_dict()
|
||||
|
||||
formatters = {
|
||||
'api': utils.json_formatter,
|
||||
@@ -241,6 +245,44 @@ def do_profile_delete(service, args):
|
||||
print('Profile deleted: %s' % args.id)
|
||||
|
||||
|
||||
@utils.arg('-s', '--spec-file', metavar='<SPEC FILE>', required=True,
|
||||
help=_('The spec file used to create the profile.'))
|
||||
def do_profile_validate(service, args):
|
||||
"""Validate a profile."""
|
||||
show_deprecated('senlin profile-validate',
|
||||
'openstack cluster profile validate')
|
||||
spec = utils.get_spec_content(args.spec_file)
|
||||
type_name = spec.get('type', None)
|
||||
type_version = spec.get('version', None)
|
||||
properties = spec.get('properties', None)
|
||||
if type_name is None:
|
||||
raise exc.CommandError(_("Missing 'type' key in spec file."))
|
||||
if type_version is None:
|
||||
raise exc.CommandError(_("Missing 'version' key in spec file."))
|
||||
if properties is None:
|
||||
raise exc.CommandError(_("Missing 'properties' key in spec file."))
|
||||
|
||||
if type_name == 'os.heat.stack':
|
||||
stack_properties = utils.process_stack_spec(properties)
|
||||
spec['properties'] = stack_properties
|
||||
|
||||
params = {
|
||||
'spec': spec,
|
||||
}
|
||||
|
||||
profile = service.validate_profile(**params)
|
||||
|
||||
formatters = {
|
||||
'metadata': utils.json_formatter,
|
||||
}
|
||||
|
||||
formatters['spec'] = utils.nested_dict_formatter(
|
||||
['type', 'version', 'properties'],
|
||||
['property', 'value'])
|
||||
|
||||
utils.print_dict(profile.to_dict(), formatters=formatters)
|
||||
|
||||
|
||||
# POLICY TYPES
|
||||
|
||||
|
||||
@@ -393,6 +435,24 @@ def do_policy_delete(service, args):
|
||||
print('Policy deleted: %s' % args.id)
|
||||
|
||||
|
||||
@utils.arg('-s', '--spec-file', metavar='<SPEC_FILE>', required=True,
|
||||
help=_('The spec file used to create the policy.'))
|
||||
def do_policy_validate(service, args):
|
||||
"""VAlidate a policy spec."""
|
||||
show_deprecated('senlin policy-validate',
|
||||
'openstack cluster policy validate')
|
||||
spec = utils.get_spec_content(args.spec_file)
|
||||
attrs = {
|
||||
'spec': spec,
|
||||
}
|
||||
|
||||
policy = service.validate_policy(**attrs)
|
||||
formatters = {
|
||||
'metadata': utils.json_formatter,
|
||||
'spec': utils.json_formatter,
|
||||
}
|
||||
utils.print_dict(policy.to_dict(), formatters=formatters)
|
||||
|
||||
# CLUSTERS
|
||||
|
||||
|
||||
@@ -449,7 +509,7 @@ def _show_cluster(service, cluster_id):
|
||||
|
||||
formatters = {
|
||||
'metadata': utils.json_formatter,
|
||||
'nodes': utils.list_formatter,
|
||||
'node_ids': utils.list_formatter,
|
||||
}
|
||||
utils.print_dict(cluster.to_dict(), formatters=formatters)
|
||||
|
||||
@@ -491,23 +551,201 @@ def do_cluster_create(service, args):
|
||||
_show_cluster(service, cluster.id)
|
||||
|
||||
|
||||
@utils.arg('-p', '--path', metavar='<PATH>',
|
||||
help=_('A Json path string specifying the attribute to collect.'))
|
||||
@utils.arg('-L', '--list', default=False, action="store_true",
|
||||
help=_('Print a full list that contains both node ids and '
|
||||
'attribute values instead of values only. Default is True.'))
|
||||
@utils.arg('-F', '--full-id', default=False, action="store_true",
|
||||
help=_('Print full IDs in list.'))
|
||||
@utils.arg('id', metavar='<CLUSTER>',
|
||||
help=_('Name or ID of cluster(s) to operate on.'))
|
||||
def do_cluster_collect(service, args):
|
||||
"""Collect attributes across a cluster."""
|
||||
show_deprecated('senlin cluster-collect', 'openstack cluster collect')
|
||||
|
||||
attrs = service.collect_cluster_attrs(args.id, args.path)
|
||||
if args.list:
|
||||
fields = ['node_id', 'attr_value']
|
||||
formatters = {
|
||||
'attr_value': utils.json_formatter
|
||||
}
|
||||
if not args.full_id:
|
||||
formatters['node_id'] = lambda x: x.node_id[:8]
|
||||
utils.print_list(attrs, fields, formatters=formatters)
|
||||
else:
|
||||
for attr in attrs:
|
||||
print(attr.attr_value)
|
||||
|
||||
|
||||
@utils.arg('id', metavar='<CLUSTER>', nargs='+',
|
||||
help=_('Name or ID of cluster(s) to delete.'))
|
||||
def do_cluster_delete(service, args):
|
||||
"""Delete the cluster(s)."""
|
||||
show_deprecated('senlin cluster-delete', 'openstack cluster delete')
|
||||
failure_count = 0
|
||||
|
||||
result = {}
|
||||
for cid in args.id:
|
||||
try:
|
||||
service.delete_cluster(cid, False)
|
||||
cluster = service.delete_cluster(cid, False)
|
||||
result[cid] = ('OK', cluster.location.split('/')[-1])
|
||||
except Exception as ex:
|
||||
failure_count += 1
|
||||
print(ex)
|
||||
if failure_count > 0:
|
||||
msg = _('Failed to delete some of the specified clusters.')
|
||||
raise exc.CommandError(msg)
|
||||
print('Request accepted')
|
||||
result[cid] = ('ERROR', six.text_type(ex))
|
||||
|
||||
for rid, res in result.items():
|
||||
utils.print_action_result(rid, res)
|
||||
|
||||
|
||||
def _run_script(node_id, addr, net, addr_type, port, user, ipv6, identity_file,
|
||||
script, options, output=None):
|
||||
version = 6 if ipv6 else 4
|
||||
|
||||
# Select the network to use.
|
||||
if net:
|
||||
addresses = addr.get(net)
|
||||
if not addresses:
|
||||
output['status'] = _('FAILED')
|
||||
output['reason'] = _("Node '%(node)s' is not attached to network "
|
||||
"'%(net)s'.") % {'node': node_id, 'net': net}
|
||||
return
|
||||
else:
|
||||
# network not specified
|
||||
if len(addr) > 1:
|
||||
output['status'] = _('FAILED')
|
||||
output['reason'] = _("Node '%(node)s' is attached to more than "
|
||||
"one network. Please pick the network to "
|
||||
"use.") % {'node': node_id}
|
||||
return
|
||||
elif not addr:
|
||||
output['status'] = _('FAILED')
|
||||
output['reason'] = _("Node '%(node)s' is not attached to any "
|
||||
"network.") % {'node': node_id}
|
||||
return
|
||||
else:
|
||||
addresses = list(six.itervalues(addr))[0]
|
||||
|
||||
# Select the address in the selected network.
|
||||
# If the extension is not present, we assume the address to be floating.
|
||||
matching_addresses = []
|
||||
for a in addresses:
|
||||
a_type = a.get('OS-EXT-IPS:type', 'floating')
|
||||
a_version = a.get('version')
|
||||
if (a_version == version and a_type == addr_type):
|
||||
matching_addresses.append(a.get('addr'))
|
||||
|
||||
if not matching_addresses:
|
||||
output['status'] = _('FAILED')
|
||||
output['reason'] = _("No address that would match network '%(net)s' "
|
||||
"and type '%(type)s' of IPv%(ver)s has been "
|
||||
"found for node '%(node)s'."
|
||||
) % {'net': net, 'type': addr_type,
|
||||
'ver': version, 'node': node_id}
|
||||
return
|
||||
|
||||
if len(matching_addresses) > 1:
|
||||
output['status'] = _('FAILED')
|
||||
output['reason'] = _("More than one IPv%(ver)s %(type)s address "
|
||||
"found.") % {'ver': version, 'type': addr_type}
|
||||
return
|
||||
|
||||
ip_address = str(matching_addresses[0])
|
||||
identity = '-i %s' % identity_file if identity_file else ''
|
||||
|
||||
cmd = [
|
||||
'ssh',
|
||||
'-%d' % version,
|
||||
'-p%d' % port,
|
||||
identity,
|
||||
options,
|
||||
'%s@%s' % (user, ip_address),
|
||||
'%s' % script
|
||||
]
|
||||
logger.debug("%s" % cmd)
|
||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||
(stdout, stderr) = proc.communicate()
|
||||
while proc.returncode is None:
|
||||
time.sleep(1)
|
||||
if proc.returncode == 0:
|
||||
output['status'] = _('SUCCEEDED (0)')
|
||||
output['output'] = stdout
|
||||
if stderr:
|
||||
output['error'] = stderr
|
||||
else:
|
||||
output['status'] = _('FAILED (%d)') % proc.returncode
|
||||
output['output'] = stdout
|
||||
if stderr:
|
||||
output['error'] = stderr
|
||||
|
||||
|
||||
@utils.arg("-p", "--port", metavar="<PORT>", type=int, default=22,
|
||||
help=_("Optional flag to indicate the port to use (Default=22)."))
|
||||
@utils.arg("-t", "--address-type", type=str, default="floating",
|
||||
help=_("Optional flag to indicate which IP type to use. Possible "
|
||||
"values includes 'fixed' and 'floating' (the Default)."))
|
||||
@utils.arg("-n", "--network", metavar='<NETWORK>', default='',
|
||||
help=_('Network to use for the ssh.'))
|
||||
@utils.arg("-6", "--ipv6", action="store_true", default=False,
|
||||
help=_("Optional flag to indicate whether to use an IPv6 address "
|
||||
"attached to a server. (Defaults to IPv4 address)"))
|
||||
@utils.arg("-u", "--user", metavar="<USER>", default="root",
|
||||
help=_("Login to use."))
|
||||
@utils.arg("-i", "--identity-file",
|
||||
help=_("Private key file, same as the '-i' option to the ssh "
|
||||
"command."))
|
||||
@utils.arg("-O", "--ssh-options", default="",
|
||||
help=_("Extra options to pass to ssh. see: man ssh."))
|
||||
@utils.arg("-s", "--script", metavar="<FILE>", required=True,
|
||||
help=_("Script file to run."))
|
||||
@utils.arg("id", metavar="<CLUSTER>",
|
||||
help=_('Name or ID of the cluster.'))
|
||||
def do_cluster_run(service, args):
|
||||
"""Run shell scripts on all nodes of a cluster."""
|
||||
if '@' in args.id:
|
||||
user, cluster = args.id.split('@', 1)
|
||||
args.user = user
|
||||
args.cluster = cluster
|
||||
|
||||
try:
|
||||
attributes = service.collect_cluster_attrs(args.id, 'details')
|
||||
except sdk_exc.ResourceNotFound:
|
||||
raise exc.CommandError(_("Cluster not found: %s") % args.id)
|
||||
|
||||
script = None
|
||||
try:
|
||||
f = open(args.script, 'r')
|
||||
script = f.read()
|
||||
except Exception:
|
||||
raise exc.CommandError(_("Cound not open script file: %s") %
|
||||
args.script)
|
||||
|
||||
tasks = dict()
|
||||
for attr in attributes:
|
||||
node_id = attr.node_id
|
||||
addr = attr.attr_value['addresses']
|
||||
|
||||
output = dict()
|
||||
th = threading.Thread(
|
||||
target=_run_script,
|
||||
args=(node_id, addr, args.network, args.address_type, args.port,
|
||||
args.user, args.ipv6, args.identity_file,
|
||||
script, args.ssh_options),
|
||||
kwargs={'output': output})
|
||||
th.start()
|
||||
tasks[th] = (node_id, output)
|
||||
|
||||
for t in tasks:
|
||||
t.join()
|
||||
|
||||
for t in tasks:
|
||||
node_id, result = tasks[t]
|
||||
print("node: %s" % node_id)
|
||||
print("status: %s" % result.get('status'))
|
||||
if "reason" in result:
|
||||
print("reason: %s" % result.get('reason'))
|
||||
if "output" in result:
|
||||
print("output:\n%s" % result.get('output'))
|
||||
if "error" in result:
|
||||
print("error:\n%s" % result.get('error'))
|
||||
|
||||
|
||||
@utils.arg('-p', '--profile', metavar='<PROFILE>',
|
||||
@@ -624,7 +862,7 @@ def do_cluster_node_del(service, args):
|
||||
@utils.arg('-t', '--min-step', metavar='<MIN_STEP>', type=int,
|
||||
help=_('An integer specifying the number of nodes for adjustment '
|
||||
'when <PERCENTAGE> is specified.'))
|
||||
@utils.arg('-s', '--strict', action='store_true', default=False,
|
||||
@utils.arg('-s', '--strict', action='store_true', default=False,
|
||||
help=_('A boolean specifying whether the resize should be '
|
||||
'performed on a best-effort basis when the new capacity '
|
||||
'may go beyond size constraints.'))
|
||||
@@ -659,7 +897,7 @@ def do_cluster_resize(service, args):
|
||||
if capacity is not None:
|
||||
if capacity < 0:
|
||||
raise exc.CommandError(_('Cluster capacity must be larger than '
|
||||
' or equal to zero.'))
|
||||
'or equal to zero.'))
|
||||
action_args['adjustment_type'] = 'EXACT_CAPACITY'
|
||||
action_args['number'] = capacity
|
||||
|
||||
@@ -745,7 +983,7 @@ def do_cluster_policy_list(service, args):
|
||||
"""List policies from cluster."""
|
||||
show_deprecated('senlin cluster-policy-list',
|
||||
'openstack cluster policy binding list')
|
||||
fields = ['policy_id', 'policy_name', 'policy_type', 'enabled']
|
||||
fields = ['policy_id', 'policy_name', 'policy_type', 'is_enabled']
|
||||
|
||||
cluster = service.get_cluster(args.id)
|
||||
queries = {
|
||||
@@ -910,9 +1148,8 @@ def do_node_list(service, args):
|
||||
|
||||
def _show_node(service, node_id, show_details=False):
|
||||
"""Show detailed info about the specified node."""
|
||||
args = {'show_details': True} if show_details else None
|
||||
try:
|
||||
node = service.get_node(node_id, args=args)
|
||||
node = service.get_node(node_id, details=show_details)
|
||||
except sdk_exc.ResourceNotFound:
|
||||
raise exc.CommandError(_('Node not found: %s') % node_id)
|
||||
|
||||
@@ -921,9 +1158,9 @@ def _show_node(service, node_id, show_details=False):
|
||||
'data': utils.json_formatter,
|
||||
}
|
||||
data = node.to_dict()
|
||||
if show_details:
|
||||
if show_details and data['details']:
|
||||
formatters['details'] = utils.nested_dict_formatter(
|
||||
list(node['details'].keys()), ['property', 'value'])
|
||||
list(data['details'].keys()), ['property', 'value'])
|
||||
|
||||
utils.print_dict(data, formatters=formatters)
|
||||
|
||||
@@ -971,18 +1208,17 @@ def do_node_show(service, args):
|
||||
def do_node_delete(service, args):
|
||||
"""Delete the node(s)."""
|
||||
show_deprecated('senlin node-delete', 'openstack cluster node delete')
|
||||
failure_count = 0
|
||||
|
||||
result = {}
|
||||
for nid in args.id:
|
||||
try:
|
||||
service.delete_node(nid, False)
|
||||
node = service.delete_node(nid, False)
|
||||
result[nid] = ('OK', node.location.split('/')[-1])
|
||||
except Exception as ex:
|
||||
failure_count += 1
|
||||
print(ex)
|
||||
if failure_count > 0:
|
||||
msg = _('Failed to delete some of the specified nodes.')
|
||||
raise exc.CommandError(msg)
|
||||
print('Request accepted')
|
||||
result[nid] = ('ERROR', six.text_type(ex))
|
||||
|
||||
for rid, res in result.items():
|
||||
utils.print_action_result(rid, res)
|
||||
|
||||
|
||||
@utils.arg('-n', '--name', metavar='<NAME>',
|
||||
@@ -1130,10 +1366,12 @@ def do_receiver_show(service, args):
|
||||
|
||||
@utils.arg('-t', '--type', metavar='<TYPE>', default='webhook',
|
||||
help=_('Type of the receiver to create.'))
|
||||
@utils.arg('-c', '--cluster', metavar='<CLUSTER>', required=True,
|
||||
help=_('Targeted cluster for this receiver.'))
|
||||
@utils.arg('-a', '--action', metavar='<ACTION>', required=True,
|
||||
help=_('Name or ID of the targeted action to be triggered.'))
|
||||
@utils.arg('-c', '--cluster', metavar='<CLUSTER>',
|
||||
help=_('Targeted cluster for this receiver. Required if receiver '
|
||||
'type is webhook.'))
|
||||
@utils.arg('-a', '--action', metavar='<ACTION>',
|
||||
help=_('Name or ID of the targeted action to be triggered. '
|
||||
'Required if receiver type is webhook.'))
|
||||
@utils.arg('-P', '--params', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
|
||||
help=_('A dictionary of parameters that will be passed to target '
|
||||
'action when the receiver is triggered.'),
|
||||
@@ -1145,6 +1383,12 @@ def do_receiver_create(service, args):
|
||||
show_deprecated('senlin receiver-create',
|
||||
'openstack cluster receiver create')
|
||||
|
||||
if args.type == 'webhook':
|
||||
if (not args.cluster or not args.action):
|
||||
msg = _('cluster and action parameters are required to create '
|
||||
'webhook type of receiver.')
|
||||
raise exc.CommandError(msg)
|
||||
|
||||
params = {
|
||||
'name': args.name,
|
||||
'type': args.type,
|
||||
@@ -1203,7 +1447,7 @@ def do_event_list(service, args):
|
||||
"""List events."""
|
||||
show_deprecated('senlin event-list', 'openstack cluster event list')
|
||||
fields = ['id', 'timestamp', 'obj_type', 'obj_id', 'obj_name', 'action',
|
||||
'status', 'status_reason', 'level']
|
||||
'status', 'level', 'cluster_id']
|
||||
queries = {
|
||||
'sort': args.sort,
|
||||
'limit': args.limit,
|
||||
@@ -1218,6 +1462,8 @@ def do_event_list(service, args):
|
||||
if not args.full_id:
|
||||
formatters['id'] = lambda x: x.id[:8]
|
||||
formatters['obj_id'] = lambda x: x.obj_id[:8] if x.obj_id else ''
|
||||
formatters['cluster_id'] = (lambda x: x.cluster_id[:8]
|
||||
if x.cluster_id else '')
|
||||
|
||||
events = service.events(**queries)
|
||||
utils.print_list(events, fields, formatters=formatters)
|
||||
@@ -1282,7 +1528,7 @@ def do_action_list(service, args):
|
||||
formatters['depended_by'] = f_depby
|
||||
else:
|
||||
formatters['id'] = lambda x: x.id[:8]
|
||||
formatters['target'] = lambda x: x.target[:8]
|
||||
formatters['target'] = lambda x: x.target_id[:8]
|
||||
f_depon = lambda x: '\n'.join(a[:8] for a in x.depends_on)
|
||||
f_depby = lambda x: '\n'.join(a[:8] for a in x.depended_by)
|
||||
formatters['depends_on'] = f_depon
|
||||
|
||||
@@ -17,6 +17,7 @@ classifier =
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.4
|
||||
Programming Language :: Python :: 3.5
|
||||
|
||||
[files]
|
||||
packages =
|
||||
@@ -58,6 +59,7 @@ openstack.clustering.v1 =
|
||||
cluster_policy_detach = senlinclient.v1.cluster:ClusterPolicyDetach
|
||||
cluster_policy_list = senlinclient.v1.policy:ListPolicy
|
||||
cluster_policy_show = senlinclient.v1.policy:ShowPolicy
|
||||
cluster_policy_validate = senlinclient.v1.policy:ValidatePolicy
|
||||
cluster_policy_type_list = senlinclient.v1.policy_type:PolicyTypeList
|
||||
cluster_policy_type_show = senlinclient.v1.policy_type:PolicyTypeShow
|
||||
cluster_policy_update = senlinclient.v1.policy:UpdatePolicy
|
||||
@@ -68,6 +70,7 @@ openstack.clustering.v1 =
|
||||
cluster_profile_type_list = senlinclient.v1.profile_type:ProfileTypeList
|
||||
cluster_profile_type_show = senlinclient.v1.profile_type:ProfileTypeShow
|
||||
cluster_profile_update = senlinclient.v1.profile:UpdateProfile
|
||||
cluster_profile_validate = senlinclient.v1.profile:ValidateProfile
|
||||
cluster_receiver_create = senlinclient.v1.receiver:CreateReceiver
|
||||
cluster_receiver_delete = senlinclient.v1.receiver:DeleteReceiver
|
||||
cluster_receiver_list = senlinclient.v1.receiver:ListReceiver
|
||||
@@ -78,6 +81,8 @@ openstack.clustering.v1 =
|
||||
cluster_expand = senlinclient.v1.cluster:ScaleOutCluster
|
||||
cluster_show = senlinclient.v1.cluster:ShowCluster
|
||||
cluster_update = senlinclient.v1.cluster:UpdateCluster
|
||||
cluster_collect = senlinclient.v1.cluster:ClusterCollect
|
||||
cluster_run = senlinclient.v1.cluster:ClusterRun
|
||||
|
||||
[global]
|
||||
setup-hooks =
|
||||
|
||||
@@ -5,15 +5,13 @@
|
||||
# Hacking already pins down pep8, pyflakes and flake8
|
||||
hacking<0.11,>=0.10.0
|
||||
coverage>=3.6 # Apache-2.0
|
||||
discover # BSD
|
||||
fixtures>=3.0.0 # Apache-2.0/BSD
|
||||
requests-mock>=0.7.0 # Apache-2.0
|
||||
requests-mock>=1.0 # Apache-2.0
|
||||
mock>=2.0 # BSD
|
||||
mox3>=0.7.0 # Apache-2.0
|
||||
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
|
||||
oslotest>=1.10.0 # Apache-2.0
|
||||
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD
|
||||
sphinx!=1.3b1,<1.3,>=1.2.1 # BSD
|
||||
testrepository>=0.0.18 # Apache-2.0/BSD
|
||||
testscenarios>=0.4 # Apache-2.0/BSD
|
||||
testtools>=1.4.0 # MIT
|
||||
reno>=1.6.2 # Apache2
|
||||
reno>=1.8.0 # Apache2
|
||||
|
||||
7
tox.ini
7
tox.ini
@@ -1,5 +1,5 @@
|
||||
[tox]
|
||||
envlist = py34,py27,pypy,pep8,releasenotes
|
||||
envlist = py35,py34,py27,pypy,pep8,releasenotes
|
||||
minversion = 1.6
|
||||
skipsdist = True
|
||||
|
||||
@@ -18,7 +18,7 @@ whitelist_externals = find
|
||||
commands =
|
||||
flake8
|
||||
# Check that .po and .pot files are valid:
|
||||
bash -c "find senlinclient -type f -regex '.*\.pot?' -print0|xargs -0 -n 1 msgfmt --check-format -o /dev/null"
|
||||
bash -c "find senlinclient -type f -regex '.*\.pot?' -print0|xargs -0 -n 1 --no-run-if-empty msgfmt --check-format -o /dev/null"
|
||||
whitelist_externals = bash
|
||||
|
||||
[testenv:venv]
|
||||
@@ -35,8 +35,9 @@ commands=
|
||||
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
|
||||
|
||||
[flake8]
|
||||
ignore = D100,D101,D102,D103,D104,D105,D200,D201,D202,D204,D205,D300,D301,D400,D401
|
||||
show-source = True
|
||||
exclude=.venv,.git,.tox,dist,*openstack/common*,*lib/python*,*egg,build
|
||||
exclude=.venv,.git,.tox,dist,*lib/python*,*egg,build
|
||||
max-complexity=20
|
||||
|
||||
[hacking]
|
||||
|
||||
Reference in New Issue
Block a user