Retire cue
Cue has been retired in mid 2016 as official project and did not continue developement, it's time to retire it completely. Remove everything, update README. Depends-On: https://review.openstack.org/551202 Change-Id: I4092bb5feac88235eae52895a57ce3d01f347638
This commit is contained in:
parent
bb1f4adca0
commit
64dbf807ae
@ -1,7 +0,0 @@
|
||||
[run]
|
||||
branch = True
|
||||
source = cueclient
|
||||
omit = cueclient/tests/*
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
26
.gitignore
vendored
26
.gitignore
vendored
@ -1,26 +0,0 @@
|
||||
*.pyc
|
||||
*.dat
|
||||
TAGS
|
||||
*.egg
|
||||
*.egg-info
|
||||
build
|
||||
.coverage
|
||||
.tox
|
||||
cover
|
||||
venv
|
||||
.venv
|
||||
*.sublime-workspace
|
||||
*.sqlite
|
||||
var/*
|
||||
etc/*.conf
|
||||
etc/*.ini
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
doc/source/api/*
|
||||
doc/build/*
|
||||
dist
|
||||
cueclient/versioninfo
|
||||
.testrepository
|
||||
.idea/
|
||||
*.DS_Store
|
||||
.coverage.*
|
@ -1,4 +0,0 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/python-cueclient.git
|
@ -1,4 +0,0 @@
|
||||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${TESTS_DIR:-./cueclient/tests/} $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
176
LICENSE
176
LICENSE
@ -1,176 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
14
README.rst
14
README.rst
@ -0,0 +1,14 @@
|
||||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
(Optional:)
|
||||
For an alternative project, please see <alternative project name> at
|
||||
<alternative project URL>.
|
||||
|
||||
For any further questions, please email
|
||||
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||
Freenode.
|
@ -1,120 +0,0 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# 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 abc
|
||||
import json
|
||||
|
||||
import six
|
||||
from six.moves.urllib.parse import urlencode
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Controller(object):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
def build_url(self, url, marker=None, limit=None, params=None):
|
||||
params = params or {}
|
||||
|
||||
if marker is not None:
|
||||
params['marker'] = marker
|
||||
if limit is not None:
|
||||
params['limit'] = limit
|
||||
|
||||
q = urlencode(params) if params else ''
|
||||
return '%(url)s%(params)s' % {
|
||||
'url': url,
|
||||
'params': '?%s' % q
|
||||
}
|
||||
|
||||
def _serialize(self, kwargs):
|
||||
if 'data' in kwargs:
|
||||
kwargs['data'] = json.dumps(kwargs['data'])
|
||||
|
||||
def _post(self, url, response_key=None, **kwargs):
|
||||
self._serialize(kwargs)
|
||||
|
||||
resp = self.client.session.post(url, **kwargs)
|
||||
data = resp.json()
|
||||
|
||||
if response_key in data:
|
||||
return data[response_key]
|
||||
return data
|
||||
|
||||
def _get(self, url, response_key=None):
|
||||
resp = self.client.session.get(url)
|
||||
data = resp.json()
|
||||
|
||||
if response_key in data:
|
||||
return data[response_key]
|
||||
return data
|
||||
|
||||
def _patch(self, url, response_key=None, **kwargs):
|
||||
self._serialize(kwargs)
|
||||
|
||||
resp = self.client.session.patch(url, **kwargs)
|
||||
data = resp.json()
|
||||
|
||||
if response_key in data:
|
||||
return data[response_key]
|
||||
return data
|
||||
|
||||
def _put(self, url, response_key=None, **kwargs):
|
||||
self._serialize(kwargs)
|
||||
|
||||
resp = self.client.session.put(url, **kwargs)
|
||||
data = resp.json()
|
||||
|
||||
if response_key in data:
|
||||
return data[response_key]
|
||||
return data
|
||||
|
||||
def _delete(self, url):
|
||||
self.client.session.delete(url)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class CrudController(Controller):
|
||||
|
||||
@abc.abstractmethod
|
||||
def list(self, *args, **kw):
|
||||
"""
|
||||
List a resource
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get(self, *args, **kw):
|
||||
"""
|
||||
Get a resouce
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create(self, *args, **kw):
|
||||
"""
|
||||
Create a resource
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update(self, *args, **kw):
|
||||
"""
|
||||
Update a resource
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete(self, *args, **kw):
|
||||
"""
|
||||
Delete a resource
|
||||
"""
|
@ -1,49 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@hp.com>
|
||||
#
|
||||
# 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 cueclient import utils
|
||||
|
||||
from openstackclient.common import utils as oscutils
|
||||
|
||||
|
||||
DEFAULT_MB_API_VERSION = '1'
|
||||
|
||||
API_NAME = 'mb'
|
||||
API_VERSION_OPTION = 'os_mb_api_version'
|
||||
API_VERSIONS = {
|
||||
'1': 'cueclient.v1.client.Client',
|
||||
}
|
||||
|
||||
|
||||
def make_client(instance):
|
||||
cls = oscutils.get_client_class(
|
||||
API_NAME, instance._api_version[API_NAME],
|
||||
API_VERSIONS)
|
||||
return cls(session=instance.session, interface=instance._interface)
|
||||
|
||||
|
||||
def build_option_parser(parser):
|
||||
"""Hook to add global options."""
|
||||
parser.add_argument(
|
||||
'--os-mb-api-version',
|
||||
metavar='<mb-api-version>',
|
||||
default=utils.env(
|
||||
'OS_MB_API_VERSION',
|
||||
default=DEFAULT_MB_API_VERSION),
|
||||
help='MB API version, default=' +
|
||||
DEFAULT_MB_API_VERSION +
|
||||
' (Env: OS_MB_API_VERSION)')
|
||||
|
||||
return parser
|
@ -1,68 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema",
|
||||
"name": "Cluster",
|
||||
"title": "Cluster",
|
||||
"type" : "object",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Cluster Identifier",
|
||||
"pattern": "^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Cluster name",
|
||||
"required": true
|
||||
},
|
||||
"network_id": {
|
||||
"type": "array",
|
||||
"description": "Network Identifier",
|
||||
"required": true
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"description": "Cluster status"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"description": "Date and time of cluster creation",
|
||||
"format": "date-time"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"description": "Date and time of cluster update",
|
||||
"format": "date-time"
|
||||
},
|
||||
"endpoints": {
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"description": "Cluster endpoints"
|
||||
},
|
||||
"flavor": {
|
||||
"type": "string",
|
||||
"description": "Cluster flavor",
|
||||
"required": true
|
||||
},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"description": "Cluster size",
|
||||
"required": true
|
||||
},
|
||||
"volume_size": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"description": "Cluster volume"
|
||||
},
|
||||
"error_detail": {
|
||||
"type": "string",
|
||||
"description": "Cluster error description"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
# 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 oslo_serialization import jsonutils
|
||||
from requests_mock.contrib import fixture as requests_mock_fixture
|
||||
import six
|
||||
import testtools
|
||||
|
||||
from cueclient.tests.fixture_data import V1
|
||||
|
||||
|
||||
class TestCueBase(testtools.TestCase):
|
||||
def setUp(self):
|
||||
super(TestCueBase, self).setUp()
|
||||
self.requests = self.useFixture(requests_mock_fixture.Fixture())
|
||||
fix = V1(self.requests)
|
||||
client_fixture = self.useFixture(fix)
|
||||
cs = client_fixture.client
|
||||
|
||||
# Build up a fake app
|
||||
self.app = mock.Mock()
|
||||
self.app.client_manager = mock.Mock()
|
||||
self.app.client_manager.mb = cs
|
||||
|
||||
def check_parser(self, cmd, args, verify_args):
|
||||
"""Test for parsing arguments"""
|
||||
cmd_parser = cmd.get_parser('check_parser')
|
||||
parsed_args = cmd_parser.parse_args(args)
|
||||
|
||||
for av in verify_args:
|
||||
attr, value = av
|
||||
if attr:
|
||||
self.assertIn(attr, parsed_args)
|
||||
self.assertEqual(getattr(parsed_args, attr), value)
|
||||
return parsed_args
|
||||
|
||||
def execute(self, cmd_class, arglist, verifylist):
|
||||
cmd = cmd_class(self.app, argparse.Namespace())
|
||||
parsed_args = self.check_parser(cmd, arglist, verifylist)
|
||||
data = cmd.take_action(parsed_args)
|
||||
return data
|
||||
|
||||
def assert_called(self, method, path, body=None):
|
||||
self.assertEqual(self.requests.last_request.method, method)
|
||||
self.assertEqual(self.requests.last_request.path_url, path)
|
||||
|
||||
if body:
|
||||
req_data = self.requests.last_request.body
|
||||
if isinstance(req_data, six.binary_type):
|
||||
req_data = req_data.decode('utf-8')
|
||||
if not isinstance(body, six.string_types):
|
||||
# json load if the input body to match against is not a string
|
||||
req_data = jsonutils.loads(req_data)
|
||||
self.assertEqual(body, req_data)
|
@ -1,113 +0,0 @@
|
||||
# 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 fixtures
|
||||
from keystoneauth1 import session
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from cueclient.v1 import client
|
||||
|
||||
MESSAGE_BROKER_URL = 'http://message.broker'
|
||||
|
||||
|
||||
class V1(fixtures.Fixture):
|
||||
|
||||
base_url = 'clusters'
|
||||
json_headers = {'Content-Type': 'application/json'}
|
||||
|
||||
def __init__(self, requests):
|
||||
super(V1, self).__init__()
|
||||
self.client = None
|
||||
self.requests = requests
|
||||
|
||||
def setUp(self):
|
||||
super(V1, self).setUp()
|
||||
|
||||
self.cluster_1234 = {
|
||||
"name": "test-cluster",
|
||||
"id": "00000000-0000-0000-0000-000000001234",
|
||||
"size": 1,
|
||||
"network_id": ["05860da0-e2bd-4315-9cfb-7dd6e9963cd9"],
|
||||
"created_at": "2015-01-01T00:00:00+00:00",
|
||||
"endpoints": [],
|
||||
"flavor": "1",
|
||||
"status": "ACTIVE",
|
||||
}
|
||||
|
||||
self.cluster_5678 = {
|
||||
"name": "test-cluster2",
|
||||
"id": "00000000-0000-0000-0000-000000005678",
|
||||
"size": 3,
|
||||
"network_id": ["05567na0-f7aa-6820-7afcd-7dd6e9963cd9"],
|
||||
"created_at": "2015-01-01T00:00:00+00:00",
|
||||
"endpoints": [],
|
||||
"flavor": "1",
|
||||
"status": "BUILDING",
|
||||
}
|
||||
|
||||
self.cluster_error = {
|
||||
"name": "test-cluster3",
|
||||
"id": "00000000-0000-0000-0000-000000008765",
|
||||
"size": 3,
|
||||
"network_id": ["05567na0-f7aa-6820-7afcd-7dd6e9963cd9"],
|
||||
"created_at": "2015-01-01T00:00:00+00:00",
|
||||
"endpoints": [],
|
||||
"flavor": "1",
|
||||
"status": "ERROR",
|
||||
"error_detail": "cluster error detailed message",
|
||||
}
|
||||
|
||||
self.new_cluster = {
|
||||
"name": "new-test-cluster",
|
||||
"id": "00000000-0000-0000-0000-000000009012",
|
||||
"size": 3,
|
||||
"network_id": ["05567na0-f7aa-6820-7afcd-7dd6e9963cd9"],
|
||||
"created_at": "2015-01-01T00:00:00+00:00",
|
||||
"endpoints": [],
|
||||
"flavor": "1",
|
||||
"status": "BUILDING",
|
||||
}
|
||||
|
||||
clusters = [self.cluster_1234, self.cluster_5678, self.cluster_error]
|
||||
|
||||
self.requests.register_uri('GET', self.url(),
|
||||
json=clusters,
|
||||
headers=self.json_headers)
|
||||
|
||||
for cluster in clusters:
|
||||
self.requests.register_uri('GET', self.url(cluster['id']),
|
||||
json=cluster,
|
||||
headers=self.json_headers)
|
||||
|
||||
for cluster in clusters:
|
||||
self.requests.register_uri('DELETE', self.url(cluster['id']),
|
||||
status_code=202)
|
||||
|
||||
self.requests.register_uri('POST', self.url(),
|
||||
json=self.new_cluster,
|
||||
headers=self.json_headers)
|
||||
|
||||
self.client = client.Client(session=session.Session())
|
||||
self.client.session.endpoint_override = MESSAGE_BROKER_URL
|
||||
|
||||
def url(self, *args, **kwargs):
|
||||
url_args = [MESSAGE_BROKER_URL]
|
||||
|
||||
if self.base_url:
|
||||
url_args.append(self.base_url)
|
||||
|
||||
url = '/'.join(str(a).strip('/') for a in tuple(url_args) + args)
|
||||
|
||||
if kwargs:
|
||||
url += '?%s' % parse.urlencode(kwargs, doseq=True)
|
||||
|
||||
return url
|
@ -1,318 +0,0 @@
|
||||
# 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 cueclient.tests import base
|
||||
from cueclient.v1.cli import clusters
|
||||
|
||||
|
||||
class TestListClusters(base.TestCueBase):
|
||||
|
||||
def test_list_clusters(self):
|
||||
"""test cluster list."""
|
||||
arglist = []
|
||||
verifylist = []
|
||||
expected = {'00000000-0000-0000-0000-000000001234':
|
||||
('00000000-0000-0000-0000-000000001234',
|
||||
'test-cluster', 'ACTIVE', 1, []),
|
||||
'00000000-0000-0000-0000-000000005678':
|
||||
('00000000-0000-0000-0000-000000005678',
|
||||
'test-cluster2', 'BUILDING', 3, []),
|
||||
'00000000-0000-0000-0000-000000008765':
|
||||
('00000000-0000-0000-0000-000000008765',
|
||||
'test-cluster3', 'ERROR', 3, [])}
|
||||
|
||||
result = self.execute(clusters.ListClustersCommand, arglist,
|
||||
verifylist)
|
||||
self.assertEqual(['id', 'name', 'status', 'size', 'endpoints'],
|
||||
result[0])
|
||||
self.assert_called('GET', '/clusters')
|
||||
for cluster in result[1]:
|
||||
self.assertEqual(expected[cluster[0]], cluster)
|
||||
|
||||
|
||||
class TestShowCluster(base.TestCueBase):
|
||||
|
||||
def test_show_cluster(self):
|
||||
"""test cluster show."""
|
||||
cluster_id = '00000000-0000-0000-0000-000000001234'
|
||||
arglist = [cluster_id]
|
||||
verifylist = []
|
||||
|
||||
result = self.execute(clusters.ShowClusterCommand, arglist, verifylist)
|
||||
expected = [('created_at', 'endpoints', 'flavor', 'id', 'name',
|
||||
'network_id', 'size', 'status'),
|
||||
(u'2015-01-01T00:00:00+00:00', [], '1',
|
||||
'00000000-0000-0000-0000-000000001234',
|
||||
'test-cluster', [u'05860da0-e2bd-4315-9cfb-7dd6e9963cd9'],
|
||||
1, 'ACTIVE')]
|
||||
self.assert_called('GET', '/clusters/' + cluster_id)
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_show_cluster_error(self):
|
||||
"""test cluster show when in error state."""
|
||||
cluster_id = '00000000-0000-0000-0000-000000008765'
|
||||
arglist = [cluster_id]
|
||||
verifylist = []
|
||||
|
||||
result = self.execute(clusters.ShowClusterCommand, arglist, verifylist)
|
||||
expected = [('created_at', 'endpoints', 'error_detail', 'flavor', 'id',
|
||||
'name', 'network_id', 'size', 'status'),
|
||||
(u'2015-01-01T00:00:00+00:00', [],
|
||||
'cluster error detailed message', '1',
|
||||
'00000000-0000-0000-0000-000000008765',
|
||||
'test-cluster3',
|
||||
[u'05567na0-f7aa-6820-7afcd-7dd6e9963cd9'],
|
||||
3, 'ERROR')]
|
||||
self.assert_called('GET', '/clusters/' + cluster_id)
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_show_cluster_without_id(self):
|
||||
"""test show cluster without specifying cluster id"""
|
||||
|
||||
arglist = []
|
||||
verifylist = []
|
||||
|
||||
self.assertRaises(SystemExit, self.execute,
|
||||
clusters.ShowClusterCommand, arglist, verifylist)
|
||||
|
||||
|
||||
class TestCreateCluster(base.TestCueBase):
|
||||
|
||||
cluster_name = "test_Cluster"
|
||||
cluster_network_id = "9d6708ee-ea48-4e78-bef6-b50b48405091"
|
||||
cluster_flavor = "1"
|
||||
cluster_size = "2"
|
||||
auth = "type=plain,user=rabbitmq,pass=rabbit"
|
||||
|
||||
def test_create_cluster(self):
|
||||
arglist = ["--name", self.cluster_name,
|
||||
"--nic", self.cluster_network_id,
|
||||
"--flavor", self.cluster_flavor,
|
||||
"--size", self.cluster_size]
|
||||
verifylist = [
|
||||
('name', self.cluster_name),
|
||||
('nic', self.cluster_network_id),
|
||||
('flavor', self.cluster_flavor),
|
||||
('size', self.cluster_size)
|
||||
]
|
||||
|
||||
request_body = {'name': self.cluster_name,
|
||||
'network_id': [self.cluster_network_id],
|
||||
'flavor': self.cluster_flavor,
|
||||
'size': self.cluster_size,
|
||||
'volume_size': None,
|
||||
'authentication': {'type': 'plain',
|
||||
'token': {'username': None,
|
||||
'password': None}}}
|
||||
|
||||
self.execute(clusters.CreateClusterCommand, arglist, verifylist)
|
||||
self.assert_called('POST', '/clusters', request_body)
|
||||
|
||||
def test_create_cluster_rabbit_auth(self):
|
||||
arglist = ["--name", self.cluster_name,
|
||||
"--nic", self.cluster_network_id,
|
||||
"--flavor", self.cluster_flavor,
|
||||
"--size", self.cluster_size,
|
||||
"--auth", self.auth,
|
||||
]
|
||||
verifylist = [
|
||||
('name', self.cluster_name),
|
||||
('nic', self.cluster_network_id),
|
||||
('flavor', self.cluster_flavor),
|
||||
('size', self.cluster_size),
|
||||
('auth', self.auth),
|
||||
]
|
||||
|
||||
request_body = {'name': self.cluster_name,
|
||||
'network_id': [self.cluster_network_id],
|
||||
'flavor': self.cluster_flavor,
|
||||
'size': self.cluster_size,
|
||||
'volume_size': None,
|
||||
'authentication': {'type': 'plain',
|
||||
'token': {'username': 'rabbitmq',
|
||||
'password': 'rabbit'}}}
|
||||
|
||||
self.execute(clusters.CreateClusterCommand, arglist, verifylist)
|
||||
self.assert_called('POST', '/clusters', request_body)
|
||||
|
||||
def test_create_cluster_rabbit_auth_type_missing(self):
|
||||
|
||||
arglist = ["--name", self.cluster_name,
|
||||
"--nic", self.cluster_network_id,
|
||||
"--flavor", self.cluster_flavor,
|
||||
"--size", self.cluster_size,
|
||||
"--auth", "user=rabbitmq,pass=rabbit",
|
||||
]
|
||||
verifylist = [
|
||||
('name', self.cluster_name),
|
||||
('nic', self.cluster_network_id),
|
||||
('flavor', self.cluster_flavor),
|
||||
('size', self.cluster_size),
|
||||
('auth', "user=rabbitmq,pass=rabbit"),
|
||||
]
|
||||
|
||||
request_body = {'name': self.cluster_name,
|
||||
'network_id': [self.cluster_network_id],
|
||||
'flavor': self.cluster_flavor,
|
||||
'size': self.cluster_size,
|
||||
'volume_size': None,
|
||||
'authentication': {'type': 'plain',
|
||||
'token': {'username': 'rabbitmq',
|
||||
'password': 'rabbit'}}}
|
||||
|
||||
self.execute(clusters.CreateClusterCommand, arglist, verifylist)
|
||||
self.assert_called('POST', '/clusters', request_body)
|
||||
|
||||
def test_create_cluster_rabbit_auth_type_empty(self):
|
||||
|
||||
arglist = ["--name", self.cluster_name,
|
||||
"--nic", self.cluster_network_id,
|
||||
"--flavor", self.cluster_flavor,
|
||||
"--size", self.cluster_size,
|
||||
"--auth", "type=,user=rabbitmq,pass=rabbit",
|
||||
]
|
||||
verifylist = [
|
||||
('name', self.cluster_name),
|
||||
('nic', self.cluster_network_id),
|
||||
('flavor', self.cluster_flavor),
|
||||
('size', self.cluster_size),
|
||||
('auth', "type=,user=rabbitmq,pass=rabbit"),
|
||||
]
|
||||
|
||||
request_body = {'name': self.cluster_name,
|
||||
'network_id': [self.cluster_network_id],
|
||||
'flavor': self.cluster_flavor,
|
||||
'size': self.cluster_size,
|
||||
'volume_size': None,
|
||||
'authentication': {'type': 'plain',
|
||||
'token': {'username': 'rabbitmq',
|
||||
'password': 'rabbit'}}}
|
||||
|
||||
self.execute(clusters.CreateClusterCommand, arglist, verifylist)
|
||||
self.assert_called('POST', '/clusters', request_body)
|
||||
|
||||
def test_create_cluster_rabbit_auth_user_missing(self):
|
||||
|
||||
arglist = ["--name", self.cluster_name,
|
||||
"--nic", self.cluster_network_id,
|
||||
"--flavor", self.cluster_flavor,
|
||||
"--size", self.cluster_size,
|
||||
"--auth", "type=plain,pass=rabbit",
|
||||
]
|
||||
verifylist = [
|
||||
('name', self.cluster_name),
|
||||
('nic', self.cluster_network_id),
|
||||
('flavor', self.cluster_flavor),
|
||||
('size', self.cluster_size),
|
||||
('auth', "type=plain,pass=rabbit"),
|
||||
]
|
||||
|
||||
request_body = {'name': self.cluster_name,
|
||||
'network_id': [self.cluster_network_id],
|
||||
'flavor': self.cluster_flavor,
|
||||
'size': self.cluster_size,
|
||||
'volume_size': None,
|
||||
'authentication': {'type': 'plain',
|
||||
'token': {'username': None,
|
||||
'password': 'rabbit'}}}
|
||||
|
||||
self.execute(clusters.CreateClusterCommand, arglist, verifylist)
|
||||
self.assert_called('POST', '/clusters', request_body)
|
||||
|
||||
def test_create_cluster_rabbit_auth_password_missing(self):
|
||||
|
||||
arglist = ["--name", self.cluster_name,
|
||||
"--nic", self.cluster_network_id,
|
||||
"--flavor", self.cluster_flavor,
|
||||
"--size", self.cluster_size,
|
||||
"--auth", "type=plain,user=rabbitmq",
|
||||
]
|
||||
verifylist = [
|
||||
('name', self.cluster_name),
|
||||
('nic', self.cluster_network_id),
|
||||
('flavor', self.cluster_flavor),
|
||||
('size', self.cluster_size),
|
||||
('auth', "type=plain,user=rabbitmq"),
|
||||
]
|
||||
|
||||
request_body = {'name': self.cluster_name,
|
||||
'network_id': [self.cluster_network_id],
|
||||
'flavor': self.cluster_flavor,
|
||||
'size': self.cluster_size,
|
||||
'volume_size': None,
|
||||
'authentication': {'type': 'plain',
|
||||
'token': {'username': 'rabbitmq',
|
||||
'password': None}}}
|
||||
|
||||
self.execute(clusters.CreateClusterCommand, arglist, verifylist)
|
||||
self.assert_called('POST', '/clusters', request_body)
|
||||
|
||||
def test_create_cluster_without_name(self):
|
||||
"""test create cluster without 'name' argument."""
|
||||
|
||||
arglist = ["--nic", self.cluster_network_id,
|
||||
"--flavor", self.cluster_flavor,
|
||||
"--size", self.cluster_size]
|
||||
verifylist = []
|
||||
self.assertRaises(SystemExit, self.execute,
|
||||
clusters.CreateClusterCommand, arglist, verifylist)
|
||||
|
||||
def test_create_cluster_without_nic(self):
|
||||
"""test create cluster without 'network_id' argument."""
|
||||
|
||||
arglist = ["--name", self.cluster_name,
|
||||
"--flavor", self.cluster_flavor,
|
||||
"--size", self.cluster_size]
|
||||
verifylist = []
|
||||
self.assertRaises(SystemExit, self.execute,
|
||||
clusters.CreateClusterCommand, arglist, verifylist)
|
||||
|
||||
def test_create_cluster_without_flavor(self):
|
||||
"""test create cluster without 'flavor' argument."""
|
||||
|
||||
arglist = ["--name", self.cluster_name,
|
||||
"--nic", self.cluster_network_id,
|
||||
"--size", self.cluster_size]
|
||||
verifylist = []
|
||||
self.assertRaises(SystemExit, self.execute,
|
||||
clusters.CreateClusterCommand, arglist, verifylist)
|
||||
|
||||
def test_create_cluster_without_size(self):
|
||||
"""test create cluster without 'size' argument."""
|
||||
|
||||
arglist = ["--name", self.cluster_name,
|
||||
"--nic", self.cluster_network_id,
|
||||
"--flavor", self.cluster_flavor]
|
||||
verifylist = []
|
||||
self.assertRaises(SystemExit, self.execute,
|
||||
clusters.CreateClusterCommand, arglist, verifylist)
|
||||
|
||||
|
||||
class TestDeleteCluster(base.TestCueBase):
|
||||
|
||||
def test_delete_cluster_without_id(self):
|
||||
"""test delete cluster without giving cluster id"""
|
||||
arglist = []
|
||||
verifylist = []
|
||||
self.assertRaises(SystemExit, self.execute,
|
||||
clusters.DeleteClusterCommand, arglist, verifylist)
|
||||
|
||||
def test_delete_cluster(self):
|
||||
"""test delete cluster"""
|
||||
cluster_id = '00000000-0000-0000-0000-000000001234'
|
||||
arglist = [cluster_id]
|
||||
verifylist = []
|
||||
result = self.execute(clusters.DeleteClusterCommand, arglist,
|
||||
verifylist)
|
||||
self.assert_called('DELETE', '/clusters/' + cluster_id)
|
||||
self.assertIsNone(result)
|
@ -1,88 +0,0 @@
|
||||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2012-2013 OpenStack Foundation
|
||||
#
|
||||
# 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 os
|
||||
|
||||
import pkg_resources
|
||||
|
||||
|
||||
def env(*vars, **kwargs):
|
||||
"""Search for the first defined of possibly many env vars
|
||||
|
||||
Returns the first environment variable defined in vars, or
|
||||
returns the default defined in kwargs.
|
||||
|
||||
"""
|
||||
for v in vars:
|
||||
value = os.environ.get(v)
|
||||
if value:
|
||||
return value
|
||||
return kwargs.get('default', '')
|
||||
|
||||
|
||||
def get_item_properties(item, fields, mixed_case_fields=[], formatters={}):
|
||||
"""Return a tuple containing the item properties.
|
||||
|
||||
:param item: a single dict resource
|
||||
:param fields: tuple of strings with the desired field names
|
||||
:param mixed_case_fields: tuple of field names to preserve case
|
||||
:param formatters: dictionary mapping field names to callables
|
||||
to format the values
|
||||
"""
|
||||
row = []
|
||||
for field in fields:
|
||||
if field in mixed_case_fields:
|
||||
field_name = field.replace(' ', '_')
|
||||
else:
|
||||
field_name = field.lower().replace(' ', '_')
|
||||
data = item[field_name] if field_name in item else ''
|
||||
if field in formatters:
|
||||
row.append(formatters[field](data))
|
||||
else:
|
||||
row.append(data)
|
||||
return tuple(row)
|
||||
|
||||
|
||||
def resource_filename(*args, **kwargs):
|
||||
"""Return specified resource as a string"""
|
||||
if len(args) == 0:
|
||||
raise ValueError()
|
||||
|
||||
package = kwargs.pop('package', None)
|
||||
|
||||
if not package:
|
||||
package = 'cueclient'
|
||||
|
||||
resource_path = os.path.join('resources', *args)
|
||||
|
||||
if not pkg_resources.resource_exists(package, resource_path):
|
||||
# TODO(ap): add exceptions
|
||||
# raise exceptions.ResourceNotFound('Could not find the requested '
|
||||
# 'resource: %s' % resource_path)
|
||||
pass
|
||||
|
||||
return pkg_resources.resource_filename(package, resource_path)
|
||||
|
||||
|
||||
def load_schema(version, name, package=None):
|
||||
"""Load json schema from resources"""
|
||||
schema_filename = resource_filename('schemas', version, '%s.json' % name,
|
||||
package=package)
|
||||
|
||||
with open(schema_filename) as schema_file:
|
||||
schema_json = json.load(schema_file)
|
||||
|
||||
return schema_json
|
@ -1,171 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@hp.com>
|
||||
#
|
||||
# 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 cliff import command
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
import six
|
||||
|
||||
from cueclient import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ListClustersCommand(lister.Lister):
|
||||
"""List Clusters"""
|
||||
|
||||
columns = ['id', 'name', 'status', 'size', 'endpoints']
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListClustersCommand, self).get_parser(prog_name)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.mb
|
||||
|
||||
data = client.clusters.list()
|
||||
|
||||
cols = self.columns
|
||||
return cols, (utils.get_item_properties(s, cols) for s in data)
|
||||
|
||||
|
||||
class ShowClusterCommand(show.ShowOne):
|
||||
"""Show Cluster"""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowClusterCommand, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('id', help="Cluster ID")
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.mb
|
||||
|
||||
data = client.clusters.get(parsed_args.id)
|
||||
|
||||
return zip(*sorted(six.iteritems(data)))
|
||||
|
||||
|
||||
class CreateClusterCommand(show.ShowOne):
|
||||
"""Create Cluster"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateClusterCommand, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('--name', help="Cluster Name", required=True)
|
||||
parser.add_argument('--nic', help="Network to place nodes on",
|
||||
required=True)
|
||||
parser.add_argument('--flavor', help="Flavor to use.", required=True)
|
||||
parser.add_argument('--size', help="Number of nodes", required=True)
|
||||
|
||||
parser.add_argument('--volume_size', help="Volume size")
|
||||
parser.add_argument('--auth',
|
||||
metavar="<type=type,user=user,pass=pass>",
|
||||
help="broker authentication,"
|
||||
"type=type,user=user,pass=pass")
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.mb
|
||||
|
||||
auth_type = username = password = None
|
||||
if parsed_args.auth:
|
||||
for kv_str in parsed_args.auth.split(","):
|
||||
k, v = kv_str.split("=")
|
||||
if 'type' == k:
|
||||
auth_type = v
|
||||
elif 'user' == k:
|
||||
username = v
|
||||
elif 'pass' == k:
|
||||
password = v
|
||||
if not auth_type:
|
||||
auth_type = 'plain'
|
||||
data = client.clusters.create(
|
||||
name=parsed_args.name,
|
||||
nic=parsed_args.nic,
|
||||
flavor=parsed_args.flavor,
|
||||
size=parsed_args.size,
|
||||
volume_size=parsed_args.volume_size,
|
||||
auth_type=auth_type,
|
||||
username=username,
|
||||
password=password)
|
||||
|
||||
return zip(*sorted(six.iteritems(data)))
|
||||
|
||||
|
||||
class SetClusterCommand(command.Command):
|
||||
"""Set Cluster"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(SetClusterCommand, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('id', help="Cluster ID")
|
||||
parser.add_argument('--name', help="Cluster Name")
|
||||
parser.add_argument('--email', help="Cluster Email")
|
||||
parser.add_argument('--ttl', type=int, help="Time To Live (Seconds)")
|
||||
description_group = parser.add_mutually_exclusive_group()
|
||||
description_group.add_argument('--description', help="Description")
|
||||
description_group.add_argument('--no-description', action='store_true')
|
||||
|
||||
parser.add_argument('--masters', help="Cluster Masters", nargs='+')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.mb
|
||||
|
||||
data = {}
|
||||
|
||||
# TODO(kiall): API needs updating.. this get is silly
|
||||
if parsed_args.name:
|
||||
data['name'] = parsed_args.name
|
||||
|
||||
if parsed_args.email:
|
||||
data['email'] = parsed_args.email
|
||||
|
||||
if parsed_args.ttl:
|
||||
data['ttl'] = parsed_args.ttl
|
||||
|
||||
if parsed_args.no_description:
|
||||
data['description'] = None
|
||||
elif parsed_args.description:
|
||||
data['description'] = parsed_args.description
|
||||
|
||||
if parsed_args.masters:
|
||||
data['masters'] = parsed_args.masters
|
||||
|
||||
updated = client.clusters.update(parsed_args.id, data)
|
||||
return zip(*sorted(six.iteritems(updated)))
|
||||
|
||||
|
||||
class DeleteClusterCommand(command.Command):
|
||||
"""Delete Cluster"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteClusterCommand, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('id', help="Cluster ID")
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.mb
|
||||
client.clusters.delete(parsed_args.id)
|
||||
LOG.info('Cluster %s was deleted', parsed_args.id)
|
@ -1,39 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@hp.com>
|
||||
#
|
||||
# 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 keystoneauth1 import adapter
|
||||
|
||||
from cueclient.v1.clusters import ClusterController
|
||||
from cueclient import version
|
||||
|
||||
|
||||
class Client(object):
|
||||
def __init__(self, region_name=None, endpoint_type='publicURL',
|
||||
extensions=None, service_type='message-broker',
|
||||
service_name=None, http_log_debug=False, session=None,
|
||||
auth=None, interface=None):
|
||||
|
||||
if interface is None:
|
||||
interface = endpoint_type.rstrip('URL')
|
||||
self.session = adapter.Adapter(
|
||||
session,
|
||||
auth=auth,
|
||||
region_name=region_name,
|
||||
service_type=service_type,
|
||||
interface=interface,
|
||||
user_agent='python-cueclient-%s' % version.version_info,
|
||||
version=('1'))
|
||||
|
||||
self.clusters = ClusterController(self)
|
@ -1,70 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@hp.com>
|
||||
#
|
||||
# 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 cueclient import controller
|
||||
from cueclient import utils
|
||||
from cueclient import warlock
|
||||
|
||||
Cluster = warlock.model_factory(utils.load_schema('v1', 'cluster'))
|
||||
|
||||
|
||||
class ClusterController(controller.Controller):
|
||||
"""Cluster Controller to manages operations."""
|
||||
def create(self, name, nic, flavor, size, volume_size, auth_type, username,
|
||||
password):
|
||||
"""Create Cluster"""
|
||||
auth = {'type': auth_type,
|
||||
'token': {'username': username,
|
||||
'password': password}}
|
||||
|
||||
data = {
|
||||
"network_id": nic.split(","),
|
||||
"name": name,
|
||||
"flavor": flavor,
|
||||
"size": size,
|
||||
"volume_size": volume_size,
|
||||
"authentication": auth,
|
||||
}
|
||||
url = self.build_url("/clusters")
|
||||
|
||||
return Cluster(self._post(url, json=data))
|
||||
|
||||
def list(self, marker=None, limit=None, params=None):
|
||||
"""List Clusters"""
|
||||
url = self.build_url("/clusters", marker, limit, params)
|
||||
|
||||
response = self._get(url, "clusters")
|
||||
return [Cluster(i) for i in response]
|
||||
|
||||
def get(self, cluster_id):
|
||||
"""Show Cluster"""
|
||||
url = self.build_url("/clusters/%s" % cluster_id)
|
||||
|
||||
return Cluster(self._get(url))
|
||||
|
||||
def update(self, cluster_id, values):
|
||||
data = {
|
||||
"cluster": values
|
||||
}
|
||||
|
||||
url = self.build_url("/clusters/%s" % cluster_id)
|
||||
|
||||
return self._patch(url, data=data)
|
||||
|
||||
def delete(self, cluster_id):
|
||||
"""Delete Cluster"""
|
||||
url = self.build_url("/clusters/%s" % cluster_id)
|
||||
|
||||
return self._delete(url)
|
@ -1,18 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@hp.com>
|
||||
#
|
||||
# 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 pbr.version
|
||||
|
||||
version_info = pbr.version.VersionInfo('python-cueclient')
|
@ -1,134 +0,0 @@
|
||||
# Copyright 2012 Brian Waldon
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Code copied from Warlock, as warlock depends on jsonschema==0.2
|
||||
# Hopefully we can upstream the changes ASAP.
|
||||
#
|
||||
|
||||
import copy
|
||||
import logging
|
||||
|
||||
import jsonschema
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class InvalidOperation(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
class ValidationError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def model_factory(schema):
|
||||
"""Generate a model class based on the provided JSON Schema
|
||||
|
||||
:param schema: dict representing valid JSON schema
|
||||
"""
|
||||
schema = copy.deepcopy(schema)
|
||||
|
||||
def validator(obj):
|
||||
"""Apply a JSON schema to an object"""
|
||||
try:
|
||||
jsonschema.validate(obj, schema, cls=jsonschema.Draft3Validator)
|
||||
except jsonschema.ValidationError as e:
|
||||
raise ValidationError(str(e))
|
||||
|
||||
class Model(dict):
|
||||
"""Self-validating model for arbitrary objects"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
d = dict(*args, **kwargs)
|
||||
|
||||
# we overload setattr so set this manually
|
||||
self.__dict__['validator'] = validator
|
||||
try:
|
||||
self.validator(d)
|
||||
except ValidationError as e:
|
||||
raise ValueError('Validation Error: %s' % str(e))
|
||||
else:
|
||||
dict.__init__(self, d)
|
||||
|
||||
self.__dict__['changes'] = {}
|
||||
|
||||
def __getattr__(self, key):
|
||||
try:
|
||||
return self.__getitem__(key)
|
||||
except KeyError:
|
||||
raise AttributeError(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
mutation = dict(self.items())
|
||||
mutation[key] = value
|
||||
try:
|
||||
self.validator(mutation)
|
||||
except ValidationError as e:
|
||||
raise InvalidOperation(str(e))
|
||||
|
||||
dict.__setitem__(self, key, value)
|
||||
|
||||
self.__dict__['changes'][key] = value
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
self.__setitem__(key, value)
|
||||
|
||||
def clear(self):
|
||||
raise InvalidOperation()
|
||||
|
||||
def pop(self, key, default=None):
|
||||
raise InvalidOperation()
|
||||
|
||||
def popitem(self):
|
||||
raise InvalidOperation()
|
||||
|
||||
def __delitem__(self, key):
|
||||
raise InvalidOperation()
|
||||
|
||||
# NOTE(termie): This is kind of the opposite of what copy usually does
|
||||
def copy(self):
|
||||
return copy.deepcopy(dict(self))
|
||||
|
||||
def update(self, other):
|
||||
# NOTE(kiall): It seems update() doesn't update the
|
||||
# self.__dict__['changes'] dict correctly.
|
||||
mutation = dict(self.items())
|
||||
mutation.update(other)
|
||||
try:
|
||||
self.validator(mutation)
|
||||
except ValidationError as e:
|
||||
raise InvalidOperation(str(e))
|
||||
dict.update(self, other)
|
||||
|
||||
def iteritems(self):
|
||||
return copy.deepcopy(dict(self)).iteritems()
|
||||
|
||||
def items(self):
|
||||
return copy.deepcopy(dict(self)).items()
|
||||
|
||||
def itervalues(self):
|
||||
return copy.deepcopy(dict(self)).itervalues()
|
||||
|
||||
def keys(self):
|
||||
return copy.deepcopy(dict(self)).keys()
|
||||
|
||||
def values(self):
|
||||
return copy.deepcopy(dict(self)).values()
|
||||
|
||||
@property
|
||||
def changes(self):
|
||||
return copy.deepcopy(self.__dict__['changes'])
|
||||
|
||||
Model.__name__ = str(schema['title'])
|
||||
return Model
|
@ -1,125 +0,0 @@
|
||||
=====================
|
||||
Cue Command Line Tool
|
||||
=====================
|
||||
|
||||
The python-cueclient can be used as a command line tool for accessing Cue API.
|
||||
|
||||
Credentials
|
||||
-----------
|
||||
|
||||
As with any OpenStack utility, :program:`python-cueclient` requires certain information to
|
||||
talk to the REST API, username, password, auth url (from where the other required
|
||||
endpoints are retrieved once you are authenticated).
|
||||
|
||||
To provide your access credentials (username, password, tenant name or project_name)
|
||||
you can pass them on the command line with the ``--os-username``, ``--os-password``, ``--os-tenant-name`` or ``--os-project-name``
|
||||
params, but it's easier to just set them as environment variables::
|
||||
|
||||
export OS_USERNAME=<your_username>
|
||||
export OS_PASSWORD=<your_password>
|
||||
export OS_PROJECT_NAME=<project_name>
|
||||
|
||||
You will also need to define the authentication url with ``--os-auth-url``
|
||||
or set is as an environment variable as well::
|
||||
|
||||
export OS_AUTH_URL=<url_to_openstack_identity>
|
||||
|
||||
Since Keystone can return multiple regions in the Service Catalog, you
|
||||
can specify the one you want with ``--os-region-name`` (or
|
||||
``export OS_REGION_NAME``). It defaults to the first in the list returned.
|
||||
|
||||
Using the command line tool
|
||||
---------------------------
|
||||
|
||||
With enough details now in environment, you can use the cue client to create,list,show or delete cluster(s).
|
||||
|
||||
The Openstack Client can be called interactively by simply typing:
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
openstack
|
||||
|
||||
Cluster Create
|
||||
--------------
|
||||
|
||||
Required fields for 'create' : name, network id , flavor and size.
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
(openstack) message-broker cluster create --name cluster_04 --nic 3dd26c0b-03f2-4d2e-ae87-c02d7f33c788 --flavor 2 --size 3 --auth type=plain,user=rabbitmq,pass=rabbit
|
||||
+-------------+--------------------------------------+
|
||||
| Field | Value |
|
||||
+-------------+--------------------------------------+
|
||||
| created_at | 2015-02-17T18:25:28+00:00 |
|
||||
| endpoints | [] |
|
||||
| flavor | 2 |
|
||||
| id | 06d3c0e4-4972-4ca9-91c1-373b1c74e8e1 |
|
||||
| name | cluster_04 |
|
||||
| network_id | 3dd26c0b-03f2-4d2e-ae87-c02d7f33c788 |
|
||||
| size | 3 |
|
||||
| status | BUILDING |
|
||||
| updated_at | 2015-02-17T18:25:28+00:00 |
|
||||
| volume_size | None |
|
||||
+-------------+--------------------------------------+
|
||||
|
||||
Cluster Show
|
||||
------------
|
||||
|
||||
Required field for 'show' : cluster-id
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
|
||||
(openstack) message-broker cluster show 06d3c0e4-4972-4ca9-91c1-373b1c74e8e1
|
||||
+-------------+--------------------------------------+
|
||||
| Field | Value |
|
||||
+-------------+--------------------------------------+
|
||||
| created_at | 2015-02-17T18:25:28+00:00 |
|
||||
| endpoints | [] |
|
||||
| flavor | 2 |
|
||||
| id | 06d3c0e4-4972-4ca9-91c1-373b1c74e8e1 |
|
||||
| name | cluster_04 |
|
||||
| network_id | 3dd26c0b-03f2-4d2e-ae87-c02d7f33c788 |
|
||||
| size | 3 |
|
||||
| status | BUILDING |
|
||||
| updated_at | 2015-02-17T18:25:28+00:00 |
|
||||
| volume_size | None |
|
||||
+-------------+--------------------------------------+
|
||||
|
||||
Cluster Delete
|
||||
--------------
|
||||
|
||||
Required field for 'delete' : cluster-id
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
(openstack) message-broker cluster delete 06d3c0e4-4972-4ca9-91c1-373b1c74e8e1
|
||||
|
||||
Cluster List
|
||||
------------
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
(openstack) message-broker cluster list
|
||||
+--------------------------------------+-------------+----------+--------+------+
|
||||
| id | name | status | flavor | size |
|
||||
+--------------------------------------+-------------+----------+--------+------+
|
||||
| 06d3c0e4-4972-4ca9-91c1-373b1c74e8e1 | cluster_04 | DELETING | 2 | 3 |
|
||||
| 09fa2dc2-7ebb-423f-9726-f45b53f0df99 | cluster_02 | DELETING | 1 | 3 |
|
||||
| 2d6a5359-2c45-44bb-baa9-3ccd2a48c217 | cluster_03 | BUILDING | 2 | 2 |
|
||||
+--------------------------------------+-------------+----------+--------+------+
|
||||
|
||||
Subcommands
|
||||
-----------
|
||||
|
||||
Here are the full list of subcommands:
|
||||
|
||||
================================== ======================================================
|
||||
subcommand Notes
|
||||
================================== ======================================================
|
||||
message-broker cluster create Create Cluster
|
||||
message-broker cluster delete Delete Cluster
|
||||
message-broker cluster show Show Cluster
|
||||
message-broker cluster list List Clusters
|
||||
================================== ======================================================
|
||||
|
@ -1,260 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# python-cueclient documentation build configuration file, created by
|
||||
# sphinx-quickstart on Wed Feb 11 14:55:41 2015.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'python-cueclient'
|
||||
copyright = u'2015, Openstack Cue Team'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.0.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.0.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
#keep_warnings = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
#html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'python-cueclientdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'python-cueclient.tex', u'python-cueclient Documentation',
|
||||
u'Openstack Cue Team', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'python-cueclient', u'python-cueclient Documentation',
|
||||
[u'Openstack Cue Team'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'python-cueclient', u'python-cueclient Documentation',
|
||||
u'Openstack Cue Team', 'python-cueclient', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#texinfo_no_detailmenu = False
|
@ -1,47 +0,0 @@
|
||||
Contributing
|
||||
============
|
||||
|
||||
Code is hosted `on GitHub`_.
|
||||
Submit bugs to the Cue Client project on `Launchpad`_.
|
||||
Submit code to the openstack/python-cueclient project using `Gerrit`_.
|
||||
|
||||
Here's a quick summary:
|
||||
|
||||
Install the git-review package to make life easier
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
pip install git-review
|
||||
|
||||
Branch, work, & submit:
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
# cut a new branch, tracking master
|
||||
git checkout --track -b bug/id origin/master
|
||||
# work work work
|
||||
git add stuff
|
||||
git commit
|
||||
# rebase/squash to a single commit before submitting
|
||||
git rebase -i
|
||||
# submit
|
||||
git-review
|
||||
|
||||
Coding Standards
|
||||
----------------
|
||||
Cue Client uses the OpenStack flake8 coding standards guidelines.
|
||||
These are stricter than pep8, and are run by gerrit on every commit.
|
||||
|
||||
You can use tox to check your code locally by running
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
# For just flake8 tests
|
||||
tox -e flake8
|
||||
# For tests + flake8
|
||||
tox
|
||||
|
||||
.. _on GitHub: https://github.com/openstack/python-cueclient
|
||||
.. _Launchpad: https://launchpad.net/python-cueclient
|
||||
.. _Gerrit: http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
@ -1,73 +0,0 @@
|
||||
..
|
||||
Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
|
||||
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.
|
||||
|
||||
.. _getting-started:
|
||||
|
||||
===============
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
.. _Development Environment:
|
||||
|
||||
The python-cueclient can be used either as a command line tool or as a binding to access Cue.
|
||||
|
||||
|
||||
Installing Cue Client from source
|
||||
=================================
|
||||
|
||||
.. index::
|
||||
double: install; python-cueclient
|
||||
|
||||
|
||||
1. Clone the CueClient repo from GitHub
|
||||
|
||||
::
|
||||
|
||||
$ git clone https://github.com/openstack/python-cueclient.git
|
||||
$ cd python-cueclient
|
||||
|
||||
|
||||
2. Setup virtualenv
|
||||
|
||||
.. note::
|
||||
This is an optional step, but will allow CueClient's dependencies
|
||||
to be installed in a contained environment that can be easily deleted
|
||||
if you choose to start over or uninstall Cue.
|
||||
|
||||
::
|
||||
|
||||
$ virtualenv --no-site-packages .venv
|
||||
$ . .venv/bin/activate
|
||||
|
||||
|
||||
3. Install CueClient and its dependencies
|
||||
|
||||
::
|
||||
|
||||
$ pip install -r requirements.txt -r test-requirements.txt
|
||||
$ python setup.py develop
|
||||
|
||||
|
||||
Installation to use as Command Line Tool
|
||||
----------------------------------------
|
||||
4. To access the shell for cue client 'python-openstackclient' has to be installed.
|
||||
|
||||
::
|
||||
|
||||
$ pip install python-openstackclient
|
||||
|
||||
|
||||
.. note::
|
||||
This step can be skipped if you choose to use python-cueclient as only a binding to Cue API.
|
@ -1,26 +0,0 @@
|
||||
.. python-cueclient documentation master file, created by
|
||||
sphinx-quickstart on Wed Feb 11 14:55:41 2015.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to python-cueclient's documentation!
|
||||
============================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
getting-started
|
||||
command-line
|
||||
python-bindings
|
||||
contributing
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
@ -1,54 +0,0 @@
|
||||
====
|
||||
cue
|
||||
====
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
`message-broker` [options] <command> [command-options]
|
||||
|
||||
`message-broker help`
|
||||
|
||||
`message-broker help` <command>
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
||||
`message-broker` is a command line client for controlling OpenStack Cue, the message broker provisioning service.
|
||||
|
||||
Before you can issue commands with `message-broker`, you must ensure that your
|
||||
environment contains the necessary variables so that you can prove to the CLI
|
||||
who you are and what credentials you have to issue the commands.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
|
||||
To get a list of available commands and options run::
|
||||
|
||||
message-broker help
|
||||
|
||||
To get usage and options of a command run::
|
||||
|
||||
message-broker help <command>
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
|
||||
Get information about cluster create command::
|
||||
|
||||
help message-broker cluster create
|
||||
|
||||
List available clusters::
|
||||
|
||||
help message-broker cluster list
|
||||
|
||||
View cluster information::
|
||||
|
||||
help message-broker cluster show
|
||||
|
||||
Delete clusters::
|
||||
|
||||
help message-broker cluster delete
|
||||
|
@ -1,196 +0,0 @@
|
||||
===============
|
||||
Python Bindings
|
||||
===============
|
||||
|
||||
The python-cueclient can be used to interact with the Cue API from any other
|
||||
python program.
|
||||
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Below is a simple example of how to instantiate and perform basic tasks using
|
||||
the bindings.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
|
||||
from keystoneauth1.identity import v3 as keystone_v3_auth
|
||||
from keystoneauth1 import session as keystone_session
|
||||
from cueclient.v1 import client
|
||||
|
||||
auth = keystone_v3_auth.Password(
|
||||
auth_url="http://example.com:5000/v3",
|
||||
username="admin",
|
||||
password="password",
|
||||
project_name="admin",
|
||||
project_domain_name="default",
|
||||
user_domain_name="default"
|
||||
)
|
||||
|
||||
session = keystone_session.Session(auth=auth)
|
||||
|
||||
# Create an instance of the client
|
||||
cue_client = client.Client(session=session)
|
||||
|
||||
# Cluster List - returns list of cluster objects
|
||||
list_response = cue_client.clusters.list()
|
||||
|
||||
# Iterate the list, printing some useful information
|
||||
for cluster in list_response:
|
||||
|
||||
print "Cluster ID: %s \t Name: %s \t NetworkId: %s \t Flavor: %s \t Size: %s" % \
|
||||
(cluster.id, cluster.name, cluster.network_id, cluster.flavor, cluster.size)
|
||||
|
||||
And the output this program might produce:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python /example.py
|
||||
Cluster ID: 213cdd06-c361-4cca-93b5-7ed651d46936 Name: test_binding2 NetworkId: 33333 Flavor: 1 Size: 2
|
||||
Cluster ID: 24f299fd-0509-4218-bf80-6c0481452480 Name: test_binding4 NetworkId: 44444 Flavor: 1 Size: 2
|
||||
|
||||
|
||||
Authentication
|
||||
==============
|
||||
|
||||
Cue supports Keystone authentication.
|
||||
|
||||
Keystone Authentication
|
||||
-----------------------
|
||||
|
||||
Below is a sample of standard authentication with keystone v3:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
|
||||
from keystoneauth1.identity import v3 as keystone_v3_auth
|
||||
from keystoneauth1 import session as keystone_session
|
||||
|
||||
auth = keystone_v3_auth.Password(
|
||||
auth_url="http://example.com:5000/v3",
|
||||
username="admin",
|
||||
password="password",
|
||||
project_name="admin",
|
||||
project_domain_name="default",
|
||||
user_domain_name="default"
|
||||
)
|
||||
|
||||
Cue Functions
|
||||
=============
|
||||
|
||||
Cluster List
|
||||
------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
|
||||
from keystoneauth1.identity import v3 as keystone_v3_auth
|
||||
from keystoneauth1 import session as keystone_session
|
||||
from cueclient.v1 import client
|
||||
|
||||
auth = keystone_v3_auth.Password(
|
||||
auth_url="http://example.com:5000/v3",
|
||||
username="admin",
|
||||
password="password",
|
||||
project_name="admin",
|
||||
project_domain_name="default",
|
||||
user_domain_name="default"
|
||||
)
|
||||
|
||||
session = keystone_session.Session(auth=auth)
|
||||
cue_client = client.Client(session=session)
|
||||
|
||||
# Cluster List
|
||||
list_response = cue_client.clusters.list()
|
||||
|
||||
|
||||
Cluster Show
|
||||
------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
|
||||
from keystoneauth1.identity import v3 as keystone_v3_auth
|
||||
from keystoneauth1 import session as keystone_session
|
||||
from cueclient.v1 import client
|
||||
|
||||
auth = keystone_v3_auth.Password(
|
||||
auth_url="http://example.com:5000/v3",
|
||||
username="admin",
|
||||
password="password",
|
||||
project_name="admin",
|
||||
project_domain_name="default",
|
||||
user_domain_name="default"
|
||||
)
|
||||
|
||||
session = keystone_session.Session(auth=auth)
|
||||
cue_client = client.Client(session=session)
|
||||
|
||||
cluster_id = "0a352f9a-8aa8-411e-9d6d-4e6217d70afd"
|
||||
|
||||
# Cluster Show
|
||||
show_response = cue_client.clusters.get(cluster_id)
|
||||
|
||||
|
||||
Cluster Create
|
||||
--------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
|
||||
from keystoneauth1.auth.identity import v3 as keystone_v3_auth
|
||||
from keystoneauth1 import session as keystone_session
|
||||
from cueclient.v1 import client
|
||||
|
||||
auth = keystone_v3_auth.Password(
|
||||
auth_url="http://example.com:5000/v3",
|
||||
username="admin",
|
||||
password="password",
|
||||
project_name="admin",
|
||||
project_domain_name="default",
|
||||
user_domain_name="default"
|
||||
)
|
||||
|
||||
session = keystone_session.Session(auth=auth)
|
||||
cue_client = client.Client(session=session)
|
||||
|
||||
# Cluster create
|
||||
create_response = cue_client.clusters.create(name="test_binding5",
|
||||
nic="55555", flavor="1",size="2",volume_size="0",
|
||||
auth_type="plain", username="rabbitmq",
|
||||
password="password")
|
||||
|
||||
Cluster Delete
|
||||
--------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
|
||||
from keystoneauth1.identity import v3 as keystone_v3_auth
|
||||
from keystoneauth1 import session as keystone_session
|
||||
from cueclient.v1 import client
|
||||
|
||||
auth = keystone_v3_auth.Password(
|
||||
auth_url="http://example.com:5000/v3",
|
||||
username="admin",
|
||||
password="password",
|
||||
project_name="admin",
|
||||
project_domain_name="default",
|
||||
user_domain_name="default"
|
||||
)
|
||||
|
||||
session = keystone_session.Session(auth=auth)
|
||||
cue_client = client.Client(session=session)
|
||||
|
||||
delete_id = "dc86d96f-6b37-4e2d-9805-4542450f427d"
|
||||
|
||||
# Cluster Delete
|
||||
delete_response = cue_client.clusters.delete(delete_id)
|
||||
|
@ -1,10 +0,0 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0
|
||||
keystoneauth1>=2.7.0 # Apache-2.0
|
||||
jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
|
||||
pbr>=1.6 # Apache-2.0
|
||||
requests!=2.9.0,>=2.8.1 # Apache-2.0
|
||||
six>=1.9.0 # MIT
|
||||
stevedore>=1.5.0 # Apache-2.0
|
65
setup.cfg
65
setup.cfg
@ -1,65 +0,0 @@
|
||||
[metadata]
|
||||
name = python-cueclient
|
||||
summary = Message Broker Provisioning Service Cue Client
|
||||
description-file =
|
||||
README.rst
|
||||
license = Apache License, Version 2.0
|
||||
author = OpenStack
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = http://www.openstack.org/
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Information Technology
|
||||
Intended Audience :: System Administrators
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
|
||||
[global]
|
||||
setup-hooks =
|
||||
pbr.hooks.setup_hook
|
||||
|
||||
[files]
|
||||
packages =
|
||||
cueclient
|
||||
|
||||
[entry_points]
|
||||
openstack.mb.v1 =
|
||||
message-broker_cluster_create = cueclient.v1.cli.clusters:CreateClusterCommand
|
||||
message-broker_cluster_list = cueclient.v1.cli.clusters:ListClustersCommand
|
||||
message-broker_cluster_show = cueclient.v1.cli.clusters:ShowClusterCommand
|
||||
message-broker_cluster_set = cueclient.v1.cli.clusters:SetClusterCommand
|
||||
message-broker_cluster_delete = cueclient.v1.cli.clusters:DeleteClusterCommand
|
||||
|
||||
openstack.cli.extension =
|
||||
mb = cueclient.osc.plugin
|
||||
|
||||
|
||||
[build_sphinx]
|
||||
all_files = 1
|
||||
build-dir = doc/build
|
||||
source-dir = doc/source
|
||||
|
||||
[egg_info]
|
||||
tag_build =
|
||||
tag_date = 0
|
||||
tag_svn_revision = 0
|
||||
|
||||
[compile_catalog]
|
||||
directory = cueclient/locale
|
||||
domain = cueclient
|
||||
|
||||
[update_catalog]
|
||||
domain = cueclient
|
||||
output_dir = cueclient/locale
|
||||
input_file = cueclient/locale/cueclient.pot
|
||||
|
||||
[extract_messages]
|
||||
keywords = _ gettext ngettext l_ lazy_gettext
|
||||
mapping_file = babel.cfg
|
||||
output_file = cueclient/locale/cueclient.pot
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
29
setup.py
29
setup.py
@ -1,29 +0,0 @@
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import setuptools
|
||||
|
||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||
# setuptools if some other modules registered functions in `atexit`.
|
||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||
try:
|
||||
import multiprocessing # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=1.8'],
|
||||
pbr=True)
|
@ -1,17 +0,0 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
# Hacking already pins down pep8, pyflakes and flake8
|
||||
hacking<0.10,>=0.9.2
|
||||
coverage>=3.6 # Apache-2.0
|
||||
discover # BSD
|
||||
fixtures>=1.3.1 # Apache-2.0/BSD
|
||||
mock>=1.2 # BSD
|
||||
oslo.serialization>=1.10.0 # Apache-2.0
|
||||
requests-mock>=0.7.0 # Apache-2.0
|
||||
python-subunit>=0.0.18 # Apache-2.0/BSD
|
||||
testrepository>=0.0.18 # Apache-2.0/BSD
|
||||
|
||||
# Doc requirements
|
||||
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD
|
||||
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
|
45
tox.ini
45
tox.ini
@ -1,45 +0,0 @@
|
||||
[tox]
|
||||
envlist = py27,py34,flake8
|
||||
minversion = 1.6
|
||||
|
||||
[testenv]
|
||||
sitepackages = False
|
||||
install_command = pip install {opts} {packages}
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
LANG=en_US.UTF-8
|
||||
LANGUAGE=en_US:en
|
||||
LC_ALL=C
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
whitelist_externals = find
|
||||
commands =
|
||||
find . -type f -name "*.pyc" -delete
|
||||
python setup.py testr --testr-args='{posargs}'
|
||||
|
||||
[testenv:flake8]
|
||||
commands = flake8
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8
|
||||
|
||||
[testenv:pyflakes]
|
||||
commands = flake8
|
||||
|
||||
[testenv:cover]
|
||||
commands = python setup.py testr --coverage --testr-args='{posargs}'
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[flake8]
|
||||
# ignored flake8 codes:
|
||||
# H302 import only modules
|
||||
# H402 one line docstring needs punctuation
|
||||
# H404 multi line docstring should start with a summary
|
||||
# H405 multi line docstring summary not separated with an empty line
|
||||
# H904 Wrap long lines in parentheses instead of a backslash
|
||||
# See cueclient for other ignored codes that may apply here
|
||||
|
||||
ignore = H302,H402,H404,H405,H904
|
||||
builtins = _
|
||||
exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,tools
|
Loading…
Reference in New Issue
Block a user