Add command to create, show, delete podmanager

This commit adds cli commands to create, show, delete podmanagers
openstack valence podmanager create --help
openstack valence podmanager show --help
openstack valence podmanager delete --help

Example:

Create Podmanager request -
openstack valence podmanager create <name> <url> --driver <driver>
--auth type=<type> --auth username=<username> --auth password=<password>

Show Podmanager -
openstack valence podmanager show <podm-id>

Delete Podmanager -
openstack valence podmanager delete <podm-id1>, <podm-id2>

Change-Id: Id5262d42232d592ea4de3de20ad88ee5c6aa03b9
Partially-Implements blueprint valenceclient
This commit is contained in:
Anusha Ramineni 2017-08-25 10:58:50 +05:30
parent 7f84237263
commit f16f4a40c2
10 changed files with 267 additions and 20 deletions

View File

@ -6,3 +6,4 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0
oslo.utils>=3.20.0 # Apache-2.0
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0
oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0
osc-lib>=1.7.0 # Apache-2.0

View File

@ -29,6 +29,9 @@ openstack.cli.extension =
openstack.valence.v1 =
valence_podmanager_list = valenceclient.osc.v1.podmanager:ListPodManagers
valence_podmanager_create = valenceclient.osc.v1.podmanager:CreatePodManager
valence_podmanager_delete = valenceclient.osc.v1.podmanager:DeletePodManagers
valence_podmanager_show = valenceclient.osc.v1.podmanager:ShowPodManager
[build_sphinx]
source-dir = doc/source

View File

@ -15,6 +15,7 @@ testtools>=1.4.0 # MIT
oslo.utils>=3.20.0 # Apache-2.0
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0
oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0
requests-mock>=1.1.0 # Apache-2.0
# releasenotes
reno!=2.3.1,>=1.8.0 # Apache-2.0

View File

@ -14,7 +14,6 @@
# under the License.
import copy
import functools
import logging
import requests
@ -125,23 +124,24 @@ class HTTPClient(object):
def _make_connection_url(self, url):
return urlparse.urljoin(self.valence_url, url)
def _http_request(self, url, method, **kwargs):
def _http_request(self, conn_url, method, **kwargs):
"""Send an http request with the specified characteristics
Wrapper around request.Session.request to handle tasks such as
setting headers and error handling.
"""
kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {}))
kwargs['headers'] = kwargs.get('headers', {})
kwargs['headers'].setdefault('User-agent', USER_AGENT)
self.log_curl_request(method, url, kwargs)
self.log_curl_request(method, conn_url, kwargs)
body = kwargs.pop('body', None)
if body:
kwargs['data'] = body
conn_url = self._make_connection_url(url)
headers = kwargs.pop('headers')
conn_url = self._make_connection_url(conn_url)
try:
resp = self.session.request(method, conn_url, **kwargs)
resp = self.session.request(method, conn_url, headers=headers,
data=body, json=kwargs)
except requests.exceptions.RequestException as e:
msg = (_("Error has occured while handling request for "
"%(url)s: %(e)s") % dict(url=conn_url, e=e))
@ -151,25 +151,25 @@ class HTTPClient(object):
self.log_http_response(resp, resp.text)
body_iter = six.StringIO(resp.text)
if resp.status_code >= http_client.BAD_REQUEST:
error_json = _extract_error_json(resp.text)
raise exc.from_response(resp, error_json.get('faultstring'),
error_json.get('debugfino'), method, url)
error_json.get('debugfino'), method,
conn_url)
elif resp.status_code in (http_client.FOUND,
http_client.USE_PROXY):
return self._http_request(resp['location'], method, **kwargs)
return resp, body_iter
def json_request(self, method, url, **kwargs):
def json_request(self, method, conn_url, **kwargs):
kwargs.setdefault('headers', {})
kwargs['headers'].setdefault('Content-Type', 'application/json')
kwargs['headers'].setdefault('Accept', 'application/json')
if 'body' in kwargs:
kwargs['body'] = jsonutils.dump_as_bytes(kwargs['body'])
resp, body_iter = self._http_request(url, method, **kwargs)
resp, body_iter = self._http_request(conn_url, method, **kwargs)
content_type = resp.headers.get('Content-Type')
if(resp.status_code in (http_client.NO_CONTENT,
http_client.RESET_CONTENT)

View File

@ -1,4 +1,4 @@
# Copyright 2017 Intel, Inc.
# Copyright 2017 NEC, Corp.
#
# 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
@ -13,7 +13,9 @@
# under the License.
#
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@ -32,3 +34,107 @@ class ListPodManagers(command.Lister):
columns = ['uuid', 'name', 'url', 'driver', 'status', 'created_at',
'updated_at']
return (columns, (utils.get_dict_properties(s, columns) for s in obj))
class CreatePodManager(command.ShowOne):
_description = "Creates new podmanager"
auth_required = False
def get_parser(self, prog_name):
parser = super(CreatePodManager, self).get_parser(prog_name)
parser.add_argument(
'name',
metavar='<name>',
help=('Name for the PodManager'))
parser.add_argument(
'url',
metavar='<url>',
help=('URL of the PodManager'))
parser.add_argument(
'--driver',
metavar='<driver>',
default='redfishv1',
help=("PodManager driver, default is 'redfishv1'"))
parser.add_argument(
'--auth',
metavar='<key=value>',
required=True,
action=parseractions.KeyValueAction,
help=("auth information to connect to podmanager, repeat option "
"to set each key. Accepted keys are type, username, password"
"If type not specified 'basic' is taken by default"))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
auth = parsed_args.auth
auth['type'] = auth.get('type', 'basic')
req = {
'name': parsed_args.name,
'url': parsed_args.url,
'driver': parsed_args.driver,
'authentication': [{'type': auth['type'],
'auth_items': {
'username': auth['username'],
'password': auth['password']}}]
}
client = self.app.client_manager.valence
obj = client.create_podmanager(req)
columns = ('uuid', 'name', 'url', 'driver', 'status', 'created_at',
'updated_at')
return (columns, (utils.get_dict_properties(obj, columns)))
class DeletePodManagers(command.Command):
_description = "Delete podmanagers"
auth_required = False
def get_parser(self, prog_name):
parser = super(DeletePodManagers, self).get_parser(prog_name)
parser.add_argument(
'id',
nargs='+',
metavar='<id>',
help=('Podmanagers id(s) to delete'))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
client = self.app.client_manager.valence
count = 0
for p in parsed_args.id:
try:
client.delete_podmanager(p)
except Exception as e:
count = count + 1
self.log.error("podmanager %s deletion failed with error %s",
p, str(e))
if count > 0:
total = len(parsed_args.id)
msg = (("%(result)s of %(total)s podmanagers(s) "
"failed to delete.") % {'result': count, 'total': total})
raise exceptions.CommandError(msg)
class ShowPodManager(command.ShowOne):
_description = "Show podmanager"
auth_required = False
def get_parser(self, prog_name):
parser = super(ShowPodManager, self).get_parser(prog_name)
parser.add_argument(
'id',
metavar='<id>',
help=('Podmanager id to show'))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
client = self.app.client_manager.valence
obj = client.show_podmanager(parsed_args.id)
columns = ('uuid', 'name', 'url', 'driver', 'status', 'created_at',
'updated_at')
return (columns, (utils.get_dict_properties(obj, columns)))

View File

@ -114,12 +114,12 @@ class HttpClientTest(utils.BaseTestCase):
client.json_request('GET', 'redfish/v1')
def test_server_http_not_valide_request(self):
def test_server_http_not_valid_request(self):
kwargs = {"valence_url": "http://localhost/"}
client = http.HTTPClient(**kwargs)
client.session.request = mock.Mock(
side_effect=http.requests.exceptions.InvalidSchema)
self.assertRaises(exc.ValidationError, client._http_request, 'GET',
self.assertRaises(exc.ValidationError, client.json_request, 'GET',
'http://localhost/')
@mock.patch.object(http.LOG, 'debug', autospec=True)
@ -149,7 +149,7 @@ class HttpClientTest(utils.BaseTestCase):
with mock.patch.object(client, 'session',
autospec=True) as mock_session:
mock_session.request.side_effect = iter([resp])
response, body_iter = client._http_request('/redfish/v1/Nodes',
'GET')
response, body_iter = client.json_request('GET',
'/redfish/v1/Nodes')
self.assertEqual(http_client.OK, response.status_code)

View File

View File

@ -0,0 +1,28 @@
# 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 argparse
import mock
from osc_lib.tests import utils
class TestValenceClient(utils.TestCommand):
def setUp(self):
super(TestValenceClient, self).setUp()
self.namespace = argparse.Namespace()
self.app.client_manager.session = mock.Mock()
self.app.client_manager.valence = mock.Mock()
self.valenceclient = self.app.client_manager.valence
self.addCleanup(mock.patch.stopall)

View File

@ -0,0 +1,92 @@
# Copyright 2017 NEC, Corp.
# 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 valenceclient.osc.v1 import podmanager
from valenceclient.tests.unit.osc import test_base
class TestCreatePodmanager(test_base.TestValenceClient):
def test_create_podmanager(self):
response = {"uuid": "test-id",
"name": "test-podm",
"url": "http://localhost",
"driver": "redfishv1",
"status": "Offline",
"created_at": "2017-08-07 06:56:34 UTC",
"updated_at": "2017-08-28 06:56:34 UTC"}
arglist = ['test-podm', 'http://localhost',
'--auth', "username=user",
'--auth', "password=pass"]
verifylist = [
('name', 'test-podm'),
('url', 'http://localhost'),
('auth', {'username': 'user', 'password': 'pass'}),
]
mocker = mock.Mock(return_value=response)
self.valenceclient.create_podmanager = mocker
cmd = podmanager.CreatePodManager(self.app, self.namespace)
parsed_args = self.check_parser(cmd, arglist, verifylist)
result = list(cmd.take_action(parsed_args))
filtered = [('uuid', 'name', 'url', 'driver', 'status', 'created_at',
'updated_at'),
('test-id', 'test-podm', 'http://localhost', 'redfishv1',
'Offline', '2017-08-07 06:56:34 UTC',
'2017-08-28 06:56:34 UTC')]
self.assertEqual(filtered, result)
class TestShowPolicy(test_base.TestValenceClient):
def test_show_policy(self):
podmanager_id = "test-id"
arglist = [podmanager_id]
verifylist = [
('id', podmanager_id),
]
response = {"uuid": "test-id",
"name": "test-podm",
"url": "http://localhost",
"driver": "redfishv1",
"status": "Offline",
"created_at": "2017-08-07 06:56:34 UTC",
"updated_at": "2017-08-28 06:56:34 UTC"}
mocker = mock.Mock(return_value=response)
self.valenceclient.show_podmanager = mocker
cmd = podmanager.ShowPodManager(self.app, self.namespace)
parsed_args = self.check_parser(cmd, arglist, verifylist)
result = list(cmd.take_action(parsed_args))
filtered = [('uuid', 'name', 'url', 'driver', 'status', 'created_at',
'updated_at'),
('test-id', 'test-podm', 'http://localhost', 'redfishv1',
'Offline', '2017-08-07 06:56:34 UTC',
'2017-08-28 06:56:34 UTC')]
self.assertEqual(filtered, result)
class TestDeletePodmanager(test_base.TestValenceClient):
def test_delete_podmanager(self):
arglist = ['test-id']
verifylist = [('id', ['test-id']), ]
mocker = mock.Mock(return_value=None)
self.valenceclient.delete_podmanager = mocker
cmd = podmanager.DeletePodManagers(self.app, self.namespace)
parsed_args = self.check_parser(cmd, arglist, verifylist)
result = cmd.take_action(parsed_args)
mocker.assert_called_with('test-id')
self.assertIsNone(result)

View File

@ -18,11 +18,27 @@ from valenceclient.common import http
class Client(object):
podmanager_path = "/v1/pod_managers"
podmanagers = "/v1/pod_managers"
podmanager_path = "/v1/pod_managers/%s"
def __init__(self, **kwargs):
self.http_client = http.HTTPClient(**kwargs)
def list_podmanagers(self):
resp, body = self.http_client.json_request('get', self.podmanager_path)
resp, body = self.http_client.json_request('get', self.podmanagers)
return body
def create_podmanager(self, request):
resp, body = self.http_client.json_request('post', self.podmanagers,
**request)
return body
def delete_podmanager(self, id):
resp, body = self.http_client.json_request('delete',
self.podmanager_path % id)
return body
def show_podmanager(self, id):
resp, body = self.http_client.json_request('get',
self.podmanager_path % id)
return body