diff --git a/README.rst b/README.rst index d00df23..1d70739 100644 --- a/README.rst +++ b/README.rst @@ -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 -------- diff --git a/requirements.txt b/requirements.txt index 1d18dd3..51ad63d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 diff --git a/rsdclient/common/__init__.py b/rsdclient/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rsdclient/common/base.py b/rsdclient/common/base.py new file mode 100644 index 0000000..b33d651 --- /dev/null +++ b/rsdclient/common/base.py @@ -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 diff --git a/rsdclient/osc/__init__.py b/rsdclient/osc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rsdclient/osc/plugin.py b/rsdclient/osc/plugin.py new file mode 100644 index 0000000..dcc8856 --- /dev/null +++ b/rsdclient/osc/plugin.py @@ -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='', + 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='', + default='https://localhost:8443/redfish/v1/', + help='The base URL to RSD pod manager') + parser.add_argument( + '--rsd-username', + metavar='', + default='admin', + help='User account with admin access') + parser.add_argument( + '--rsd-password', + metavar='', + 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 diff --git a/rsdclient/osc/v1/__init__.py b/rsdclient/osc/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rsdclient/osc/v1/node.py b/rsdclient/osc/v1/node.py new file mode 100644 index 0000000..5077aba --- /dev/null +++ b/rsdclient/osc/v1/node.py @@ -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='', + 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) diff --git a/rsdclient/tests/test_rsdclient.py b/rsdclient/tests/test_rsdclient.py deleted file mode 100644 index 56b0454..0000000 --- a/rsdclient/tests/test_rsdclient.py +++ /dev/null @@ -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 diff --git a/rsdclient/tests/v1/__init__.py b/rsdclient/tests/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rsdclient/tests/v1/test_client.py b/rsdclient/tests/v1/test_client.py new file mode 100644 index 0000000..285d70c --- /dev/null +++ b/rsdclient/tests/v1/test_client.py @@ -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) diff --git a/rsdclient/tests/v1/test_node.py b/rsdclient/tests/v1/test_node.py new file mode 100644 index 0000000..d71077b --- /dev/null +++ b/rsdclient/tests/v1/test_node.py @@ -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'}) diff --git a/rsdclient/v1/__init__.py b/rsdclient/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rsdclient/v1/client.py b/rsdclient/v1/client.py new file mode 100644 index 0000000..09ec413 --- /dev/null +++ b/rsdclient/v1/client.py @@ -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) diff --git a/rsdclient/v1/node.py b/rsdclient/v1/node.py new file mode 100644 index 0000000..8412a9b --- /dev/null +++ b/rsdclient/v1/node.py @@ -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) diff --git a/setup.cfg b/setup.cfg index b2e3d42..c5abc33 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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