Add basic code structure of OSC plugin

This patch added the code of specific RSD plugin in OSC, which allow
user to issue node composition command through OSC, like
'openstack rsd compose
 --rsd-url "https://localhost:8442/redfish/v1/"
 --rsd-username "admin"
 --rsd-password "admin"
 --rsd-disable-verify
 --name "Fake-Name" '

Note: for node composition command, it only support "name" parameter
now. More functionalities will be implemented later.

Change-Id: Idaf48c8b2e5c9b370e69520578d91c8ddb30fd74
This commit is contained in:
Lin Yang 2017-07-31 20:47:18 -07:00
parent 8a9bd85c78
commit d043f3be0c
16 changed files with 278 additions and 29 deletions

View File

@ -11,7 +11,7 @@ Note that this is a hard requirement.
* Free software: Apache license
* Documentation: http://docs.openstack.org/developer/python-rsdclient
* Source: http://git.openstack.org/cgit/openstack/python-rsdclient
* Bugs: http://bugs.launchpad.net/https://launchpad.net/python-rsdclient
* Bugs: https://launchpad.net/python-rsdclient
Features
--------

View File

@ -3,3 +3,5 @@
# process, which may cause wedges in the gate later.
pbr>=2.0 # Apache-2.0
osc-lib>=1.7.0 # Apache-2.0
rsd-lib>=0.0.1 # Apache-2.0

View File

29
rsdclient/common/base.py Normal file
View File

@ -0,0 +1,29 @@
# Copyright 2017 99cloud, Inc.
# All Rights Reserved.
#
# 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.
"""
Base utilities to build API operation managers and objects on top of
"""
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class Manager(object):
"""Provides CRUD operations with a particular API."""
def __init__(self, client):
self.client = client

View File

79
rsdclient/osc/plugin.py Normal file
View File

@ -0,0 +1,79 @@
# Copyright 2017 Intel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""OpenStackClient plugin for RSD(Rack Scale Design)."""
import logging
from osc_lib import utils
LOG = logging.getLogger(__name__)
DEFAULT_API_VERSION = '1.2'
API_VERSION_OPTION = 'os_rsd_api_version'
API_NAME = 'rsd'
API_VERSIONS = {
'1.2': 'rsdclient.v1.client.Client',
}
def make_client(instance):
"""Returns a rsd client."""
rsd_client = utils.get_client_class(
API_NAME,
instance._api_version[API_NAME],
API_VERSIONS)
LOG.debug('Instantiating RSD client: %s', rsd_client)
client = rsd_client(base_url=instance._cli_options.rsd_url,
username=instance._cli_options.rsd_username,
password=instance._cli_options.rsd_password,
verify=instance._cli_options.rsd_disable_verify)
return client
def build_option_parser(parser):
"""Hook to add global options"""
parser.add_argument(
'--rsd-api-version',
metavar='<rsd-api-version>',
default=utils.env(
'RSD_API_VERSION',
default=DEFAULT_API_VERSION),
help='RSD API version, default=' +
DEFAULT_API_VERSION +
' (Env: RSD_API_VERSION)')
parser.add_argument(
'--rsd-url',
metavar='<rsd-url>',
default='https://localhost:8443/redfish/v1/',
help='The base URL to RSD pod manager')
parser.add_argument(
'--rsd-username',
metavar='<rsd-username>',
default='admin',
help='User account with admin access')
parser.add_argument(
'--rsd-password',
metavar='<rsd-password>',
default='admin',
help='User account password')
parser.add_argument(
'--rsd-disable-verify',
action='store_false',
help='If this is set, it will ignore verifying the SSL ' +
'certificate')
return parser

View File

41
rsdclient/osc/v1/node.py Normal file
View File

@ -0,0 +1,41 @@
# Copyright 2017 Intel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from osc_lib.command import command
class ComposeNode(command.Command):
_description = "Compose a Node"
def get_parser(self, prog_name):
parser = super(ComposeNode, self).get_parser(prog_name)
# NOTE: All arguments are positional and, if not provided
# with a default, required.
parser.add_argument('--name',
dest='name',
required=True,
metavar='<name>',
help='Name of the composed node.')
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
rsd_client = self.app.client_manager.rsd
args = {
'Name': parsed_args.name
}
rsd_client.node.compose(args)
print("Request to compose node %s was accepted"
% parsed_args.name)

View File

@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
# 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.
"""
test_rsdclient
----------------------------------
Tests for `rsdclient` module.
"""
from rsdclient.tests import base
class TestRsdclient(base.TestCase):
def test_something(self):
pass

View File

View File

@ -0,0 +1,32 @@
# Copyright 2017 Intel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import mock
import testtools
from rsdclient.v1 import client
class ClientInitializeTest(testtools.TestCase):
@mock.patch('rsd_lib.RSDLib')
def test_init_client(self, mock_rsdlib):
client.Client('fake_rsd_url', 'fake_username', 'fake_password')
mock_rsdlib.assert_called_once_with('fake_rsd_url', 'fake_username',
'fake_password', verify=True)
client.Client('fake_rsd_url', 'fake_username', 'fake_password',
verify=False)
mock_rsdlib.assert_called_with('fake_rsd_url', 'fake_username',
'fake_password', verify=False)

View File

@ -0,0 +1,35 @@
# Copyright 2017 Intel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import mock
import testtools
from rsdclient.v1 import node
class ClusterManagerTest(testtools.TestCase):
def setUp(self):
super(ClusterManagerTest, self).setUp()
self.client = mock.Mock()
self.mgr = node.NodeManager(self.client)
def test_compose(self):
mock_node_collection = mock.Mock()
self.client.get_node_collection.return_value = mock_node_collection
self.mgr.compose({'Name': 'fake_name'})
self.mgr.client.get_node_collection.assert_called_once()
mock_node_collection.compose_node.assert_called_once_with(
{'Name': 'fake_name'})

0
rsdclient/v1/__init__.py Normal file
View File

26
rsdclient/v1/client.py Normal file
View File

@ -0,0 +1,26 @@
# Copyright 2017 Intel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import rsd_lib
from rsdclient.v1 import node
class Client(object):
def __init__(self, base_url, username, password, verify=True):
self.client = rsd_lib.RSDLib(base_url, username, password,
verify=verify)
self.node = node.NodeManager(self.client)

26
rsdclient/v1/node.py Normal file
View File

@ -0,0 +1,26 @@
# Copyright 2017 Intel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from rsdclient.common import base
class NodeManager(base.Manager):
# resource_class = Node
_resource_name = 'nodes'
def compose(self, properites):
# TODO(lin.yang): should return id of new composed node, like
# 'redfish/v1/Nodes/1'
return self.client.get_node_collection().compose_node(properites)

View File

@ -23,6 +23,13 @@ classifier =
packages =
rsdclient
[entry_points]
openstack.cli.extension =
rsd = rsdclient.osc.plugin
openstack.rsd.v1 =
rsd_compose = rsdclient.osc.v1.node:ComposeNode
[build_sphinx]
all-files = 1
warning-is-error = 1