Add OpenstackClient plugin for cluster profile show

This change implements the "openstack cluster profile show" command
  Based on the existing senlin command: senlin profile-show

Change-Id: Ib4658b6fea9c097ecd043c394522dac88b97fb94
Blueprint: senlin-support-python-openstackclient
This commit is contained in:
dixiaoli 2016-02-01 00:01:14 +08:00
parent 466f4b2e73
commit 993081ab10
11 changed files with 428 additions and 0 deletions

View File

@ -4,12 +4,14 @@
Babel>=1.3 # BSD
pbr>=1.6 # Apache-2.0
cliff!=1.16.0,>=1.15.0 # Apache-2.0
PrettyTable<0.8,>=0.7 # BSD
openstacksdk # Apache-2.0
oslo.i18n>=2.1.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
oslo.utils>=3.4.0 # Apache-2.0
python-heatclient>=0.6.0 # Apache-2.0
python-openstackclient>=2.0.0 # Apache-2.0
PyYAML>=3.1.0 # MIT
requests!=2.9.0,>=2.8.1 # Apache-2.0
six>=1.9.0 # MIT

View File

View File

@ -0,0 +1,46 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from openstack import connection
from openstackclient.common import utils
LOG = logging.getLogger(__name__)
DEFAULT_CLUSTERING_API_VERSION = '1'
API_VERSION_OPTION = 'os_clustering_api_version'
API_NAME = 'clustering'
def make_client(instance):
"""Returns a clustering proxy"""
conn = connection.Connection(authenticator=instance.session.auth)
LOG.debug('Connection: %s', conn)
LOG.debug('Clustering client initialized using OpenStackSDK: %s',
conn.cluster)
return conn.cluster
def build_option_parser(parser):
"""Hook to add global options"""
parser.add_argument(
'--os-clustering-api-version',
metavar='<clustering-api-version>',
default=utils.env(
'OS_CLUSTERING_API_VERSION',
default=DEFAULT_CLUSTERING_API_VERSION),
help='Clustering API version, default=' +
DEFAULT_CLUSTERING_API_VERSION +
' (Env: OS_CLUSTERING_API_VERSION)')
return parser

View File

View File

@ -0,0 +1,72 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Clustering v1 profile action implementations"""
import logging
from cliff import show
from openstack import exceptions as sdk_exc
from openstackclient.common import exceptions as exc
from openstackclient.common import utils
from senlinclient.common import utils as senlin_utils
class ShowProfile(show.ShowOne):
"""Show profile details."""
log = logging.getLogger(__name__ + ".ShowProfile")
def get_parser(self, prog_name):
parser = super(ShowProfile, self).get_parser(prog_name)
parser.add_argument(
'profile',
metavar='<PROFILE>',
help='Name or ID of profile to show',
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
senlin_client = self.app.client_manager.clustering
return _show_profile(senlin_client, profile_id=parsed_args.profile)
def _show_profile(senlin_client, profile_id):
try:
data = senlin_client.get_profile(profile_id)
except sdk_exc.ResourceNotFound:
raise exc.CommandError('Profile not found: %s' % profile_id)
else:
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',
'permission',
'project',
'spec',
'type',
'updated_at',
'user'
]
return columns, utils.get_dict_properties(data.to_dict(), columns,
formatters=formatters)

View File

View File

@ -0,0 +1,160 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import requests
import six
import sys
AUTH_TOKEN = "foobar"
AUTH_URL = "http://0.0.0.0"
USERNAME = "itchy"
PASSWORD = "scratchy"
TEST_RESPONSE_DICT_V3 = {
"token": {
"audit_ids": [
"a"
],
"catalog": [
],
"expires_at": "2034-09-29T18:27:15.978064Z",
"extras": {},
"issued_at": "2014-09-29T17:27:15.978097Z",
"methods": [
"password"
],
"project": {
"domain": {
"id": "default",
"name": "Default"
},
"id": "bbb",
"name": "project"
},
"roles": [
],
"user": {
"domain": {
"id": "default",
"name": "Default"
},
"id": "aaa",
"name": USERNAME
}
}
}
TEST_VERSIONS = {
"versions": {
"values": [
{
"id": "v3.0",
"links": [
{
"href": AUTH_URL,
"rel": "self"
}
],
"media-types": [
{
"base": "application/json",
"type": "application/vnd.openstack.identity-v3+json"
},
{
"base": "application/xml",
"type": "application/vnd.openstack.identity-v3+xml"
}
],
"status": "stable",
"updated": "2013-03-06T00:00:00Z"
}
]
}
}
class FakeStdout(object):
def __init__(self):
self.content = []
def write(self, text):
self.content.append(text)
def make_string(self):
result = ''
for line in self.content:
result = result + line
return result
class FakeApp(object):
def __init__(self, _stdout):
self.stdout = _stdout
self.client_manager = None
self.stdin = sys.stdin
self.stdout = _stdout or sys.stdout
self.stderr = sys.stderr
class FakeClient(object):
def __init__(self, **kwargs):
self.auth_url = kwargs['auth_url']
self.token = kwargs['token']
class FakeClientManager(object):
def __init__(self):
self.compute = None
self.identity = None
self.image = None
self.object_store = None
self.volume = None
self.network = None
self.session = None
self.auth_ref = None
class FakeModule(object):
def __init__(self, name, version):
self.name = name
self.__version__ = version
class FakeResource(object):
def __init__(self, manager, info, loaded=False):
self.manager = manager
self._info = info
self._add_details(info)
self._loaded = loaded
def _add_details(self, info):
for (k, v) in six.iteritems(info):
setattr(self, k, v)
def __repr__(self):
reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and
k != 'manager')
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
return "<%s %s>" % (self.__class__.__name__, info)
class FakeResponse(requests.Response):
def __init__(self, headers={}, status_code=200, data=None, encoding=None):
super(FakeResponse, self).__init__()
self.status_code = status_code
self.headers.update(headers)
self._content = json.dumps(data)
if not isinstance(self._content, six.binary_type):
self._content = self._content.encode()

View File

@ -0,0 +1,33 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from openstackclient.tests import utils
from senlinclient.tests.unit.osc import fakes
class FakeClusteringv1Client(object):
def __init__(self, **kwargs):
self.http_client = mock.Mock()
self.http_client.auth_token = kwargs['token']
self.profiles = fakes.FakeResource(None, {})
class TestClusteringv1(utils.TestCommand):
def setUp(self):
super(TestClusteringv1, self).setUp()
self.app.client_manager.clustering = FakeClusteringv1Client(
token=fakes.AUTH_TOKEN,
auth_url=fakes.AUTH_URL
)

View File

@ -0,0 +1,109 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
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 senlinclient.osc.v1 import profile as osc_profile
from senlinclient.tests.unit.osc.v1 import fakes
class TestProfile(fakes.TestClusteringv1):
def setUp(self):
super(TestProfile, self).setUp()
self.mock_client = self.app.client_manager.clustering
class TestProfileShow(TestProfile):
get_response = {"profile": {
"created_at": "2015-03-01T14:28:25",
"domain": 'false',
"id": "7fa885cd-fa39-4531-a42d-780af95c84a4",
"metadata": {},
"name": "test_prof1",
"project": "42d9e9663331431f97b75e25136307ff",
"spec": {
"disable_rollback": 'false',
"environment": {
"resource_registry": {
"os.heat.server": "OS::Heat::Server"
}
},
"files": {
"file:///opt/stack/senlin/examples/profiles/test_script.sh":
"#!/bin/bash\n\necho \"this is a test script file\"\n"
},
"parameters": {},
"template": {
"heat_template_version": "2014-10-16",
"outputs": {
"result": {
"value": {
"get_attr": [
"random",
"value"
]
}
}
},
"parameters": {
"file": {
"default": {
"get_file": "file:///opt/stack/senlin/"
"examples/profiles/test_script.sh"
},
"type": "string"
}
},
"resources": {
"random": {
"properties": {
"length": 64
},
"type": "OS::Heat::RandomString"
}
},
"timeout": 60
},
"type": "os.heat.stack",
"version": "1.0"
},
"type": "os.heat.stack-1.0",
"updated_at": 'null',
"user": "5e5bf8027826429c96af157f68dc9072"
}}
def setUp(self):
super(TestProfileShow, self).setUp()
self.cmd = osc_profile.ShowProfile(self.app, None)
self.mock_client.get_profile = mock.Mock(
return_value=sdk_profile.Profile(None, self.get_response))
utils.get_dict_properties = mock.Mock(return_value='')
def test_profile_show(self):
arglist = ['my_profile']
parsed_args = self.check_parser(self.cmd, arglist, [])
self.cmd.take_action(parsed_args)
self.mock_client.get_profile.assert_called_with('my_profile')
def test_profile_show_not_found(self):
arglist = ['my_profile']
parsed_args = self.check_parser(self.cmd, arglist, [])
self.mock_client.get_profile.side_effect = sdk_exc.ResourceNotFound()
self.assertRaises(
exc.CommandError,
self.cmd.take_action,
parsed_args)

View File

@ -26,6 +26,12 @@ packages =
console_scripts =
senlin = senlinclient.shell:main
openstack.cli.extension =
clustering = senlinclient.osc.plugin
openstack.clustering.v1 =
cluster_profile_show = senlinclient.osc.v1.profile:ShowProfile
[global]
setup-hooks =
pbr.hooks.setup_hook