Remove v2 classes
Remove all cinderclient.v2 classes, mostly incorporating them into their v3 counterparts and updating the tests and test fixtures. Depends-on: https://review.opendev.org/c/openstack/horizon/+/800814 Change-Id: I335db5c1799edb2273bf8bfc9e1bc9de404a4ba5
This commit is contained in:
parent
3502a5591a
commit
cb5235250c
@ -13,7 +13,6 @@
|
|||||||
from keystoneauth1 import fixture
|
from keystoneauth1 import fixture
|
||||||
|
|
||||||
from cinderclient.tests.unit.fixture_data import base
|
from cinderclient.tests.unit.fixture_data import base
|
||||||
from cinderclient.v2 import client as v2client
|
|
||||||
from cinderclient.v3 import client as v3client
|
from cinderclient.v3 import client as v3client
|
||||||
|
|
||||||
|
|
||||||
@ -34,21 +33,6 @@ class Base(base.Fixture):
|
|||||||
headers=self.json_headers)
|
headers=self.json_headers)
|
||||||
|
|
||||||
|
|
||||||
class V2(Base):
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(V2, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
svc = self.token.add_service('volumev2')
|
|
||||||
svc.add_endpoint(self.volume_url)
|
|
||||||
|
|
||||||
def new_client(self):
|
|
||||||
return v2client.Client(username='xx',
|
|
||||||
api_key='xx',
|
|
||||||
project_id='xx',
|
|
||||||
auth_url=self.identity_url)
|
|
||||||
|
|
||||||
|
|
||||||
class V3(Base):
|
class V3(Base):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -22,13 +22,13 @@ from cinderclient import base
|
|||||||
from cinderclient import exceptions
|
from cinderclient import exceptions
|
||||||
from cinderclient.tests.unit import test_utils
|
from cinderclient.tests.unit import test_utils
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
from cinderclient.v3 import client
|
from cinderclient.v3 import client
|
||||||
from cinderclient.v3 import volumes
|
from cinderclient.v3 import volumes
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
cs = fakes.FakeClient()
|
||||||
|
|
||||||
REQUEST_ID = 'req-test-request-id'
|
REQUEST_ID = test_utils.REQUEST_ID
|
||||||
|
|
||||||
|
|
||||||
def create_response_obj_with_header():
|
def create_response_obj_with_header():
|
||||||
|
@ -26,7 +26,6 @@ import cinderclient.client
|
|||||||
from cinderclient import exceptions
|
from cinderclient import exceptions
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v3 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
import cinderclient.v2.client
|
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
@ddt.ddt
|
||||||
|
@ -24,9 +24,9 @@ from cinderclient import base
|
|||||||
from cinderclient import exceptions
|
from cinderclient import exceptions
|
||||||
from cinderclient import shell_utils
|
from cinderclient import shell_utils
|
||||||
from cinderclient.tests.unit import utils as test_utils
|
from cinderclient.tests.unit import utils as test_utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
|
||||||
from cinderclient import utils
|
from cinderclient import utils
|
||||||
|
|
||||||
|
REQUEST_ID = 'req-test-request-id'
|
||||||
UUID = '8e8ec658-c7b0-4243-bdf8-6f7f2952c0d0'
|
UUID = '8e8ec658-c7b0-4243-bdf8-6f7f2952c0d0'
|
||||||
|
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ class FakeManager(base.ManagerWithFind):
|
|||||||
raise exceptions.NotFound(resource_id)
|
raise exceptions.NotFound(resource_id)
|
||||||
|
|
||||||
def list(self, search_opts, **kwargs):
|
def list(self, search_opts, **kwargs):
|
||||||
return common_base.ListWithMeta(self.resources, fakes.REQUEST_ID)
|
return common_base.ListWithMeta(self.resources, REQUEST_ID)
|
||||||
|
|
||||||
|
|
||||||
class FakeManagerWithApi(base.Manager):
|
class FakeManagerWithApi(base.Manager):
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
# Copyright (c) 2013 OpenStack Foundation
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from cinderclient.tests.unit import utils
|
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
|
||||||
|
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
|
||||||
|
|
||||||
|
|
||||||
class QuotaSetsTest(utils.TestCase):
|
|
||||||
|
|
||||||
def test_tenant_quotas_get(self):
|
|
||||||
tenant_id = 'test'
|
|
||||||
quota = cs.quotas.get(tenant_id)
|
|
||||||
cs.assert_called('GET', '/os-quota-sets/%s?usage=False' % tenant_id)
|
|
||||||
self._assert_request_id(quota)
|
|
||||||
|
|
||||||
def test_tenant_quotas_defaults(self):
|
|
||||||
tenant_id = 'test'
|
|
||||||
quota = cs.quotas.defaults(tenant_id)
|
|
||||||
cs.assert_called('GET', '/os-quota-sets/%s/defaults' % tenant_id)
|
|
||||||
self._assert_request_id(quota)
|
|
||||||
|
|
||||||
def test_update_quota(self):
|
|
||||||
q = cs.quotas.get('test')
|
|
||||||
q.update(volumes=2)
|
|
||||||
q.update(snapshots=2)
|
|
||||||
q.update(gigabytes=2000)
|
|
||||||
q.update(backups=2)
|
|
||||||
q.update(backup_gigabytes=2000)
|
|
||||||
q.update(per_volume_gigabytes=100)
|
|
||||||
cs.assert_called('PUT', '/os-quota-sets/test')
|
|
||||||
self._assert_request_id(q)
|
|
||||||
|
|
||||||
def test_refresh_quota(self):
|
|
||||||
q = cs.quotas.get('test')
|
|
||||||
q2 = cs.quotas.get('test')
|
|
||||||
self.assertEqual(q.volumes, q2.volumes)
|
|
||||||
self.assertEqual(q.snapshots, q2.snapshots)
|
|
||||||
self.assertEqual(q.gigabytes, q2.gigabytes)
|
|
||||||
self.assertEqual(q.backups, q2.backups)
|
|
||||||
self.assertEqual(q.backup_gigabytes, q2.backup_gigabytes)
|
|
||||||
self.assertEqual(q.per_volume_gigabytes, q2.per_volume_gigabytes)
|
|
||||||
q2.volumes = 0
|
|
||||||
self.assertNotEqual(q.volumes, q2.volumes)
|
|
||||||
q2.snapshots = 0
|
|
||||||
self.assertNotEqual(q.snapshots, q2.snapshots)
|
|
||||||
q2.gigabytes = 0
|
|
||||||
self.assertNotEqual(q.gigabytes, q2.gigabytes)
|
|
||||||
q2.backups = 0
|
|
||||||
self.assertNotEqual(q.backups, q2.backups)
|
|
||||||
q2.backup_gigabytes = 0
|
|
||||||
self.assertNotEqual(q.backup_gigabytes, q2.backup_gigabytes)
|
|
||||||
q2.per_volume_gigabytes = 0
|
|
||||||
self.assertNotEqual(q.per_volume_gigabytes, q2.per_volume_gigabytes)
|
|
||||||
q2.get()
|
|
||||||
self.assertEqual(q.volumes, q2.volumes)
|
|
||||||
self.assertEqual(q.snapshots, q2.snapshots)
|
|
||||||
self.assertEqual(q.gigabytes, q2.gigabytes)
|
|
||||||
self.assertEqual(q.backups, q2.backups)
|
|
||||||
self.assertEqual(q.backup_gigabytes, q2.backup_gigabytes)
|
|
||||||
self.assertEqual(q.per_volume_gigabytes, q2.per_volume_gigabytes)
|
|
||||||
self._assert_request_id(q)
|
|
||||||
self._assert_request_id(q2)
|
|
||||||
|
|
||||||
def test_delete_quota(self):
|
|
||||||
tenant_id = 'test'
|
|
||||||
quota = cs.quotas.delete(tenant_id)
|
|
||||||
cs.assert_called('DELETE', '/os-quota-sets/test')
|
|
||||||
self._assert_request_id(quota)
|
|
@ -1,58 +0,0 @@
|
|||||||
# Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from cinderclient.tests.unit import utils
|
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
|
||||||
|
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeTransfersTest(utils.TestCase):
|
|
||||||
|
|
||||||
def test_create(self):
|
|
||||||
vol = cs.transfers.create('1234')
|
|
||||||
cs.assert_called('POST', '/os-volume-transfer')
|
|
||||||
self._assert_request_id(vol)
|
|
||||||
|
|
||||||
def test_get(self):
|
|
||||||
transfer_id = '5678'
|
|
||||||
vol = cs.transfers.get(transfer_id)
|
|
||||||
cs.assert_called('GET', '/os-volume-transfer/%s' % transfer_id)
|
|
||||||
self._assert_request_id(vol)
|
|
||||||
|
|
||||||
def test_list(self):
|
|
||||||
lst = cs.transfers.list()
|
|
||||||
cs.assert_called('GET', '/os-volume-transfer/detail')
|
|
||||||
self._assert_request_id(lst)
|
|
||||||
|
|
||||||
def test_delete(self):
|
|
||||||
b = cs.transfers.list()[0]
|
|
||||||
vol = b.delete()
|
|
||||||
cs.assert_called('DELETE', '/os-volume-transfer/5678')
|
|
||||||
self._assert_request_id(vol)
|
|
||||||
vol = cs.transfers.delete('5678')
|
|
||||||
self._assert_request_id(vol)
|
|
||||||
cs.assert_called('DELETE', '/os-volume-transfer/5678')
|
|
||||||
vol = cs.transfers.delete(b)
|
|
||||||
cs.assert_called('DELETE', '/os-volume-transfer/5678')
|
|
||||||
self._assert_request_id(vol)
|
|
||||||
|
|
||||||
def test_accept(self):
|
|
||||||
transfer_id = '5678'
|
|
||||||
auth_key = '12345'
|
|
||||||
vol = cs.transfers.accept(transfer_id, auth_key)
|
|
||||||
cs.assert_called('POST', '/os-volume-transfer/%s/accept' % transfer_id)
|
|
||||||
self._assert_request_id(vol)
|
|
@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
from cinderclient import extension
|
from cinderclient import extension
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
from cinderclient.v2.contrib import list_extensions
|
from cinderclient.v3.contrib import list_extensions
|
||||||
|
|
||||||
extensions = [
|
extensions = [
|
||||||
extension.Extension(list_extensions.__name__.split(".")[-1],
|
extension.Extension(list_extensions.__name__.split(".")[-1],
|
@ -15,7 +15,7 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from cinderclient.tests.unit import fakes
|
from cinderclient.tests.unit import fakes
|
||||||
from cinderclient.tests.unit.v2 import fakes as fake_v2
|
from cinderclient.tests.unit.v3 import fakes_base
|
||||||
from cinderclient.v3 import client
|
from cinderclient.v3 import client
|
||||||
|
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ class FakeClient(fakes.FakeClient, client.Client):
|
|||||||
return self.client.get_volume_api_version_from_endpoint()
|
return self.client.get_volume_api_version_from_endpoint()
|
||||||
|
|
||||||
|
|
||||||
class FakeHTTPClient(fake_v2.FakeHTTPClient):
|
class FakeHTTPClient(fakes_base.FakeHTTPClient):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(FakeHTTPClient, self).__init__()
|
super(FakeHTTPClient, self).__init__()
|
||||||
@ -194,6 +194,19 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient):
|
|||||||
del svc['backend_state']
|
del svc['backend_state']
|
||||||
return (200, {}, {'services': services})
|
return (200, {}, {'services': services})
|
||||||
|
|
||||||
|
def put_os_services_enable(self, body, **kw):
|
||||||
|
return (200, {}, {'host': body['host'], 'binary': body['binary'],
|
||||||
|
'status': 'enabled'})
|
||||||
|
|
||||||
|
def put_os_services_disable(self, body, **kw):
|
||||||
|
return (200, {}, {'host': body['host'], 'binary': body['binary'],
|
||||||
|
'status': 'disabled'})
|
||||||
|
|
||||||
|
def put_os_services_disable_log_reason(self, body, **kw):
|
||||||
|
return (200, {}, {'host': body['host'], 'binary': body['binary'],
|
||||||
|
'status': 'disabled',
|
||||||
|
'disabled_reason': body['disabled_reason']})
|
||||||
|
|
||||||
#
|
#
|
||||||
# Clusters
|
# Clusters
|
||||||
#
|
#
|
||||||
@ -285,7 +298,7 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient):
|
|||||||
# Backups
|
# Backups
|
||||||
#
|
#
|
||||||
def put_backups_1234(self, **kw):
|
def put_backups_1234(self, **kw):
|
||||||
backup = fake_v2._stub_backup(
|
backup = fakes_base._stub_backup(
|
||||||
id='1234',
|
id='1234',
|
||||||
base_uri='http://localhost:8776',
|
base_uri='http://localhost:8776',
|
||||||
tenant_id='0fa851f6668144cf9cd8c8419c1646c1')
|
tenant_id='0fa851f6668144cf9cd8c8419c1646c1')
|
||||||
@ -640,10 +653,10 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient):
|
|||||||
transfer2 = 'f625ec3e-13dd-4498-a22a-50afd534cc41'
|
transfer2 = 'f625ec3e-13dd-4498-a22a-50afd534cc41'
|
||||||
return (200, {},
|
return (200, {},
|
||||||
{'transfers': [
|
{'transfers': [
|
||||||
fake_v2._stub_transfer_full(transfer1, base_uri,
|
fakes_base._stub_transfer_full(transfer1, base_uri,
|
||||||
tenant_id),
|
tenant_id),
|
||||||
fake_v2._stub_transfer_full(transfer2, base_uri,
|
fakes_base._stub_transfer_full(transfer2, base_uri,
|
||||||
tenant_id)]})
|
tenant_id)]})
|
||||||
|
|
||||||
def get_volume_transfers_5678(self, **kw):
|
def get_volume_transfers_5678(self, **kw):
|
||||||
base_uri = 'http://localhost:8776'
|
base_uri = 'http://localhost:8776'
|
||||||
@ -651,7 +664,8 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient):
|
|||||||
transfer1 = '5678'
|
transfer1 = '5678'
|
||||||
return (200, {},
|
return (200, {},
|
||||||
{'transfer':
|
{'transfer':
|
||||||
fake_v2._stub_transfer_full(transfer1, base_uri, tenant_id)})
|
fakes_base._stub_transfer_full(transfer1, base_uri,
|
||||||
|
tenant_id)})
|
||||||
|
|
||||||
def delete_volume_transfers_5678(self, **kw):
|
def delete_volume_transfers_5678(self, **kw):
|
||||||
return (202, {}, None)
|
return (202, {}, None)
|
||||||
@ -661,16 +675,16 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient):
|
|||||||
tenant_id = '0fa851f6668144cf9cd8c8419c1646c1'
|
tenant_id = '0fa851f6668144cf9cd8c8419c1646c1'
|
||||||
transfer1 = '5678'
|
transfer1 = '5678'
|
||||||
return (202, {},
|
return (202, {},
|
||||||
{'transfer': fake_v2._stub_transfer(transfer1, base_uri,
|
{'transfer': fakes_base._stub_transfer(transfer1, base_uri,
|
||||||
tenant_id)})
|
tenant_id)})
|
||||||
|
|
||||||
def post_volume_transfers_5678_accept(self, **kw):
|
def post_volume_transfers_5678_accept(self, **kw):
|
||||||
base_uri = 'http://localhost:8776'
|
base_uri = 'http://localhost:8776'
|
||||||
tenant_id = '0fa851f6668144cf9cd8c8419c1646c1'
|
tenant_id = '0fa851f6668144cf9cd8c8419c1646c1'
|
||||||
transfer1 = '5678'
|
transfer1 = '5678'
|
||||||
return (200, {},
|
return (200, {},
|
||||||
{'transfer': fake_v2._stub_transfer(transfer1, base_uri,
|
{'transfer': fakes_base._stub_transfer(transfer1, base_uri,
|
||||||
tenant_id)})
|
tenant_id)})
|
||||||
|
|
||||||
|
|
||||||
def fake_request_get():
|
def fake_request_get():
|
||||||
|
@ -18,7 +18,6 @@ from urllib import parse as urlparse
|
|||||||
from cinderclient import client as base_client
|
from cinderclient import client as base_client
|
||||||
from cinderclient.tests.unit import fakes
|
from cinderclient.tests.unit import fakes
|
||||||
import cinderclient.tests.unit.utils as utils
|
import cinderclient.tests.unit.utils as utils
|
||||||
from cinderclient.v2 import client
|
|
||||||
|
|
||||||
|
|
||||||
REQUEST_ID = 'req-test-request-id'
|
REQUEST_ID = 'req-test-request-id'
|
||||||
@ -332,19 +331,6 @@ def stub_default_types():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class FakeClient(fakes.FakeClient, client.Client):
|
|
||||||
|
|
||||||
def __init__(self, api_version=None, *args, **kwargs):
|
|
||||||
client.Client.__init__(self, 'username', 'password',
|
|
||||||
'project_id', 'auth_url',
|
|
||||||
extensions=kwargs.get('extensions'))
|
|
||||||
self.api_version = api_version
|
|
||||||
self.client = FakeHTTPClient(**kwargs)
|
|
||||||
|
|
||||||
def get_volume_api_version_from_endpoint(self):
|
|
||||||
return self.client.get_volume_api_version_from_endpoint()
|
|
||||||
|
|
||||||
|
|
||||||
class FakeHTTPClient(base_client.HTTPClient):
|
class FakeHTTPClient(base_client.HTTPClient):
|
||||||
|
|
||||||
def __init__(self, version_header=None, **kwargs):
|
def __init__(self, version_header=None, **kwargs):
|
@ -21,7 +21,7 @@ import requests
|
|||||||
|
|
||||||
from cinderclient import exceptions
|
from cinderclient import exceptions
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.v2 import client
|
from cinderclient.v3 import client
|
||||||
|
|
||||||
|
|
||||||
class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
@ -13,11 +13,12 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from cinderclient import api_versions
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
from cinderclient.v2.capabilities import Capabilities
|
from cinderclient.v3.capabilities import Capabilities
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
cs = fakes.FakeClient(api_versions.APIVersion('3.0'))
|
||||||
|
|
||||||
FAKE_CAPABILITY = {
|
FAKE_CAPABILITY = {
|
||||||
'namespace': 'OS::Storage::Capabilities::fake',
|
'namespace': 'OS::Storage::Capabilities::fake',
|
@ -14,11 +14,12 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from cinderclient import api_versions
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
|
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
cs = fakes.FakeClient(api_versions.APIVersion('3.0'))
|
||||||
|
|
||||||
|
|
||||||
class cgsnapshotsTest(utils.TestCase):
|
class cgsnapshotsTest(utils.TestCase):
|
@ -14,10 +14,11 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from cinderclient import api_versions
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
cs = fakes.FakeClient(api_versions.APIVersion('3.0'))
|
||||||
|
|
||||||
|
|
||||||
class ConsistencygroupsTest(utils.TestCase):
|
class ConsistencygroupsTest(utils.TestCase):
|
@ -18,7 +18,7 @@ from unittest import mock
|
|||||||
import ddt
|
import ddt
|
||||||
|
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.v2 import limits
|
from cinderclient.v3 import limits
|
||||||
|
|
||||||
|
|
||||||
REQUEST_ID = 'req-test-request-id'
|
REQUEST_ID = 'req-test-request-id'
|
@ -13,11 +13,13 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from cinderclient import api_versions
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
from cinderclient.v2.pools import Pool
|
from cinderclient.v3.pools import Pool
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
|
||||||
|
cs = fakes.FakeClient(api_versions.APIVersion('3.0'))
|
||||||
|
|
||||||
|
|
||||||
class PoolsTest(utils.TestCase):
|
class PoolsTest(utils.TestCase):
|
@ -13,11 +13,12 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from cinderclient import api_versions
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
|
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
cs = fakes.FakeClient(api_versions.APIVersion('3.0'))
|
||||||
|
|
||||||
|
|
||||||
class QoSSpecsTest(utils.TestCase):
|
class QoSSpecsTest(utils.TestCase):
|
@ -13,11 +13,12 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from cinderclient import api_versions
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
|
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
cs = fakes.FakeClient(api_versions.APIVersion('3.0'))
|
||||||
|
|
||||||
|
|
||||||
class QuotaClassSetsTest(utils.TestCase):
|
class QuotaClassSetsTest(utils.TestCase):
|
@ -13,17 +13,78 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from cinderclient import api_versions
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v3 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
|
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
cs = fakes.FakeClient(api_versions.APIVersion('3.0'))
|
||||||
|
|
||||||
|
|
||||||
class QuotaSetsTest(utils.TestCase):
|
class QuotaSetsTest(utils.TestCase):
|
||||||
|
|
||||||
|
def test_tenant_quotas_get(self):
|
||||||
|
tenant_id = 'test'
|
||||||
|
quota = cs.quotas.get(tenant_id)
|
||||||
|
cs.assert_called('GET', '/os-quota-sets/%s?usage=False' % tenant_id)
|
||||||
|
self._assert_request_id(quota)
|
||||||
|
|
||||||
|
def test_tenant_quotas_defaults(self):
|
||||||
|
tenant_id = 'test'
|
||||||
|
quota = cs.quotas.defaults(tenant_id)
|
||||||
|
cs.assert_called('GET', '/os-quota-sets/%s/defaults' % tenant_id)
|
||||||
|
self._assert_request_id(quota)
|
||||||
|
|
||||||
|
def test_update_quota(self):
|
||||||
|
q = cs.quotas.get('test')
|
||||||
|
q.update(volumes=2)
|
||||||
|
q.update(snapshots=2)
|
||||||
|
q.update(gigabytes=2000)
|
||||||
|
q.update(backups=2)
|
||||||
|
q.update(backup_gigabytes=2000)
|
||||||
|
q.update(per_volume_gigabytes=100)
|
||||||
|
cs.assert_called('PUT', '/os-quota-sets/test')
|
||||||
|
self._assert_request_id(q)
|
||||||
|
|
||||||
def test_update_quota_with_skip_(self):
|
def test_update_quota_with_skip_(self):
|
||||||
q = cs.quotas.get('test')
|
q = cs.quotas.get('test')
|
||||||
q.update(skip_validation=False)
|
q.update(skip_validation=False)
|
||||||
cs.assert_called('PUT', '/os-quota-sets/test?skip_validation=False')
|
cs.assert_called('PUT', '/os-quota-sets/test?skip_validation=False')
|
||||||
self._assert_request_id(q)
|
self._assert_request_id(q)
|
||||||
|
|
||||||
|
def test_refresh_quota(self):
|
||||||
|
q = cs.quotas.get('test')
|
||||||
|
q2 = cs.quotas.get('test')
|
||||||
|
self.assertEqual(q.volumes, q2.volumes)
|
||||||
|
self.assertEqual(q.snapshots, q2.snapshots)
|
||||||
|
self.assertEqual(q.gigabytes, q2.gigabytes)
|
||||||
|
self.assertEqual(q.backups, q2.backups)
|
||||||
|
self.assertEqual(q.backup_gigabytes, q2.backup_gigabytes)
|
||||||
|
self.assertEqual(q.per_volume_gigabytes, q2.per_volume_gigabytes)
|
||||||
|
q2.volumes = 0
|
||||||
|
self.assertNotEqual(q.volumes, q2.volumes)
|
||||||
|
q2.snapshots = 0
|
||||||
|
self.assertNotEqual(q.snapshots, q2.snapshots)
|
||||||
|
q2.gigabytes = 0
|
||||||
|
self.assertNotEqual(q.gigabytes, q2.gigabytes)
|
||||||
|
q2.backups = 0
|
||||||
|
self.assertNotEqual(q.backups, q2.backups)
|
||||||
|
q2.backup_gigabytes = 0
|
||||||
|
self.assertNotEqual(q.backup_gigabytes, q2.backup_gigabytes)
|
||||||
|
q2.per_volume_gigabytes = 0
|
||||||
|
self.assertNotEqual(q.per_volume_gigabytes, q2.per_volume_gigabytes)
|
||||||
|
q2.get()
|
||||||
|
self.assertEqual(q.volumes, q2.volumes)
|
||||||
|
self.assertEqual(q.snapshots, q2.snapshots)
|
||||||
|
self.assertEqual(q.gigabytes, q2.gigabytes)
|
||||||
|
self.assertEqual(q.backups, q2.backups)
|
||||||
|
self.assertEqual(q.backup_gigabytes, q2.backup_gigabytes)
|
||||||
|
self.assertEqual(q.per_volume_gigabytes, q2.per_volume_gigabytes)
|
||||||
|
self._assert_request_id(q)
|
||||||
|
self._assert_request_id(q2)
|
||||||
|
|
||||||
|
def test_delete_quota(self):
|
||||||
|
tenant_id = 'test'
|
||||||
|
quota = cs.quotas.delete(tenant_id)
|
||||||
|
cs.assert_called('DELETE', '/os-quota-sets/test')
|
||||||
|
self._assert_request_id(quota)
|
||||||
|
@ -13,15 +13,17 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from cinderclient import api_versions
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
from cinderclient.v2 import services
|
from cinderclient.v3 import services
|
||||||
|
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
cs = fakes.FakeClient(api_version=api_versions.APIVersion('3.0'))
|
||||||
|
|
||||||
|
|
||||||
class ServicesTest(utils.TestCase):
|
class ServicesTest(utils.TestCase):
|
||||||
|
"""Tests for v3.0 behavior"""
|
||||||
|
|
||||||
def test_list_services(self):
|
def test_list_services(self):
|
||||||
svs = cs.services.list()
|
svs = cs.services.list()
|
@ -1745,7 +1745,7 @@ class ShellTest(utils.TestCase):
|
|||||||
)
|
)
|
||||||
@ddt.unpack
|
@ddt.unpack
|
||||||
@mock.patch('cinderclient.utils.print_dict')
|
@mock.patch('cinderclient.utils.print_dict')
|
||||||
@mock.patch('cinderclient.tests.unit.v2.fakes._stub_restore')
|
@mock.patch('cinderclient.tests.unit.v3.fakes_base._stub_restore')
|
||||||
def test_do_backup_restore(self,
|
def test_do_backup_restore(self,
|
||||||
mock_stub_restore,
|
mock_stub_restore,
|
||||||
mock_print_dict,
|
mock_print_dict,
|
||||||
|
@ -20,7 +20,7 @@ from cinderclient.tests.unit import utils
|
|||||||
|
|
||||||
class SnapshotActionsTest(utils.FixturedTestCase):
|
class SnapshotActionsTest(utils.FixturedTestCase):
|
||||||
|
|
||||||
client_fixture_class = client.V2
|
client_fixture_class = client.V3
|
||||||
data_fixture_class = snapshots.Fixture
|
data_fixture_class = snapshots.Fixture
|
||||||
|
|
||||||
def test_update_snapshot_status(self):
|
def test_update_snapshot_status(self):
|
@ -15,8 +15,8 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
from cinderclient.v2 import volume_type_access
|
from cinderclient.v3 import volume_type_access
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
cs = fakes.FakeClient()
|
||||||
|
|
@ -15,8 +15,8 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
from cinderclient.v2 import volume_types
|
from cinderclient.v3 import volume_types
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
cs = fakes.FakeClient()
|
||||||
|
|
@ -17,6 +17,7 @@ from cinderclient import api_versions
|
|||||||
from cinderclient import exceptions as exc
|
from cinderclient import exceptions as exc
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v3 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
|
from cinderclient.v3 import volume_backups_restore
|
||||||
|
|
||||||
|
|
||||||
class VolumesTest(utils.TestCase):
|
class VolumesTest(utils.TestCase):
|
||||||
@ -35,3 +36,23 @@ class VolumesTest(utils.TestCase):
|
|||||||
b = cs.backups.get('1234')
|
b = cs.backups.get('1234')
|
||||||
self.assertRaises(exc.VersionNotFoundForAPIMethod,
|
self.assertRaises(exc.VersionNotFoundForAPIMethod,
|
||||||
b.update, name='new-name')
|
b.update, name='new-name')
|
||||||
|
|
||||||
|
def test_restore(self):
|
||||||
|
cs = fakes.FakeClient(api_version=api_versions.APIVersion('3.0'))
|
||||||
|
backup_id = '76a17945-3c6f-435c-975b-b5685db10b62'
|
||||||
|
info = cs.restores.restore(backup_id)
|
||||||
|
cs.assert_called('POST', '/backups/%s/restore' % backup_id)
|
||||||
|
self.assertIsInstance(info,
|
||||||
|
volume_backups_restore.VolumeBackupsRestore)
|
||||||
|
self._assert_request_id(info)
|
||||||
|
|
||||||
|
def test_restore_with_name(self):
|
||||||
|
cs = fakes.FakeClient(api_version=api_versions.APIVersion('3.0'))
|
||||||
|
backup_id = '76a17945-3c6f-435c-975b-b5685db10b62'
|
||||||
|
name = 'restore_vol'
|
||||||
|
info = cs.restores.restore(backup_id, name=name)
|
||||||
|
expected_body = {'restore': {'volume_id': None, 'name': name}}
|
||||||
|
cs.assert_called('POST', '/backups/%s/restore' % backup_id,
|
||||||
|
body=expected_body)
|
||||||
|
self.assertIsInstance(info,
|
||||||
|
volume_backups_restore.VolumeBackupsRestore)
|
||||||
|
@ -14,8 +14,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
from cinderclient.v2 import volume_backups_restore
|
|
||||||
|
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
cs = fakes.FakeClient()
|
||||||
@ -118,24 +117,6 @@ class VolumeBackupsTest(utils.TestCase):
|
|||||||
'/backups/76a17945-3c6f-435c-975b-b5685db10b62')
|
'/backups/76a17945-3c6f-435c-975b-b5685db10b62')
|
||||||
self._assert_request_id(del_back)
|
self._assert_request_id(del_back)
|
||||||
|
|
||||||
def test_restore(self):
|
|
||||||
backup_id = '76a17945-3c6f-435c-975b-b5685db10b62'
|
|
||||||
info = cs.restores.restore(backup_id)
|
|
||||||
cs.assert_called('POST', '/backups/%s/restore' % backup_id)
|
|
||||||
self.assertIsInstance(info,
|
|
||||||
volume_backups_restore.VolumeBackupsRestore)
|
|
||||||
self._assert_request_id(info)
|
|
||||||
|
|
||||||
def test_restore_with_name(self):
|
|
||||||
backup_id = '76a17945-3c6f-435c-975b-b5685db10b62'
|
|
||||||
name = 'restore_vol'
|
|
||||||
info = cs.restores.restore(backup_id, name=name)
|
|
||||||
expected_body = {'restore': {'volume_id': None, 'name': name}}
|
|
||||||
cs.assert_called('POST', '/backups/%s/restore' % backup_id,
|
|
||||||
body=expected_body)
|
|
||||||
self.assertIsInstance(info,
|
|
||||||
volume_backups_restore.VolumeBackupsRestore)
|
|
||||||
|
|
||||||
def test_reset_state(self):
|
def test_reset_state(self):
|
||||||
b = cs.backups.list()[0]
|
b = cs.backups.list()[0]
|
||||||
api = '/backups/76a17945-3c6f-435c-975b-b5685db10b62/action'
|
api = '/backups/76a17945-3c6f-435c-975b-b5685db10b62/action'
|
@ -14,8 +14,8 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
from cinderclient.v2.volume_encryption_types import VolumeEncryptionType
|
from cinderclient.v3.volume_encryption_types import VolumeEncryptionType
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
cs = fakes.FakeClient()
|
||||||
|
|
@ -87,6 +87,25 @@ class VolumesTest(utils.TestCase):
|
|||||||
cs.assert_called('POST', '/volumes', body=expected)
|
cs.assert_called('POST', '/volumes', body=expected)
|
||||||
self._assert_request_id(vol)
|
self._assert_request_id(vol)
|
||||||
|
|
||||||
|
def test_create_volume_with_hint(self):
|
||||||
|
cs = fakes.FakeClient(api_versions.APIVersion('3.0'))
|
||||||
|
vol = cs.volumes.create(1, scheduler_hints='uuid')
|
||||||
|
expected = {'volume': {'description': None,
|
||||||
|
'availability_zone': None,
|
||||||
|
'source_volid': None,
|
||||||
|
'snapshot_id': None,
|
||||||
|
'size': 1,
|
||||||
|
'name': None,
|
||||||
|
'imageRef': None,
|
||||||
|
'volume_type': None,
|
||||||
|
'metadata': {},
|
||||||
|
'consistencygroup_id': None,
|
||||||
|
'backup_id': None,
|
||||||
|
},
|
||||||
|
'OS-SCH-HNT:scheduler_hints': 'uuid'}
|
||||||
|
cs.assert_called('POST', '/volumes', body=expected)
|
||||||
|
self._assert_request_id(vol)
|
||||||
|
|
||||||
@ddt.data((False, '/volumes/summary'),
|
@ddt.data((False, '/volumes/summary'),
|
||||||
(True, '/volumes/summary?all_tenants=True'))
|
(True, '/volumes/summary?all_tenants=True'))
|
||||||
def test_volume_summary(self, all_tenants_input):
|
def test_volume_summary(self, all_tenants_input):
|
||||||
|
@ -15,14 +15,16 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from cinderclient import api_versions
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v2 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
from cinderclient.v2.volumes import Volume
|
from cinderclient.v3.volumes import Volume
|
||||||
|
|
||||||
cs = fakes.FakeClient()
|
cs = fakes.FakeClient(api_version=api_versions.APIVersion('3.0'))
|
||||||
|
|
||||||
|
|
||||||
class VolumesTest(utils.TestCase):
|
class VolumesTest(utils.TestCase):
|
||||||
|
"""Block Storage API v3.0"""
|
||||||
|
|
||||||
def test_list_volumes_with_marker_limit(self):
|
def test_list_volumes_with_marker_limit(self):
|
||||||
lst = cs.volumes.list(marker=1234, limit=2)
|
lst = cs.volumes.list(marker=1234, limit=2)
|
||||||
@ -58,6 +60,11 @@ class VolumesTest(utils.TestCase):
|
|||||||
self._assert_request_id(volumes)
|
self._assert_request_id(volumes)
|
||||||
cs.client.osapi_max_limit = 1000
|
cs.client.osapi_max_limit = 1000
|
||||||
|
|
||||||
|
def test_create_volume(self):
|
||||||
|
vol = cs.volumes.create(1)
|
||||||
|
cs.assert_called('POST', '/volumes')
|
||||||
|
self._assert_request_id(vol)
|
||||||
|
|
||||||
def test_delete_volume(self):
|
def test_delete_volume(self):
|
||||||
v = cs.volumes.list()[0]
|
v = cs.volumes.list()[0]
|
||||||
del_v = v.delete()
|
del_v = v.delete()
|
||||||
@ -70,28 +77,6 @@ class VolumesTest(utils.TestCase):
|
|||||||
cs.assert_called('DELETE', '/volumes/1234')
|
cs.assert_called('DELETE', '/volumes/1234')
|
||||||
self._assert_request_id(del_v)
|
self._assert_request_id(del_v)
|
||||||
|
|
||||||
def test_create_volume(self):
|
|
||||||
vol = cs.volumes.create(1)
|
|
||||||
cs.assert_called('POST', '/volumes')
|
|
||||||
self._assert_request_id(vol)
|
|
||||||
|
|
||||||
def test_create_volume_with_hint(self):
|
|
||||||
vol = cs.volumes.create(1, scheduler_hints='uuid')
|
|
||||||
expected = {'volume': {'description': None,
|
|
||||||
'availability_zone': None,
|
|
||||||
'source_volid': None,
|
|
||||||
'snapshot_id': None,
|
|
||||||
'size': 1,
|
|
||||||
'name': None,
|
|
||||||
'imageRef': None,
|
|
||||||
'volume_type': None,
|
|
||||||
'metadata': {},
|
|
||||||
'consistencygroup_id': None,
|
|
||||||
},
|
|
||||||
'OS-SCH-HNT:scheduler_hints': 'uuid'}
|
|
||||||
cs.assert_called('POST', '/volumes', body=expected)
|
|
||||||
self._assert_request_id(vol)
|
|
||||||
|
|
||||||
def test_attach(self):
|
def test_attach(self):
|
||||||
v = cs.volumes.get('1234')
|
v = cs.volumes.get('1234')
|
||||||
self._assert_request_id(v)
|
self._assert_request_id(v)
|
@ -1,17 +0,0 @@
|
|||||||
# Copyright (c) 2013 OpenStack Foundation
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from cinderclient.v2.client import Client # noqa
|
|
@ -1,41 +0,0 @@
|
|||||||
# Copyright 2011-2013 OpenStack Foundation
|
|
||||||
# Copyright 2013 IBM Corp.
|
|
||||||
# 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.
|
|
||||||
"""Availability Zone interface (v2 extension)"""
|
|
||||||
|
|
||||||
from cinderclient import base
|
|
||||||
|
|
||||||
|
|
||||||
class AvailabilityZone(base.Resource):
|
|
||||||
NAME_ATTR = 'display_name'
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<AvailabilityZone: %s>" % self.zoneName
|
|
||||||
|
|
||||||
|
|
||||||
class AvailabilityZoneManager(base.ManagerWithFind):
|
|
||||||
"""Manage :class:`AvailabilityZone` resources."""
|
|
||||||
resource_class = AvailabilityZone
|
|
||||||
|
|
||||||
def list(self, detailed=False):
|
|
||||||
"""Lists all availability zones.
|
|
||||||
|
|
||||||
:rtype: list of :class:`AvailabilityZone`
|
|
||||||
"""
|
|
||||||
if detailed is True:
|
|
||||||
return self._list("/os-availability-zone/detail",
|
|
||||||
"availabilityZoneInfo")
|
|
||||||
else:
|
|
||||||
return self._list("/os-availability-zone", "availabilityZoneInfo")
|
|
@ -1,38 +0,0 @@
|
|||||||
# Copyright (c) 2015 Hitachi Data Systems, 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.
|
|
||||||
|
|
||||||
"""Capabilities interface (v2 extension)"""
|
|
||||||
|
|
||||||
from cinderclient import base
|
|
||||||
|
|
||||||
|
|
||||||
class Capabilities(base.Resource):
|
|
||||||
NAME_ATTR = 'name'
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<Capabilities: %s>" % self._info.get('namespace')
|
|
||||||
|
|
||||||
|
|
||||||
class CapabilitiesManager(base.Manager):
|
|
||||||
"""Manage :class:`Capabilities` resources."""
|
|
||||||
resource_class = Capabilities
|
|
||||||
|
|
||||||
def get(self, host):
|
|
||||||
"""Show backend volume stats and properties.
|
|
||||||
|
|
||||||
:param host: Specified backend to obtain volume stats and properties.
|
|
||||||
:rtype: :class:`Capabilities`
|
|
||||||
"""
|
|
||||||
return self._get('/capabilities/%s' % host, None)
|
|
@ -1,112 +0,0 @@
|
|||||||
# Copyright (C) 2012 - 2014 EMC Corporation.
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""cgsnapshot interface (v2 extension)."""
|
|
||||||
|
|
||||||
from cinderclient.apiclient import base as common_base
|
|
||||||
from cinderclient import base
|
|
||||||
from cinderclient import utils
|
|
||||||
|
|
||||||
|
|
||||||
class Cgsnapshot(base.Resource):
|
|
||||||
"""A cgsnapshot is snapshot of a consistency group."""
|
|
||||||
def __repr__(self):
|
|
||||||
return "<cgsnapshot: %s>" % self.id
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
"""Delete this cgsnapshot."""
|
|
||||||
return self.manager.delete(self)
|
|
||||||
|
|
||||||
def update(self, **kwargs):
|
|
||||||
"""Update the name or description for this cgsnapshot."""
|
|
||||||
return self.manager.update(self, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class CgsnapshotManager(base.ManagerWithFind):
|
|
||||||
"""Manage :class:`Cgsnapshot` resources."""
|
|
||||||
resource_class = Cgsnapshot
|
|
||||||
|
|
||||||
def create(self, consistencygroup_id, name=None, description=None,
|
|
||||||
user_id=None,
|
|
||||||
project_id=None):
|
|
||||||
"""Creates a cgsnapshot.
|
|
||||||
|
|
||||||
:param consistencygroup: Name or uuid of a consistency group
|
|
||||||
:param name: Name of the cgsnapshot
|
|
||||||
:param description: Description of the cgsnapshot
|
|
||||||
:param user_id: User id derived from context
|
|
||||||
:param project_id: Project id derived from context
|
|
||||||
:rtype: :class:`Cgsnapshot`
|
|
||||||
"""
|
|
||||||
|
|
||||||
body = {'cgsnapshot': {'consistencygroup_id': consistencygroup_id,
|
|
||||||
'name': name,
|
|
||||||
'description': description,
|
|
||||||
'user_id': user_id,
|
|
||||||
'project_id': project_id,
|
|
||||||
'status': "creating",
|
|
||||||
}}
|
|
||||||
|
|
||||||
return self._create('/cgsnapshots', body, 'cgsnapshot')
|
|
||||||
|
|
||||||
def get(self, cgsnapshot_id):
|
|
||||||
"""Get a cgsnapshot.
|
|
||||||
|
|
||||||
:param cgsnapshot_id: The ID of the cgsnapshot to get.
|
|
||||||
:rtype: :class:`Cgsnapshot`
|
|
||||||
"""
|
|
||||||
return self._get("/cgsnapshots/%s" % cgsnapshot_id, "cgsnapshot")
|
|
||||||
|
|
||||||
def list(self, detailed=True, search_opts=None):
|
|
||||||
"""Lists all cgsnapshots.
|
|
||||||
|
|
||||||
:rtype: list of :class:`Cgsnapshot`
|
|
||||||
"""
|
|
||||||
query_string = utils.build_query_param(search_opts)
|
|
||||||
|
|
||||||
detail = ""
|
|
||||||
if detailed:
|
|
||||||
detail = "/detail"
|
|
||||||
|
|
||||||
return self._list("/cgsnapshots%s%s" % (detail, query_string),
|
|
||||||
"cgsnapshots")
|
|
||||||
|
|
||||||
def delete(self, cgsnapshot):
|
|
||||||
"""Delete a cgsnapshot.
|
|
||||||
|
|
||||||
:param cgsnapshot: The :class:`Cgsnapshot` to delete.
|
|
||||||
"""
|
|
||||||
return self._delete("/cgsnapshots/%s" % base.getid(cgsnapshot))
|
|
||||||
|
|
||||||
def update(self, cgsnapshot, **kwargs):
|
|
||||||
"""Update the name or description for a cgsnapshot.
|
|
||||||
|
|
||||||
:param cgsnapshot: The :class:`Cgsnapshot` to update.
|
|
||||||
"""
|
|
||||||
if not kwargs:
|
|
||||||
return
|
|
||||||
|
|
||||||
body = {"cgsnapshot": kwargs}
|
|
||||||
|
|
||||||
return self._update("/cgsnapshots/%s" % base.getid(cgsnapshot), body)
|
|
||||||
|
|
||||||
def _action(self, action, cgsnapshot, info=None, **kwargs):
|
|
||||||
"""Perform a cgsnapshot "action."
|
|
||||||
"""
|
|
||||||
body = {action: info}
|
|
||||||
self.run_hooks('modify_body_for_action', body, **kwargs)
|
|
||||||
url = '/cgsnapshots/%s/action' % base.getid(cgsnapshot)
|
|
||||||
resp, body = self.api.client.post(url, body=body)
|
|
||||||
return common_base.TupleWithMeta((resp, body), resp)
|
|
@ -1,141 +0,0 @@
|
|||||||
# Copyright (c) 2013 OpenStack Foundation
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from cinderclient import api_versions
|
|
||||||
from cinderclient import client
|
|
||||||
from cinderclient.v2 import availability_zones
|
|
||||||
from cinderclient.v2 import capabilities
|
|
||||||
from cinderclient.v2 import cgsnapshots
|
|
||||||
from cinderclient.v2 import consistencygroups
|
|
||||||
from cinderclient.v2 import limits
|
|
||||||
from cinderclient.v2 import pools
|
|
||||||
from cinderclient.v2 import qos_specs
|
|
||||||
from cinderclient.v2 import quota_classes
|
|
||||||
from cinderclient.v2 import quotas
|
|
||||||
from cinderclient.v2 import services
|
|
||||||
from cinderclient.v2 import volume_backups
|
|
||||||
from cinderclient.v2 import volume_backups_restore
|
|
||||||
from cinderclient.v2 import volume_encryption_types
|
|
||||||
from cinderclient.v2 import volume_snapshots
|
|
||||||
from cinderclient.v2 import volume_transfers
|
|
||||||
from cinderclient.v2 import volume_type_access
|
|
||||||
from cinderclient.v2 import volume_types
|
|
||||||
from cinderclient.v2 import volumes
|
|
||||||
|
|
||||||
|
|
||||||
class Client(object):
|
|
||||||
"""Top-level object to access the OpenStack Volume API.
|
|
||||||
|
|
||||||
Create an instance with your creds::
|
|
||||||
|
|
||||||
>>> client = Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL)
|
|
||||||
|
|
||||||
Then call methods on its managers::
|
|
||||||
|
|
||||||
>>> client.volumes.list()
|
|
||||||
...
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, username=None, api_key=None, project_id=None,
|
|
||||||
auth_url='', insecure=False, timeout=None, tenant_id=None,
|
|
||||||
proxy_tenant_id=None, proxy_token=None, region_name=None,
|
|
||||||
endpoint_type='publicURL', extensions=None,
|
|
||||||
service_type='volumev2', service_name=None,
|
|
||||||
volume_service_name=None, os_endpoint=None, retries=0,
|
|
||||||
http_log_debug=False, cacert=None, cert=None,
|
|
||||||
auth_system='keystone', auth_plugin=None, session=None,
|
|
||||||
api_version=None, logger=None, **kwargs):
|
|
||||||
# FIXME(comstud): Rename the api_key argument above when we
|
|
||||||
# know it's not being used as keyword argument
|
|
||||||
password = api_key
|
|
||||||
self.version = '2.0'
|
|
||||||
self.limits = limits.LimitsManager(self)
|
|
||||||
|
|
||||||
# extensions
|
|
||||||
self.volumes = volumes.VolumeManager(self)
|
|
||||||
self.volume_snapshots = volume_snapshots.SnapshotManager(self)
|
|
||||||
self.volume_types = volume_types.VolumeTypeManager(self)
|
|
||||||
self.volume_type_access = \
|
|
||||||
volume_type_access.VolumeTypeAccessManager(self)
|
|
||||||
self.volume_encryption_types = \
|
|
||||||
volume_encryption_types.VolumeEncryptionTypeManager(self)
|
|
||||||
self.qos_specs = qos_specs.QoSSpecsManager(self)
|
|
||||||
self.quota_classes = quota_classes.QuotaClassSetManager(self)
|
|
||||||
self.quotas = quotas.QuotaSetManager(self)
|
|
||||||
self.backups = volume_backups.VolumeBackupManager(self)
|
|
||||||
self.restores = volume_backups_restore.VolumeBackupRestoreManager(self)
|
|
||||||
self.transfers = volume_transfers.VolumeTransferManager(self)
|
|
||||||
self.services = services.ServiceManager(self)
|
|
||||||
self.consistencygroups = consistencygroups.\
|
|
||||||
ConsistencygroupManager(self)
|
|
||||||
self.cgsnapshots = cgsnapshots.CgsnapshotManager(self)
|
|
||||||
self.availability_zones = \
|
|
||||||
availability_zones.AvailabilityZoneManager(self)
|
|
||||||
self.pools = pools.PoolManager(self)
|
|
||||||
self.capabilities = capabilities.CapabilitiesManager(self)
|
|
||||||
self.api_version = api_version or api_versions.APIVersion(self.version)
|
|
||||||
|
|
||||||
# Add in any extensions...
|
|
||||||
if extensions:
|
|
||||||
for extension in extensions:
|
|
||||||
if extension.manager_class:
|
|
||||||
setattr(self, extension.name,
|
|
||||||
extension.manager_class(self))
|
|
||||||
|
|
||||||
if not logger:
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
self.client = client._construct_http_client(
|
|
||||||
username=username,
|
|
||||||
password=password,
|
|
||||||
project_id=project_id,
|
|
||||||
auth_url=auth_url,
|
|
||||||
insecure=insecure,
|
|
||||||
timeout=timeout,
|
|
||||||
tenant_id=tenant_id,
|
|
||||||
proxy_tenant_id=tenant_id,
|
|
||||||
proxy_token=proxy_token,
|
|
||||||
region_name=region_name,
|
|
||||||
endpoint_type=endpoint_type,
|
|
||||||
service_type=service_type,
|
|
||||||
service_name=service_name,
|
|
||||||
volume_service_name=volume_service_name,
|
|
||||||
os_endpoint=os_endpoint,
|
|
||||||
retries=retries,
|
|
||||||
http_log_debug=http_log_debug,
|
|
||||||
cacert=cacert,
|
|
||||||
cert=cert,
|
|
||||||
auth_system=auth_system,
|
|
||||||
auth_plugin=auth_plugin,
|
|
||||||
session=session,
|
|
||||||
api_version=self.api_version,
|
|
||||||
logger=logger,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
def authenticate(self):
|
|
||||||
"""Authenticate against the server.
|
|
||||||
|
|
||||||
Normally this is called automatically when you first access the API,
|
|
||||||
but you can call this method to force authentication right now.
|
|
||||||
|
|
||||||
Returns on success; raises :exc:`exceptions.Unauthorized` if the
|
|
||||||
credentials are wrong.
|
|
||||||
"""
|
|
||||||
self.client.authenticate()
|
|
||||||
|
|
||||||
def get_volume_api_version_from_endpoint(self):
|
|
||||||
return self.client.get_volume_api_version_from_endpoint()
|
|
@ -1,149 +0,0 @@
|
|||||||
# Copyright (C) 2012 - 2014 EMC Corporation.
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""Consistencygroup interface (v2 extension)."""
|
|
||||||
|
|
||||||
from cinderclient.apiclient import base as common_base
|
|
||||||
from cinderclient import base
|
|
||||||
from cinderclient import utils
|
|
||||||
|
|
||||||
|
|
||||||
class Consistencygroup(base.Resource):
|
|
||||||
"""A Consistencygroup of volumes."""
|
|
||||||
def __repr__(self):
|
|
||||||
return "<Consistencygroup: %s>" % self.id
|
|
||||||
|
|
||||||
def delete(self, force='False'):
|
|
||||||
"""Delete this consistency group."""
|
|
||||||
return self.manager.delete(self, force)
|
|
||||||
|
|
||||||
def update(self, **kwargs):
|
|
||||||
"""Update the name or description for this consistency group."""
|
|
||||||
return self.manager.update(self, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class ConsistencygroupManager(base.ManagerWithFind):
|
|
||||||
"""Manage :class:`Consistencygroup` resources."""
|
|
||||||
resource_class = Consistencygroup
|
|
||||||
|
|
||||||
def create(self, volume_types, name=None,
|
|
||||||
description=None, user_id=None,
|
|
||||||
project_id=None, availability_zone=None):
|
|
||||||
"""Creates a consistency group.
|
|
||||||
|
|
||||||
:param name: Name of the ConsistencyGroup
|
|
||||||
:param description: Description of the ConsistencyGroup
|
|
||||||
:param volume_types: Types of volume
|
|
||||||
:param user_id: User id derived from context
|
|
||||||
:param project_id: Project id derived from context
|
|
||||||
:param availability_zone: Availability Zone to use
|
|
||||||
:rtype: :class:`Consistencygroup`
|
|
||||||
"""
|
|
||||||
|
|
||||||
body = {'consistencygroup': {'name': name,
|
|
||||||
'description': description,
|
|
||||||
'volume_types': volume_types,
|
|
||||||
'user_id': user_id,
|
|
||||||
'project_id': project_id,
|
|
||||||
'availability_zone': availability_zone,
|
|
||||||
'status': "creating",
|
|
||||||
}}
|
|
||||||
|
|
||||||
return self._create('/consistencygroups', body, 'consistencygroup')
|
|
||||||
|
|
||||||
def create_from_src(self, cgsnapshot_id, source_cgid, name=None,
|
|
||||||
description=None, user_id=None,
|
|
||||||
project_id=None):
|
|
||||||
"""Creates a consistency group from a cgsnapshot or a source CG.
|
|
||||||
|
|
||||||
:param cgsnapshot_id: UUID of a CGSnapshot
|
|
||||||
:param source_cgid: UUID of a source CG
|
|
||||||
:param name: Name of the ConsistencyGroup
|
|
||||||
:param description: Description of the ConsistencyGroup
|
|
||||||
:param user_id: User id derived from context
|
|
||||||
:param project_id: Project id derived from context
|
|
||||||
:rtype: A dictionary containing Consistencygroup metadata
|
|
||||||
"""
|
|
||||||
body = {'consistencygroup-from-src': {'name': name,
|
|
||||||
'description': description,
|
|
||||||
'cgsnapshot_id': cgsnapshot_id,
|
|
||||||
'source_cgid': source_cgid,
|
|
||||||
'user_id': user_id,
|
|
||||||
'project_id': project_id,
|
|
||||||
'status': "creating",
|
|
||||||
}}
|
|
||||||
|
|
||||||
self.run_hooks('modify_body_for_update', body,
|
|
||||||
'consistencygroup-from-src')
|
|
||||||
resp, body = self.api.client.post(
|
|
||||||
"/consistencygroups/create_from_src", body=body)
|
|
||||||
return common_base.DictWithMeta(body['consistencygroup'], resp)
|
|
||||||
|
|
||||||
def get(self, group_id):
|
|
||||||
"""Get a consistency group.
|
|
||||||
|
|
||||||
:param group_id: The ID of the consistency group to get.
|
|
||||||
:rtype: :class:`Consistencygroup`
|
|
||||||
"""
|
|
||||||
return self._get("/consistencygroups/%s" % group_id,
|
|
||||||
"consistencygroup")
|
|
||||||
|
|
||||||
def list(self, detailed=True, search_opts=None):
|
|
||||||
"""Lists all consistency groups.
|
|
||||||
|
|
||||||
:rtype: list of :class:`Consistencygroup`
|
|
||||||
"""
|
|
||||||
|
|
||||||
query_string = utils.build_query_param(search_opts)
|
|
||||||
|
|
||||||
detail = ""
|
|
||||||
if detailed:
|
|
||||||
detail = "/detail"
|
|
||||||
|
|
||||||
return self._list("/consistencygroups%s%s" % (detail, query_string),
|
|
||||||
"consistencygroups")
|
|
||||||
|
|
||||||
def delete(self, consistencygroup, force=False):
|
|
||||||
"""Delete a consistency group.
|
|
||||||
|
|
||||||
:param Consistencygroup: The :class:`Consistencygroup` to delete.
|
|
||||||
"""
|
|
||||||
body = {'consistencygroup': {'force': force}}
|
|
||||||
self.run_hooks('modify_body_for_action', body, 'consistencygroup')
|
|
||||||
url = '/consistencygroups/%s/delete' % base.getid(consistencygroup)
|
|
||||||
resp, body = self.api.client.post(url, body=body)
|
|
||||||
return common_base.TupleWithMeta((resp, body), resp)
|
|
||||||
|
|
||||||
def update(self, consistencygroup, **kwargs):
|
|
||||||
"""Update the name or description for a consistency group.
|
|
||||||
|
|
||||||
:param Consistencygroup: The :class:`Consistencygroup` to update.
|
|
||||||
"""
|
|
||||||
if not kwargs:
|
|
||||||
return
|
|
||||||
|
|
||||||
body = {"consistencygroup": kwargs}
|
|
||||||
|
|
||||||
return self._update("/consistencygroups/%s" %
|
|
||||||
base.getid(consistencygroup), body)
|
|
||||||
|
|
||||||
def _action(self, action, consistencygroup, info=None, **kwargs):
|
|
||||||
"""Perform a consistency group "action."
|
|
||||||
"""
|
|
||||||
body = {action: info}
|
|
||||||
self.run_hooks('modify_body_for_action', body, **kwargs)
|
|
||||||
url = '/consistencygroups/%s/action' % base.getid(consistencygroup)
|
|
||||||
resp, body = self.api.client.post(url, body=body)
|
|
||||||
return common_base.TupleWithMeta((resp, body), resp)
|
|
@ -1,44 +0,0 @@
|
|||||||
# Copyright (c) 2013 OpenStack Foundation
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from cinderclient import base
|
|
||||||
from cinderclient import utils
|
|
||||||
|
|
||||||
|
|
||||||
class ListExtResource(base.Resource):
|
|
||||||
@property
|
|
||||||
def summary(self):
|
|
||||||
descr = self.description.strip()
|
|
||||||
if not descr:
|
|
||||||
return '??'
|
|
||||||
lines = descr.split("\n")
|
|
||||||
if len(lines) == 1:
|
|
||||||
return lines[0]
|
|
||||||
else:
|
|
||||||
return lines[0] + "..."
|
|
||||||
|
|
||||||
|
|
||||||
class ListExtManager(base.Manager):
|
|
||||||
resource_class = ListExtResource
|
|
||||||
|
|
||||||
def show_all(self):
|
|
||||||
return self._list("/extensions", 'extensions')
|
|
||||||
|
|
||||||
|
|
||||||
def do_list_extensions(client, _args):
|
|
||||||
"""Lists all available os-api extensions."""
|
|
||||||
extensions = client.list_extensions.show_all()
|
|
||||||
fields = ["Name", "Summary", "Alias", "Updated"]
|
|
||||||
utils.print_list(extensions, fields)
|
|
@ -1,99 +0,0 @@
|
|||||||
# Copyright 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.
|
|
||||||
"""Limits interface (v2 extension)"""
|
|
||||||
|
|
||||||
from cinderclient import base
|
|
||||||
from cinderclient import utils
|
|
||||||
|
|
||||||
|
|
||||||
class Limits(base.Resource):
|
|
||||||
"""A collection of RateLimit and AbsoluteLimit objects."""
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<Limits>"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def absolute(self):
|
|
||||||
for (name, value) in list(self._info['absolute'].items()):
|
|
||||||
yield AbsoluteLimit(name, value)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rate(self):
|
|
||||||
for group in self._info['rate']:
|
|
||||||
uri = group['uri']
|
|
||||||
regex = group['regex']
|
|
||||||
for rate in group['limit']:
|
|
||||||
yield RateLimit(rate['verb'], uri, regex, rate['value'],
|
|
||||||
rate['remaining'], rate['unit'],
|
|
||||||
rate['next-available'])
|
|
||||||
|
|
||||||
|
|
||||||
class RateLimit(object):
|
|
||||||
"""Data model that represents a flattened view of a single rate limit."""
|
|
||||||
|
|
||||||
def __init__(self, verb, uri, regex, value, remain,
|
|
||||||
unit, next_available):
|
|
||||||
self.verb = verb
|
|
||||||
self.uri = uri
|
|
||||||
self.regex = regex
|
|
||||||
self.value = value
|
|
||||||
self.remain = remain
|
|
||||||
self.unit = unit
|
|
||||||
self.next_available = next_available
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return self.uri == other.uri \
|
|
||||||
and self.regex == other.regex \
|
|
||||||
and self.value == other.value \
|
|
||||||
and self.verb == other.verb \
|
|
||||||
and self.remain == other.remain \
|
|
||||||
and self.unit == other.unit \
|
|
||||||
and self.next_available == other.next_available
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<RateLimit: method=%s uri=%s>" % (self.verb, self.uri)
|
|
||||||
|
|
||||||
|
|
||||||
class AbsoluteLimit(object):
|
|
||||||
"""Data model that represents a single absolute limit."""
|
|
||||||
|
|
||||||
def __init__(self, name, value):
|
|
||||||
self.name = name
|
|
||||||
self.value = value
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return self.value == other.value and self.name == other.name
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<AbsoluteLimit: name=%s>" % (self.name)
|
|
||||||
|
|
||||||
|
|
||||||
class LimitsManager(base.Manager):
|
|
||||||
"""Manager object used to interact with limits resource."""
|
|
||||||
|
|
||||||
resource_class = Limits
|
|
||||||
|
|
||||||
def get(self, tenant_id=None):
|
|
||||||
"""Get a specific extension.
|
|
||||||
|
|
||||||
:rtype: :class:`Limits`
|
|
||||||
"""
|
|
||||||
opts = {}
|
|
||||||
if tenant_id:
|
|
||||||
opts['tenant_id'] = tenant_id
|
|
||||||
|
|
||||||
query_string = utils.build_query_param(opts)
|
|
||||||
|
|
||||||
return self._get("/limits%s" % query_string, "limits")
|
|
@ -1,60 +0,0 @@
|
|||||||
# Copyright (C) 2015 Hewlett-Packard Development Company, L.P.
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""Pools interface (v2 extension)"""
|
|
||||||
|
|
||||||
from cinderclient import base
|
|
||||||
|
|
||||||
|
|
||||||
class Pool(base.Resource):
|
|
||||||
NAME_ATTR = 'name'
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<Pool: %s>" % self.name
|
|
||||||
|
|
||||||
|
|
||||||
class PoolManager(base.Manager):
|
|
||||||
"""Manage :class:`Pool` resources."""
|
|
||||||
resource_class = Pool
|
|
||||||
|
|
||||||
def list(self, detailed=False):
|
|
||||||
"""Lists all
|
|
||||||
|
|
||||||
:rtype: list of :class:`Pool`
|
|
||||||
"""
|
|
||||||
if detailed is True:
|
|
||||||
pools = self._list("/scheduler-stats/get_pools?detail=True",
|
|
||||||
"pools")
|
|
||||||
# Other than the name, all of the pool data is buried below in
|
|
||||||
# a 'capabilities' dictionary. In order to be consistent with the
|
|
||||||
# get-pools command line, these elements are moved up a level to
|
|
||||||
# be attributes of the pool itself.
|
|
||||||
for pool in pools:
|
|
||||||
if hasattr(pool, 'capabilities'):
|
|
||||||
for k, v in pool.capabilities.items():
|
|
||||||
setattr(pool, k, v)
|
|
||||||
|
|
||||||
# Remove the capabilities dictionary since all of its
|
|
||||||
# elements have been copied up to the containing pool
|
|
||||||
del pool.capabilities
|
|
||||||
return pools
|
|
||||||
else:
|
|
||||||
pools = self._list("/scheduler-stats/get_pools", "pools")
|
|
||||||
|
|
||||||
# avoid cluttering the basic pool list with capabilities dict
|
|
||||||
for pool in pools:
|
|
||||||
if hasattr(pool, 'capabilities'):
|
|
||||||
del pool.capabilities
|
|
||||||
return pools
|
|
@ -1,155 +0,0 @@
|
|||||||
# Copyright (c) 2013 eBay Inc.
|
|
||||||
# Copyright (c) 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
QoS Specs interface.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from cinderclient.apiclient import base as common_base
|
|
||||||
from cinderclient import base
|
|
||||||
|
|
||||||
|
|
||||||
class QoSSpecs(base.Resource):
|
|
||||||
"""QoS specs entity represents quality-of-service parameters/requirements.
|
|
||||||
|
|
||||||
A QoS specs is a set of parameters or requirements for quality-of-service
|
|
||||||
purpose, which can be associated with volume types (for now). In future,
|
|
||||||
QoS specs may be extended to be associated other entities, such as single
|
|
||||||
volume.
|
|
||||||
"""
|
|
||||||
def __repr__(self):
|
|
||||||
return "<QoSSpecs: %s>" % self.name
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
return self.manager.delete(self)
|
|
||||||
|
|
||||||
|
|
||||||
class QoSSpecsManager(base.ManagerWithFind):
|
|
||||||
"""
|
|
||||||
Manage :class:`QoSSpecs` resources.
|
|
||||||
"""
|
|
||||||
resource_class = QoSSpecs
|
|
||||||
|
|
||||||
def list(self, search_opts=None):
|
|
||||||
"""Get a list of all qos specs.
|
|
||||||
|
|
||||||
:rtype: list of :class:`QoSSpecs`.
|
|
||||||
"""
|
|
||||||
return self._list("/qos-specs", "qos_specs")
|
|
||||||
|
|
||||||
def get(self, qos_specs):
|
|
||||||
"""Get a specific qos specs.
|
|
||||||
|
|
||||||
:param qos_specs: The ID of the :class:`QoSSpecs` to get.
|
|
||||||
:rtype: :class:`QoSSpecs`
|
|
||||||
"""
|
|
||||||
return self._get("/qos-specs/%s" % base.getid(qos_specs), "qos_specs")
|
|
||||||
|
|
||||||
def delete(self, qos_specs, force=False):
|
|
||||||
"""Delete a specific qos specs.
|
|
||||||
|
|
||||||
:param qos_specs: The ID of the :class:`QoSSpecs` to be removed.
|
|
||||||
:param force: Flag that indicates whether to delete target qos specs
|
|
||||||
if it was in-use.
|
|
||||||
"""
|
|
||||||
return self._delete("/qos-specs/%s?force=%s" %
|
|
||||||
(base.getid(qos_specs), force))
|
|
||||||
|
|
||||||
def create(self, name, specs):
|
|
||||||
"""Create a qos specs.
|
|
||||||
|
|
||||||
:param name: Descriptive name of the qos specs, must be unique
|
|
||||||
:param specs: A dict of key/value pairs to be set
|
|
||||||
:rtype: :class:`QoSSpecs`
|
|
||||||
"""
|
|
||||||
|
|
||||||
body = {
|
|
||||||
"qos_specs": {
|
|
||||||
"name": name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body["qos_specs"].update(specs)
|
|
||||||
return self._create("/qos-specs", body, "qos_specs")
|
|
||||||
|
|
||||||
def set_keys(self, qos_specs, specs):
|
|
||||||
"""Add/Update keys in qos specs.
|
|
||||||
|
|
||||||
:param qos_specs: The ID of qos specs
|
|
||||||
:param specs: A dict of key/value pairs to be set
|
|
||||||
:rtype: :class:`QoSSpecs`
|
|
||||||
"""
|
|
||||||
|
|
||||||
body = {
|
|
||||||
"qos_specs": {}
|
|
||||||
}
|
|
||||||
|
|
||||||
body["qos_specs"].update(specs)
|
|
||||||
return self._update("/qos-specs/%s" % qos_specs, body)
|
|
||||||
|
|
||||||
def unset_keys(self, qos_specs, specs):
|
|
||||||
"""Remove keys from a qos specs.
|
|
||||||
|
|
||||||
:param qos_specs: The ID of qos specs
|
|
||||||
:param specs: A list of key to be unset
|
|
||||||
:rtype: :class:`QoSSpecs`
|
|
||||||
"""
|
|
||||||
|
|
||||||
body = {'keys': specs}
|
|
||||||
|
|
||||||
return self._update("/qos-specs/%s/delete_keys" % qos_specs,
|
|
||||||
body)
|
|
||||||
|
|
||||||
def get_associations(self, qos_specs):
|
|
||||||
"""Get associated entities of a qos specs.
|
|
||||||
|
|
||||||
:param qos_specs: The id of the :class: `QoSSpecs`
|
|
||||||
:return: a list of entities that associated with specific qos specs.
|
|
||||||
"""
|
|
||||||
return self._list("/qos-specs/%s/associations" % base.getid(qos_specs),
|
|
||||||
"qos_associations")
|
|
||||||
|
|
||||||
def associate(self, qos_specs, vol_type_id):
|
|
||||||
"""Associate a volume type with specific qos specs.
|
|
||||||
|
|
||||||
:param qos_specs: The qos specs to be associated with
|
|
||||||
:param vol_type_id: The volume type id to be associated with
|
|
||||||
"""
|
|
||||||
resp, body = self.api.client.get(
|
|
||||||
"/qos-specs/%s/associate?vol_type_id=%s" %
|
|
||||||
(base.getid(qos_specs), vol_type_id))
|
|
||||||
return common_base.TupleWithMeta((resp, body), resp)
|
|
||||||
|
|
||||||
def disassociate(self, qos_specs, vol_type_id):
|
|
||||||
"""Disassociate qos specs from volume type.
|
|
||||||
|
|
||||||
:param qos_specs: The qos specs to be associated with
|
|
||||||
:param vol_type_id: The volume type id to be associated with
|
|
||||||
"""
|
|
||||||
resp, body = self.api.client.get(
|
|
||||||
"/qos-specs/%s/disassociate?vol_type_id=%s" %
|
|
||||||
(base.getid(qos_specs), vol_type_id))
|
|
||||||
return common_base.TupleWithMeta((resp, body), resp)
|
|
||||||
|
|
||||||
def disassociate_all(self, qos_specs):
|
|
||||||
"""Disassociate all entities from specific qos specs.
|
|
||||||
|
|
||||||
:param qos_specs: The qos specs to be associated with
|
|
||||||
"""
|
|
||||||
resp, body = self.api.client.get(
|
|
||||||
"/qos-specs/%s/disassociate_all" %
|
|
||||||
base.getid(qos_specs))
|
|
||||||
return common_base.TupleWithMeta((resp, body), resp)
|
|
@ -1,47 +0,0 @@
|
|||||||
# Copyright (c) 2013 OpenStack Foundation
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from cinderclient import base
|
|
||||||
|
|
||||||
|
|
||||||
class QuotaClassSet(base.Resource):
|
|
||||||
|
|
||||||
@property
|
|
||||||
def id(self):
|
|
||||||
"""Needed by base.Resource to self-refresh and be indexed."""
|
|
||||||
return self.class_name
|
|
||||||
|
|
||||||
def update(self, *args, **kwargs):
|
|
||||||
return self.manager.update(self.class_name, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class QuotaClassSetManager(base.Manager):
|
|
||||||
resource_class = QuotaClassSet
|
|
||||||
|
|
||||||
def get(self, class_name):
|
|
||||||
return self._get("/os-quota-class-sets/%s" % (class_name),
|
|
||||||
"quota_class_set")
|
|
||||||
|
|
||||||
def update(self, class_name, **updates):
|
|
||||||
quota_class_set = {}
|
|
||||||
|
|
||||||
for update in updates:
|
|
||||||
quota_class_set[update] = updates[update]
|
|
||||||
|
|
||||||
result = self._update('/os-quota-class-sets/%s' % (class_name),
|
|
||||||
{'quota_class_set': quota_class_set})
|
|
||||||
return self.resource_class(self,
|
|
||||||
result['quota_class_set'], loaded=True,
|
|
||||||
resp=result.request_ids)
|
|
@ -1,56 +0,0 @@
|
|||||||
# Copyright (c) 2013 OpenStack Foundation
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from cinderclient import base
|
|
||||||
|
|
||||||
|
|
||||||
class QuotaSet(base.Resource):
|
|
||||||
|
|
||||||
@property
|
|
||||||
def id(self):
|
|
||||||
"""Needed by base.Resource to self-refresh and be indexed."""
|
|
||||||
return self.tenant_id
|
|
||||||
|
|
||||||
def update(self, *args, **kwargs):
|
|
||||||
return self.manager.update(self.tenant_id, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class QuotaSetManager(base.Manager):
|
|
||||||
resource_class = QuotaSet
|
|
||||||
|
|
||||||
def get(self, tenant_id, usage=False):
|
|
||||||
if hasattr(tenant_id, 'tenant_id'):
|
|
||||||
tenant_id = tenant_id.tenant_id
|
|
||||||
return self._get("/os-quota-sets/%s?usage=%s" % (tenant_id, usage),
|
|
||||||
"quota_set")
|
|
||||||
|
|
||||||
def update(self, tenant_id, **updates):
|
|
||||||
body = {'quota_set': {'tenant_id': tenant_id}}
|
|
||||||
|
|
||||||
for update in updates:
|
|
||||||
body['quota_set'][update] = updates[update]
|
|
||||||
|
|
||||||
result = self._update('/os-quota-sets/%s' % (tenant_id), body)
|
|
||||||
return self.resource_class(self, result['quota_set'], loaded=True,
|
|
||||||
resp=result.request_ids)
|
|
||||||
|
|
||||||
def defaults(self, tenant_id):
|
|
||||||
return self._get('/os-quota-sets/%s/defaults' % tenant_id,
|
|
||||||
'quota_set')
|
|
||||||
|
|
||||||
def delete(self, tenant_id):
|
|
||||||
if hasattr(tenant_id, 'tenant_id'):
|
|
||||||
tenant_id = tenant_id.tenant_id
|
|
||||||
return self._delete("/os-quota-sets/%s" % tenant_id)
|
|
@ -1,80 +0,0 @@
|
|||||||
# Copyright (c) 2013 OpenStack Foundation
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
service interface
|
|
||||||
"""
|
|
||||||
|
|
||||||
from cinderclient import base
|
|
||||||
|
|
||||||
|
|
||||||
class Service(base.Resource):
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<Service: binary=%s host=%s>" % (self.binary, self.host)
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceManager(base.ManagerWithFind):
|
|
||||||
resource_class = Service
|
|
||||||
|
|
||||||
def list(self, host=None, binary=None):
|
|
||||||
"""
|
|
||||||
Describes service list for host.
|
|
||||||
|
|
||||||
:param host: destination host name.
|
|
||||||
:param binary: service binary.
|
|
||||||
"""
|
|
||||||
url = "/os-services"
|
|
||||||
filters = []
|
|
||||||
if host:
|
|
||||||
filters.append("host=%s" % host)
|
|
||||||
if binary:
|
|
||||||
filters.append("binary=%s" % binary)
|
|
||||||
if filters:
|
|
||||||
url = "%s?%s" % (url, "&".join(filters))
|
|
||||||
return self._list(url, "services")
|
|
||||||
|
|
||||||
def enable(self, host, binary):
|
|
||||||
"""Enable the service specified by hostname and binary."""
|
|
||||||
body = {"host": host, "binary": binary}
|
|
||||||
result = self._update("/os-services/enable", body)
|
|
||||||
return self.resource_class(self, result, resp=result.request_ids)
|
|
||||||
|
|
||||||
def disable(self, host, binary):
|
|
||||||
"""Disable the service specified by hostname and binary."""
|
|
||||||
body = {"host": host, "binary": binary}
|
|
||||||
result = self._update("/os-services/disable", body)
|
|
||||||
return self.resource_class(self, result, resp=result.request_ids)
|
|
||||||
|
|
||||||
def disable_log_reason(self, host, binary, reason):
|
|
||||||
"""Disable the service with reason."""
|
|
||||||
body = {"host": host, "binary": binary, "disabled_reason": reason}
|
|
||||||
result = self._update("/os-services/disable-log-reason", body)
|
|
||||||
return self.resource_class(self, result, resp=result.request_ids)
|
|
||||||
|
|
||||||
def freeze_host(self, host):
|
|
||||||
"""Freeze the service specified by hostname."""
|
|
||||||
body = {"host": host}
|
|
||||||
return self._update("/os-services/freeze", body)
|
|
||||||
|
|
||||||
def thaw_host(self, host):
|
|
||||||
"""Thaw the service specified by hostname."""
|
|
||||||
body = {"host": host}
|
|
||||||
return self._update("/os-services/thaw", body)
|
|
||||||
|
|
||||||
def failover_host(self, host, backend_id):
|
|
||||||
"""Failover a replicated backend by hostname."""
|
|
||||||
body = {"host": host, "backend_id": backend_id}
|
|
||||||
return self._update("/os-services/failover_host", body)
|
|
@ -1,137 +0,0 @@
|
|||||||
# Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Volume Backups interface (v2 extension).
|
|
||||||
"""
|
|
||||||
|
|
||||||
from cinderclient.apiclient import base as common_base
|
|
||||||
from cinderclient import base
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeBackup(base.Resource):
|
|
||||||
"""A volume backup is a block level backup of a volume."""
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<VolumeBackup: %s>" % self.id
|
|
||||||
|
|
||||||
def delete(self, force=False):
|
|
||||||
"""Delete this volume backup."""
|
|
||||||
return self.manager.delete(self, force)
|
|
||||||
|
|
||||||
def reset_state(self, state):
|
|
||||||
return self.manager.reset_state(self, state)
|
|
||||||
|
|
||||||
def update(self, **kwargs):
|
|
||||||
"""Update the name or description for this backup."""
|
|
||||||
return self.manager.update(self, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeBackupManager(base.ManagerWithFind):
|
|
||||||
"""Manage :class:`VolumeBackup` resources."""
|
|
||||||
resource_class = VolumeBackup
|
|
||||||
|
|
||||||
def create(self, volume_id, container=None,
|
|
||||||
name=None, description=None,
|
|
||||||
incremental=False, force=False,
|
|
||||||
snapshot_id=None):
|
|
||||||
"""Creates a volume backup.
|
|
||||||
|
|
||||||
:param volume_id: The ID of the volume to backup.
|
|
||||||
:param container: The name of the backup service container.
|
|
||||||
:param name: The name of the backup.
|
|
||||||
:param description: The description of the backup.
|
|
||||||
:param incremental: Incremental backup.
|
|
||||||
:param force: If True, allows an in-use volume to be backed up.
|
|
||||||
:param snapshot_id: The ID of the snapshot to backup. This should
|
|
||||||
be a snapshot of the src volume, when specified,
|
|
||||||
the new backup will be based on the snapshot.
|
|
||||||
:rtype: :class:`VolumeBackup`
|
|
||||||
"""
|
|
||||||
body = {'backup': {'volume_id': volume_id,
|
|
||||||
'container': container,
|
|
||||||
'name': name,
|
|
||||||
'description': description,
|
|
||||||
'incremental': incremental,
|
|
||||||
'force': force,
|
|
||||||
'snapshot_id': snapshot_id, }}
|
|
||||||
return self._create('/backups', body, 'backup')
|
|
||||||
|
|
||||||
def get(self, backup_id):
|
|
||||||
"""Show volume backup details.
|
|
||||||
|
|
||||||
:param backup_id: The ID of the backup to display.
|
|
||||||
:rtype: :class:`VolumeBackup`
|
|
||||||
"""
|
|
||||||
return self._get("/backups/%s" % backup_id, "backup")
|
|
||||||
|
|
||||||
def list(self, detailed=True, search_opts=None, marker=None, limit=None,
|
|
||||||
sort=None):
|
|
||||||
"""Get a list of all volume backups.
|
|
||||||
|
|
||||||
:rtype: list of :class:`VolumeBackup`
|
|
||||||
"""
|
|
||||||
resource_type = "backups"
|
|
||||||
url = self._build_list_url(resource_type, detailed=detailed,
|
|
||||||
search_opts=search_opts, marker=marker,
|
|
||||||
limit=limit, sort=sort)
|
|
||||||
return self._list(url, resource_type, limit=limit)
|
|
||||||
|
|
||||||
def delete(self, backup, force=False):
|
|
||||||
"""Delete a volume backup.
|
|
||||||
|
|
||||||
:param backup: The :class:`VolumeBackup` to delete.
|
|
||||||
:param force: Allow delete in state other than error or available.
|
|
||||||
"""
|
|
||||||
if force:
|
|
||||||
return self._action('os-force_delete', backup)
|
|
||||||
else:
|
|
||||||
return self._delete("/backups/%s" % base.getid(backup))
|
|
||||||
|
|
||||||
def reset_state(self, backup, state):
|
|
||||||
"""Update the specified volume backup with the provided state."""
|
|
||||||
return self._action('os-reset_status', backup,
|
|
||||||
{'status': state} if state else {})
|
|
||||||
|
|
||||||
def _action(self, action, backup, info=None, **kwargs):
|
|
||||||
"""Perform a volume backup action."""
|
|
||||||
body = {action: info}
|
|
||||||
self.run_hooks('modify_body_for_action', body, **kwargs)
|
|
||||||
url = '/backups/%s/action' % base.getid(backup)
|
|
||||||
resp, body = self.api.client.post(url, body=body)
|
|
||||||
return common_base.TupleWithMeta((resp, body), resp)
|
|
||||||
|
|
||||||
def export_record(self, backup_id):
|
|
||||||
"""Export volume backup metadata record.
|
|
||||||
|
|
||||||
:param backup_id: The ID of the backup to export.
|
|
||||||
:rtype: A dictionary containing 'backup_url' and 'backup_service'.
|
|
||||||
"""
|
|
||||||
resp, body = \
|
|
||||||
self.api.client.get("/backups/%s/export_record" % backup_id)
|
|
||||||
return common_base.DictWithMeta(body['backup-record'], resp)
|
|
||||||
|
|
||||||
def import_record(self, backup_service, backup_url):
|
|
||||||
"""Import volume backup metadata record.
|
|
||||||
|
|
||||||
:param backup_service: Backup service to use for importing the backup
|
|
||||||
:param backup_url: Backup URL for importing the backup metadata
|
|
||||||
:rtype: A dictionary containing volume backup metadata.
|
|
||||||
"""
|
|
||||||
body = {'backup-record': {'backup_service': backup_service,
|
|
||||||
'backup_url': backup_url}}
|
|
||||||
self.run_hooks('modify_body_for_update', body, 'backup-record')
|
|
||||||
resp, body = self.api.client.post("/backups/import_record", body=body)
|
|
||||||
return common_base.DictWithMeta(body['backup'], resp)
|
|
@ -1,44 +0,0 @@
|
|||||||
# Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""Volume Backups Restore interface (v2 extension).
|
|
||||||
|
|
||||||
This is part of the Volume Backups interface.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from cinderclient import base
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeBackupsRestore(base.Resource):
|
|
||||||
"""A Volume Backups Restore represents a restore operation."""
|
|
||||||
def __repr__(self):
|
|
||||||
return "<VolumeBackupsRestore: %s>" % self.volume_id
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeBackupRestoreManager(base.Manager):
|
|
||||||
"""Manage :class:`VolumeBackupsRestore` resources."""
|
|
||||||
resource_class = VolumeBackupsRestore
|
|
||||||
|
|
||||||
def restore(self, backup_id, volume_id=None, name=None):
|
|
||||||
"""Restore a backup to a volume.
|
|
||||||
|
|
||||||
:param backup_id: The ID of the backup to restore.
|
|
||||||
:param volume_id: The ID of the volume to restore the backup to.
|
|
||||||
:param name : The name for new volume creation to restore.
|
|
||||||
:rtype: :class:`Restore`
|
|
||||||
"""
|
|
||||||
body = {'restore': {'volume_id': volume_id, 'name': name}}
|
|
||||||
return self._create("/backups/%s/restore" % backup_id,
|
|
||||||
body, "restore")
|
|
@ -1,104 +0,0 @@
|
|||||||
# Copyright (c) 2013 The Johns Hopkins University/Applied Physics Laboratory
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Volume Encryption Type interface
|
|
||||||
"""
|
|
||||||
|
|
||||||
from cinderclient.apiclient import base as common_base
|
|
||||||
from cinderclient import base
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeEncryptionType(base.Resource):
|
|
||||||
"""
|
|
||||||
A Volume Encryption Type is a collection of settings used to conduct
|
|
||||||
encryption for a specific volume type.
|
|
||||||
"""
|
|
||||||
def __repr__(self):
|
|
||||||
return "<VolumeEncryptionType: %s>" % self.encryption_id
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeEncryptionTypeManager(base.ManagerWithFind):
|
|
||||||
"""
|
|
||||||
Manage :class: `VolumeEncryptionType` resources.
|
|
||||||
"""
|
|
||||||
resource_class = VolumeEncryptionType
|
|
||||||
|
|
||||||
def list(self, search_opts=None):
|
|
||||||
"""
|
|
||||||
List all volume encryption types.
|
|
||||||
|
|
||||||
:param search_opts: Search options to filter out volume
|
|
||||||
encryption types
|
|
||||||
:return: a list of :class: VolumeEncryptionType instances
|
|
||||||
"""
|
|
||||||
# Since the encryption type is a volume type extension, we cannot get
|
|
||||||
# all encryption types without going through all volume types.
|
|
||||||
volume_types = self.api.volume_types.list()
|
|
||||||
encryption_types = []
|
|
||||||
list_of_resp = []
|
|
||||||
for volume_type in volume_types:
|
|
||||||
encryption_type = self._get("/types/%s/encryption"
|
|
||||||
% base.getid(volume_type))
|
|
||||||
if hasattr(encryption_type, 'volume_type_id'):
|
|
||||||
encryption_types.append(encryption_type)
|
|
||||||
|
|
||||||
list_of_resp.extend(encryption_type.request_ids)
|
|
||||||
|
|
||||||
return common_base.ListWithMeta(encryption_types, list_of_resp)
|
|
||||||
|
|
||||||
def get(self, volume_type):
|
|
||||||
"""
|
|
||||||
Get the volume encryption type for the specified volume type.
|
|
||||||
|
|
||||||
:param volume_type: the volume type to query
|
|
||||||
:return: an instance of :class: VolumeEncryptionType
|
|
||||||
"""
|
|
||||||
return self._get("/types/%s/encryption" % base.getid(volume_type))
|
|
||||||
|
|
||||||
def create(self, volume_type, specs):
|
|
||||||
"""
|
|
||||||
Creates encryption type for a volume type. Default: admin only.
|
|
||||||
|
|
||||||
:param volume_type: the volume type on which to add an encryption type
|
|
||||||
:param specs: the encryption type specifications to add
|
|
||||||
:return: an instance of :class: VolumeEncryptionType
|
|
||||||
"""
|
|
||||||
body = {'encryption': specs}
|
|
||||||
return self._create("/types/%s/encryption" % base.getid(volume_type),
|
|
||||||
body, "encryption")
|
|
||||||
|
|
||||||
def update(self, volume_type, specs):
|
|
||||||
"""
|
|
||||||
Update the encryption type information for the specified volume type.
|
|
||||||
|
|
||||||
:param volume_type: the volume type whose encryption type information
|
|
||||||
must be updated
|
|
||||||
:param specs: the encryption type specifications to update
|
|
||||||
:return: an instance of :class: VolumeEncryptionType
|
|
||||||
"""
|
|
||||||
body = {'encryption': specs}
|
|
||||||
return self._update("/types/%s/encryption/provider" %
|
|
||||||
base.getid(volume_type), body)
|
|
||||||
|
|
||||||
def delete(self, volume_type):
|
|
||||||
"""
|
|
||||||
Delete the encryption type information for the specified volume type.
|
|
||||||
|
|
||||||
:param volume_type: the volume type whose encryption type information
|
|
||||||
must be deleted
|
|
||||||
"""
|
|
||||||
return self._delete("/types/%s/encryption/provider" %
|
|
||||||
base.getid(volume_type))
|
|
@ -1,39 +0,0 @@
|
|||||||
# Copyright (c) 2013 OpenStack Foundation
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""Volume snapshot interface (v2 extension)."""
|
|
||||||
|
|
||||||
from cinderclient import api_versions
|
|
||||||
from cinderclient.v3 import volume_snapshots
|
|
||||||
|
|
||||||
|
|
||||||
class Snapshot(volume_snapshots.Snapshot):
|
|
||||||
def list_manageable(self, host, detailed=True, marker=None, limit=None,
|
|
||||||
offset=None, sort=None):
|
|
||||||
return self.manager.list_manageable(host, detailed=detailed,
|
|
||||||
marker=marker, limit=limit,
|
|
||||||
offset=offset, sort=sort)
|
|
||||||
|
|
||||||
|
|
||||||
class SnapshotManager(volume_snapshots.SnapshotManager):
|
|
||||||
resource_class = Snapshot
|
|
||||||
|
|
||||||
@api_versions.wraps("2.0")
|
|
||||||
def list_manageable(self, host, detailed=True, marker=None, limit=None,
|
|
||||||
offset=None, sort=None):
|
|
||||||
url = self._build_list_url("os-snapshot-manage", detailed=detailed,
|
|
||||||
search_opts={'host': host}, marker=marker,
|
|
||||||
limit=limit, offset=offset, sort=sort)
|
|
||||||
return self._list(url, "manageable-snapshots")
|
|
@ -1,88 +0,0 @@
|
|||||||
# Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Volume transfer interface (v2 extension).
|
|
||||||
"""
|
|
||||||
|
|
||||||
from cinderclient import base
|
|
||||||
from cinderclient import utils
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeTransfer(base.Resource):
|
|
||||||
"""Transfer a volume from one tenant to another"""
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<VolumeTransfer: %s>" % self.id
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
"""Delete this volume transfer."""
|
|
||||||
return self.manager.delete(self)
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeTransferManager(base.ManagerWithFind):
|
|
||||||
"""Manage :class:`VolumeTransfer` resources."""
|
|
||||||
resource_class = VolumeTransfer
|
|
||||||
|
|
||||||
def create(self, volume_id, name=None):
|
|
||||||
"""Creates a volume transfer.
|
|
||||||
|
|
||||||
:param volume_id: The ID of the volume to transfer.
|
|
||||||
:param name: The name of the transfer.
|
|
||||||
:rtype: :class:`VolumeTransfer`
|
|
||||||
"""
|
|
||||||
body = {'transfer': {'volume_id': volume_id,
|
|
||||||
'name': name}}
|
|
||||||
return self._create('/os-volume-transfer', body, 'transfer')
|
|
||||||
|
|
||||||
def accept(self, transfer_id, auth_key):
|
|
||||||
"""Accept a volume transfer.
|
|
||||||
|
|
||||||
:param transfer_id: The ID of the transfer to accept.
|
|
||||||
:param auth_key: The auth_key of the transfer.
|
|
||||||
:rtype: :class:`VolumeTransfer`
|
|
||||||
"""
|
|
||||||
body = {'accept': {'auth_key': auth_key}}
|
|
||||||
return self._create('/os-volume-transfer/%s/accept' % transfer_id,
|
|
||||||
body, 'transfer')
|
|
||||||
|
|
||||||
def get(self, transfer_id):
|
|
||||||
"""Show details of a volume transfer.
|
|
||||||
|
|
||||||
:param transfer_id: The ID of the volume transfer to display.
|
|
||||||
:rtype: :class:`VolumeTransfer`
|
|
||||||
"""
|
|
||||||
return self._get("/os-volume-transfer/%s" % transfer_id, "transfer")
|
|
||||||
|
|
||||||
def list(self, detailed=True, search_opts=None):
|
|
||||||
"""Get a list of all volume transfer.
|
|
||||||
|
|
||||||
:rtype: list of :class:`VolumeTransfer`
|
|
||||||
"""
|
|
||||||
query_string = utils.build_query_param(search_opts)
|
|
||||||
|
|
||||||
detail = ""
|
|
||||||
if detailed:
|
|
||||||
detail = "/detail"
|
|
||||||
|
|
||||||
return self._list("/os-volume-transfer%s%s" % (detail, query_string),
|
|
||||||
"transfers")
|
|
||||||
|
|
||||||
def delete(self, transfer_id):
|
|
||||||
"""Delete a volume transfer.
|
|
||||||
|
|
||||||
:param transfer_id: The :class:`VolumeTransfer` to delete.
|
|
||||||
"""
|
|
||||||
return self._delete("/os-volume-transfer/%s" % base.getid(transfer_id))
|
|
@ -1,53 +0,0 @@
|
|||||||
# Copyright 2014 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.
|
|
||||||
|
|
||||||
"""Volume type access interface."""
|
|
||||||
|
|
||||||
from cinderclient.apiclient import base as common_base
|
|
||||||
from cinderclient import base
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeTypeAccess(base.Resource):
|
|
||||||
def __repr__(self):
|
|
||||||
return "<VolumeTypeAccess: %s>" % self.project_id
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeTypeAccessManager(base.ManagerWithFind):
|
|
||||||
"""
|
|
||||||
Manage :class:`VolumeTypeAccess` resources.
|
|
||||||
"""
|
|
||||||
resource_class = VolumeTypeAccess
|
|
||||||
|
|
||||||
def list(self, volume_type):
|
|
||||||
return self._list(
|
|
||||||
'/types/%s/os-volume-type-access' % base.getid(volume_type),
|
|
||||||
'volume_type_access')
|
|
||||||
|
|
||||||
def add_project_access(self, volume_type, project):
|
|
||||||
"""Add a project to the given volume type access list."""
|
|
||||||
info = {'project': project}
|
|
||||||
return self._action('addProjectAccess', volume_type, info)
|
|
||||||
|
|
||||||
def remove_project_access(self, volume_type, project):
|
|
||||||
"""Remove a project from the given volume type access list."""
|
|
||||||
info = {'project': project}
|
|
||||||
return self._action('removeProjectAccess', volume_type, info)
|
|
||||||
|
|
||||||
def _action(self, action, volume_type, info, **kwargs):
|
|
||||||
"""Perform a volume type action."""
|
|
||||||
body = {action: info}
|
|
||||||
self.run_hooks('modify_body_for_action', body, **kwargs)
|
|
||||||
url = '/types/%s/action' % base.getid(volume_type)
|
|
||||||
resp, body = self.api.client.post(url, body=body)
|
|
||||||
return common_base.TupleWithMeta((resp, body), resp)
|
|
@ -1,153 +0,0 @@
|
|||||||
# Copyright (c) 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.
|
|
||||||
|
|
||||||
"""Volume Type interface."""
|
|
||||||
|
|
||||||
from cinderclient.apiclient import base as common_base
|
|
||||||
from cinderclient import base
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeType(base.Resource):
|
|
||||||
"""A Volume Type is the type of volume to be created."""
|
|
||||||
def __repr__(self):
|
|
||||||
return "<VolumeType: %s>" % self.name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_public(self):
|
|
||||||
"""
|
|
||||||
Provide a user-friendly accessor to os-volume-type-access:is_public
|
|
||||||
"""
|
|
||||||
return self._info.get("os-volume-type-access:is_public",
|
|
||||||
self._info.get("is_public", 'N/A'))
|
|
||||||
|
|
||||||
def get_keys(self):
|
|
||||||
"""Get extra specs from a volume type.
|
|
||||||
|
|
||||||
:param vol_type: The :class:`VolumeType` to get extra specs from
|
|
||||||
"""
|
|
||||||
_resp, body = self.manager.api.client.get(
|
|
||||||
"/types/%s/extra_specs" %
|
|
||||||
base.getid(self))
|
|
||||||
return body["extra_specs"]
|
|
||||||
|
|
||||||
def set_keys(self, metadata):
|
|
||||||
"""Set extra specs on a volume type.
|
|
||||||
|
|
||||||
:param type : The :class:`VolumeType` to set extra spec on
|
|
||||||
:param metadata: A dict of key/value pairs to be set
|
|
||||||
"""
|
|
||||||
body = {'extra_specs': metadata}
|
|
||||||
return self.manager._create(
|
|
||||||
"/types/%s/extra_specs" % base.getid(self),
|
|
||||||
body,
|
|
||||||
"extra_specs",
|
|
||||||
return_raw=True)
|
|
||||||
|
|
||||||
def unset_keys(self, keys):
|
|
||||||
"""Unset extra specs on a volue type.
|
|
||||||
|
|
||||||
:param type_id: The :class:`VolumeType` to unset extra spec on
|
|
||||||
:param keys: A list of keys to be unset
|
|
||||||
"""
|
|
||||||
|
|
||||||
# NOTE(jdg): This wasn't actually doing all of the keys before
|
|
||||||
# the return in the loop resulted in only ONE key being unset,
|
|
||||||
# since on success the return was ListWithMeta class, we'll only
|
|
||||||
# interrupt the loop and if an exception is raised.
|
|
||||||
response_list = []
|
|
||||||
for k in keys:
|
|
||||||
resp, body = self.manager._delete(
|
|
||||||
"/types/%s/extra_specs/%s" % (
|
|
||||||
base.getid(self), k))
|
|
||||||
response_list.append(resp)
|
|
||||||
|
|
||||||
return common_base.ListWithMeta([], response_list)
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeTypeManager(base.ManagerWithFind):
|
|
||||||
"""Manage :class:`VolumeType` resources."""
|
|
||||||
resource_class = VolumeType
|
|
||||||
|
|
||||||
def list(self, search_opts=None, is_public=None):
|
|
||||||
"""Lists all volume types.
|
|
||||||
|
|
||||||
:rtype: list of :class:`VolumeType`.
|
|
||||||
"""
|
|
||||||
query_string = ''
|
|
||||||
if not is_public:
|
|
||||||
query_string = '?is_public=%s' % is_public
|
|
||||||
return self._list("/types%s" % (query_string), "volume_types")
|
|
||||||
|
|
||||||
def get(self, volume_type):
|
|
||||||
"""Get a specific volume type.
|
|
||||||
|
|
||||||
:param volume_type: The ID of the :class:`VolumeType` to get.
|
|
||||||
:rtype: :class:`VolumeType`
|
|
||||||
"""
|
|
||||||
return self._get("/types/%s" % base.getid(volume_type), "volume_type")
|
|
||||||
|
|
||||||
def default(self):
|
|
||||||
"""Get the default volume type.
|
|
||||||
|
|
||||||
:rtype: :class:`VolumeType`
|
|
||||||
"""
|
|
||||||
return self._get("/types/default", "volume_type")
|
|
||||||
|
|
||||||
def delete(self, volume_type):
|
|
||||||
"""Deletes a specific volume_type.
|
|
||||||
|
|
||||||
:param volume_type: The name or ID of the :class:`VolumeType` to get.
|
|
||||||
"""
|
|
||||||
return self._delete("/types/%s" % base.getid(volume_type))
|
|
||||||
|
|
||||||
def create(self, name, description=None, is_public=True):
|
|
||||||
"""Creates a volume type.
|
|
||||||
|
|
||||||
:param name: Descriptive name of the volume type
|
|
||||||
:param description: Description of the volume type
|
|
||||||
:param is_public: Volume type visibility
|
|
||||||
:rtype: :class:`VolumeType`
|
|
||||||
"""
|
|
||||||
|
|
||||||
body = {
|
|
||||||
"volume_type": {
|
|
||||||
"name": name,
|
|
||||||
"description": description,
|
|
||||||
"os-volume-type-access:is_public": is_public,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self._create("/types", body, "volume_type")
|
|
||||||
|
|
||||||
def update(self, volume_type, name=None, description=None, is_public=None):
|
|
||||||
"""Update the name and/or description for a volume type.
|
|
||||||
|
|
||||||
:param volume_type: The ID of the :class:`VolumeType` to update.
|
|
||||||
:param name: Descriptive name of the volume type.
|
|
||||||
:param description: Description of the volume type.
|
|
||||||
:rtype: :class:`VolumeType`
|
|
||||||
"""
|
|
||||||
|
|
||||||
body = {
|
|
||||||
"volume_type": {
|
|
||||||
"name": name,
|
|
||||||
"description": description
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if is_public is not None:
|
|
||||||
body["volume_type"]["is_public"] = is_public
|
|
||||||
|
|
||||||
return self._update("/types/%s" % base.getid(volume_type),
|
|
||||||
body, response_key="volume_type")
|
|
@ -16,4 +16,24 @@
|
|||||||
"""Capabilities interface (v3 extension)"""
|
"""Capabilities interface (v3 extension)"""
|
||||||
|
|
||||||
|
|
||||||
from cinderclient.v2.capabilities import * # noqa
|
from cinderclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class Capabilities(base.Resource):
|
||||||
|
NAME_ATTR = 'name'
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Capabilities: %s>" % self._info.get('namespace')
|
||||||
|
|
||||||
|
|
||||||
|
class CapabilitiesManager(base.Manager):
|
||||||
|
"""Manage :class:`Capabilities` resources."""
|
||||||
|
resource_class = Capabilities
|
||||||
|
|
||||||
|
def get(self, host):
|
||||||
|
"""Show backend volume stats and properties.
|
||||||
|
|
||||||
|
:param host: Specified backend to obtain volume stats and properties.
|
||||||
|
:rtype: :class:`Capabilities`
|
||||||
|
"""
|
||||||
|
return self._get('/capabilities/%s' % host, None)
|
||||||
|
@ -15,4 +15,98 @@
|
|||||||
|
|
||||||
"""cgsnapshot interface (v3 extension)."""
|
"""cgsnapshot interface (v3 extension)."""
|
||||||
|
|
||||||
from cinderclient.v2.cgsnapshots import * # noqa
|
from cinderclient.apiclient import base as common_base
|
||||||
|
from cinderclient import base
|
||||||
|
from cinderclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
class Cgsnapshot(base.Resource):
|
||||||
|
"""A cgsnapshot is snapshot of a consistency group."""
|
||||||
|
def __repr__(self):
|
||||||
|
return "<cgsnapshot: %s>" % self.id
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
"""Delete this cgsnapshot."""
|
||||||
|
return self.manager.delete(self)
|
||||||
|
|
||||||
|
def update(self, **kwargs):
|
||||||
|
"""Update the name or description for this cgsnapshot."""
|
||||||
|
return self.manager.update(self, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class CgsnapshotManager(base.ManagerWithFind):
|
||||||
|
"""Manage :class:`Cgsnapshot` resources."""
|
||||||
|
resource_class = Cgsnapshot
|
||||||
|
|
||||||
|
def create(self, consistencygroup_id, name=None, description=None,
|
||||||
|
user_id=None,
|
||||||
|
project_id=None):
|
||||||
|
"""Creates a cgsnapshot.
|
||||||
|
|
||||||
|
:param consistencygroup: Name or uuid of a consistency group
|
||||||
|
:param name: Name of the cgsnapshot
|
||||||
|
:param description: Description of the cgsnapshot
|
||||||
|
:param user_id: User id derived from context
|
||||||
|
:param project_id: Project id derived from context
|
||||||
|
:rtype: :class:`Cgsnapshot`
|
||||||
|
"""
|
||||||
|
|
||||||
|
body = {'cgsnapshot': {'consistencygroup_id': consistencygroup_id,
|
||||||
|
'name': name,
|
||||||
|
'description': description,
|
||||||
|
'user_id': user_id,
|
||||||
|
'project_id': project_id,
|
||||||
|
'status': "creating",
|
||||||
|
}}
|
||||||
|
|
||||||
|
return self._create('/cgsnapshots', body, 'cgsnapshot')
|
||||||
|
|
||||||
|
def get(self, cgsnapshot_id):
|
||||||
|
"""Get a cgsnapshot.
|
||||||
|
|
||||||
|
:param cgsnapshot_id: The ID of the cgsnapshot to get.
|
||||||
|
:rtype: :class:`Cgsnapshot`
|
||||||
|
"""
|
||||||
|
return self._get("/cgsnapshots/%s" % cgsnapshot_id, "cgsnapshot")
|
||||||
|
|
||||||
|
def list(self, detailed=True, search_opts=None):
|
||||||
|
"""Lists all cgsnapshots.
|
||||||
|
|
||||||
|
:rtype: list of :class:`Cgsnapshot`
|
||||||
|
"""
|
||||||
|
query_string = utils.build_query_param(search_opts)
|
||||||
|
|
||||||
|
detail = ""
|
||||||
|
if detailed:
|
||||||
|
detail = "/detail"
|
||||||
|
|
||||||
|
return self._list("/cgsnapshots%s%s" % (detail, query_string),
|
||||||
|
"cgsnapshots")
|
||||||
|
|
||||||
|
def delete(self, cgsnapshot):
|
||||||
|
"""Delete a cgsnapshot.
|
||||||
|
|
||||||
|
:param cgsnapshot: The :class:`Cgsnapshot` to delete.
|
||||||
|
"""
|
||||||
|
return self._delete("/cgsnapshots/%s" % base.getid(cgsnapshot))
|
||||||
|
|
||||||
|
def update(self, cgsnapshot, **kwargs):
|
||||||
|
"""Update the name or description for a cgsnapshot.
|
||||||
|
|
||||||
|
:param cgsnapshot: The :class:`Cgsnapshot` to update.
|
||||||
|
"""
|
||||||
|
if not kwargs:
|
||||||
|
return
|
||||||
|
|
||||||
|
body = {"cgsnapshot": kwargs}
|
||||||
|
|
||||||
|
return self._update("/cgsnapshots/%s" % base.getid(cgsnapshot), body)
|
||||||
|
|
||||||
|
def _action(self, action, cgsnapshot, info=None, **kwargs):
|
||||||
|
"""Perform a cgsnapshot "action."
|
||||||
|
"""
|
||||||
|
body = {action: info}
|
||||||
|
self.run_hooks('modify_body_for_action', body, **kwargs)
|
||||||
|
url = '/cgsnapshots/%s/action' % base.getid(cgsnapshot)
|
||||||
|
resp, body = self.api.client.post(url, body=body)
|
||||||
|
return common_base.TupleWithMeta((resp, body), resp)
|
||||||
|
@ -15,4 +15,135 @@
|
|||||||
|
|
||||||
"""Consistencygroup interface (v3 extension)."""
|
"""Consistencygroup interface (v3 extension)."""
|
||||||
|
|
||||||
from cinderclient.v2.consistencygroups import * # noqa
|
from cinderclient.apiclient import base as common_base
|
||||||
|
from cinderclient import base
|
||||||
|
from cinderclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
class Consistencygroup(base.Resource):
|
||||||
|
"""A Consistencygroup of volumes."""
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Consistencygroup: %s>" % self.id
|
||||||
|
|
||||||
|
def delete(self, force='False'):
|
||||||
|
"""Delete this consistency group."""
|
||||||
|
return self.manager.delete(self, force)
|
||||||
|
|
||||||
|
def update(self, **kwargs):
|
||||||
|
"""Update the name or description for this consistency group."""
|
||||||
|
return self.manager.update(self, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ConsistencygroupManager(base.ManagerWithFind):
|
||||||
|
"""Manage :class:`Consistencygroup` resources."""
|
||||||
|
resource_class = Consistencygroup
|
||||||
|
|
||||||
|
def create(self, volume_types, name=None,
|
||||||
|
description=None, user_id=None,
|
||||||
|
project_id=None, availability_zone=None):
|
||||||
|
"""Creates a consistency group.
|
||||||
|
|
||||||
|
:param name: Name of the ConsistencyGroup
|
||||||
|
:param description: Description of the ConsistencyGroup
|
||||||
|
:param volume_types: Types of volume
|
||||||
|
:param user_id: User id derived from context
|
||||||
|
:param project_id: Project id derived from context
|
||||||
|
:param availability_zone: Availability Zone to use
|
||||||
|
:rtype: :class:`Consistencygroup`
|
||||||
|
"""
|
||||||
|
|
||||||
|
body = {'consistencygroup': {'name': name,
|
||||||
|
'description': description,
|
||||||
|
'volume_types': volume_types,
|
||||||
|
'user_id': user_id,
|
||||||
|
'project_id': project_id,
|
||||||
|
'availability_zone': availability_zone,
|
||||||
|
'status': "creating",
|
||||||
|
}}
|
||||||
|
|
||||||
|
return self._create('/consistencygroups', body, 'consistencygroup')
|
||||||
|
|
||||||
|
def create_from_src(self, cgsnapshot_id, source_cgid, name=None,
|
||||||
|
description=None, user_id=None,
|
||||||
|
project_id=None):
|
||||||
|
"""Creates a consistency group from a cgsnapshot or a source CG.
|
||||||
|
|
||||||
|
:param cgsnapshot_id: UUID of a CGSnapshot
|
||||||
|
:param source_cgid: UUID of a source CG
|
||||||
|
:param name: Name of the ConsistencyGroup
|
||||||
|
:param description: Description of the ConsistencyGroup
|
||||||
|
:param user_id: User id derived from context
|
||||||
|
:param project_id: Project id derived from context
|
||||||
|
:rtype: A dictionary containing Consistencygroup metadata
|
||||||
|
"""
|
||||||
|
body = {'consistencygroup-from-src': {'name': name,
|
||||||
|
'description': description,
|
||||||
|
'cgsnapshot_id': cgsnapshot_id,
|
||||||
|
'source_cgid': source_cgid,
|
||||||
|
'user_id': user_id,
|
||||||
|
'project_id': project_id,
|
||||||
|
'status': "creating",
|
||||||
|
}}
|
||||||
|
|
||||||
|
self.run_hooks('modify_body_for_update', body,
|
||||||
|
'consistencygroup-from-src')
|
||||||
|
resp, body = self.api.client.post(
|
||||||
|
"/consistencygroups/create_from_src", body=body)
|
||||||
|
return common_base.DictWithMeta(body['consistencygroup'], resp)
|
||||||
|
|
||||||
|
def get(self, group_id):
|
||||||
|
"""Get a consistency group.
|
||||||
|
|
||||||
|
:param group_id: The ID of the consistency group to get.
|
||||||
|
:rtype: :class:`Consistencygroup`
|
||||||
|
"""
|
||||||
|
return self._get("/consistencygroups/%s" % group_id,
|
||||||
|
"consistencygroup")
|
||||||
|
|
||||||
|
def list(self, detailed=True, search_opts=None):
|
||||||
|
"""Lists all consistency groups.
|
||||||
|
|
||||||
|
:rtype: list of :class:`Consistencygroup`
|
||||||
|
"""
|
||||||
|
|
||||||
|
query_string = utils.build_query_param(search_opts)
|
||||||
|
|
||||||
|
detail = ""
|
||||||
|
if detailed:
|
||||||
|
detail = "/detail"
|
||||||
|
|
||||||
|
return self._list("/consistencygroups%s%s" % (detail, query_string),
|
||||||
|
"consistencygroups")
|
||||||
|
|
||||||
|
def delete(self, consistencygroup, force=False):
|
||||||
|
"""Delete a consistency group.
|
||||||
|
|
||||||
|
:param Consistencygroup: The :class:`Consistencygroup` to delete.
|
||||||
|
"""
|
||||||
|
body = {'consistencygroup': {'force': force}}
|
||||||
|
self.run_hooks('modify_body_for_action', body, 'consistencygroup')
|
||||||
|
url = '/consistencygroups/%s/delete' % base.getid(consistencygroup)
|
||||||
|
resp, body = self.api.client.post(url, body=body)
|
||||||
|
return common_base.TupleWithMeta((resp, body), resp)
|
||||||
|
|
||||||
|
def update(self, consistencygroup, **kwargs):
|
||||||
|
"""Update the name or description for a consistency group.
|
||||||
|
|
||||||
|
:param Consistencygroup: The :class:`Consistencygroup` to update.
|
||||||
|
"""
|
||||||
|
if not kwargs:
|
||||||
|
return
|
||||||
|
|
||||||
|
body = {"consistencygroup": kwargs}
|
||||||
|
|
||||||
|
return self._update("/consistencygroups/%s" %
|
||||||
|
base.getid(consistencygroup), body)
|
||||||
|
|
||||||
|
def _action(self, action, consistencygroup, info=None, **kwargs):
|
||||||
|
"""Perform a consistency group "action."
|
||||||
|
"""
|
||||||
|
body = {action: info}
|
||||||
|
self.run_hooks('modify_body_for_action', body, **kwargs)
|
||||||
|
url = '/consistencygroups/%s/action' % base.getid(consistencygroup)
|
||||||
|
resp, body = self.api.client.post(url, body=body)
|
||||||
|
return common_base.TupleWithMeta((resp, body), resp)
|
||||||
|
@ -13,4 +13,32 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from cinderclient.v2.contrib.list_extensions import * # noqa
|
from cinderclient import base
|
||||||
|
from cinderclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
class ListExtResource(base.Resource):
|
||||||
|
@property
|
||||||
|
def summary(self):
|
||||||
|
descr = self.description.strip()
|
||||||
|
if not descr:
|
||||||
|
return '??'
|
||||||
|
lines = descr.split("\n")
|
||||||
|
if len(lines) == 1:
|
||||||
|
return lines[0]
|
||||||
|
else:
|
||||||
|
return lines[0] + "..."
|
||||||
|
|
||||||
|
|
||||||
|
class ListExtManager(base.Manager):
|
||||||
|
resource_class = ListExtResource
|
||||||
|
|
||||||
|
def show_all(self):
|
||||||
|
return self._list("/extensions", 'extensions')
|
||||||
|
|
||||||
|
|
||||||
|
def do_list_extensions(client, _args):
|
||||||
|
"""Lists all available os-api extensions."""
|
||||||
|
extensions = client.list_extensions.show_all()
|
||||||
|
fields = ["Name", "Summary", "Alias", "Updated"]
|
||||||
|
utils.print_list(extensions, fields)
|
||||||
|
@ -13,4 +13,86 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from cinderclient.v2.limits import * # noqa
|
from cinderclient import base
|
||||||
|
from cinderclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
class Limits(base.Resource):
|
||||||
|
"""A collection of RateLimit and AbsoluteLimit objects."""
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Limits>"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def absolute(self):
|
||||||
|
for (name, value) in list(self._info['absolute'].items()):
|
||||||
|
yield AbsoluteLimit(name, value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rate(self):
|
||||||
|
for group in self._info['rate']:
|
||||||
|
uri = group['uri']
|
||||||
|
regex = group['regex']
|
||||||
|
for rate in group['limit']:
|
||||||
|
yield RateLimit(rate['verb'], uri, regex, rate['value'],
|
||||||
|
rate['remaining'], rate['unit'],
|
||||||
|
rate['next-available'])
|
||||||
|
|
||||||
|
|
||||||
|
class RateLimit(object):
|
||||||
|
"""Data model that represents a flattened view of a single rate limit."""
|
||||||
|
|
||||||
|
def __init__(self, verb, uri, regex, value, remain,
|
||||||
|
unit, next_available):
|
||||||
|
self.verb = verb
|
||||||
|
self.uri = uri
|
||||||
|
self.regex = regex
|
||||||
|
self.value = value
|
||||||
|
self.remain = remain
|
||||||
|
self.unit = unit
|
||||||
|
self.next_available = next_available
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.uri == other.uri \
|
||||||
|
and self.regex == other.regex \
|
||||||
|
and self.value == other.value \
|
||||||
|
and self.verb == other.verb \
|
||||||
|
and self.remain == other.remain \
|
||||||
|
and self.unit == other.unit \
|
||||||
|
and self.next_available == other.next_available
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<RateLimit: method=%s uri=%s>" % (self.verb, self.uri)
|
||||||
|
|
||||||
|
|
||||||
|
class AbsoluteLimit(object):
|
||||||
|
"""Data model that represents a single absolute limit."""
|
||||||
|
|
||||||
|
def __init__(self, name, value):
|
||||||
|
self.name = name
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.value == other.value and self.name == other.name
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<AbsoluteLimit: name=%s>" % (self.name)
|
||||||
|
|
||||||
|
|
||||||
|
class LimitsManager(base.Manager):
|
||||||
|
"""Manager object used to interact with limits resource."""
|
||||||
|
|
||||||
|
resource_class = Limits
|
||||||
|
|
||||||
|
def get(self, tenant_id=None):
|
||||||
|
"""Get a specific extension.
|
||||||
|
|
||||||
|
:rtype: :class:`Limits`
|
||||||
|
"""
|
||||||
|
opts = {}
|
||||||
|
if tenant_id:
|
||||||
|
opts['tenant_id'] = tenant_id
|
||||||
|
|
||||||
|
query_string = utils.build_query_param(opts)
|
||||||
|
|
||||||
|
return self._get("/limits%s" % query_string, "limits")
|
||||||
|
@ -15,4 +15,46 @@
|
|||||||
|
|
||||||
"""Pools interface (v3 extension)"""
|
"""Pools interface (v3 extension)"""
|
||||||
|
|
||||||
from cinderclient.v2.pools import * # noqa
|
from cinderclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class Pool(base.Resource):
|
||||||
|
NAME_ATTR = 'name'
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Pool: %s>" % self.name
|
||||||
|
|
||||||
|
|
||||||
|
class PoolManager(base.Manager):
|
||||||
|
"""Manage :class:`Pool` resources."""
|
||||||
|
resource_class = Pool
|
||||||
|
|
||||||
|
def list(self, detailed=False):
|
||||||
|
"""Lists all
|
||||||
|
|
||||||
|
:rtype: list of :class:`Pool`
|
||||||
|
"""
|
||||||
|
if detailed is True:
|
||||||
|
pools = self._list("/scheduler-stats/get_pools?detail=True",
|
||||||
|
"pools")
|
||||||
|
# Other than the name, all of the pool data is buried below in
|
||||||
|
# a 'capabilities' dictionary. In order to be consistent with the
|
||||||
|
# get-pools command line, these elements are moved up a level to
|
||||||
|
# be attributes of the pool itself.
|
||||||
|
for pool in pools:
|
||||||
|
if hasattr(pool, 'capabilities'):
|
||||||
|
for k, v in pool.capabilities.items():
|
||||||
|
setattr(pool, k, v)
|
||||||
|
|
||||||
|
# Remove the capabilities dictionary since all of its
|
||||||
|
# elements have been copied up to the containing pool
|
||||||
|
del pool.capabilities
|
||||||
|
return pools
|
||||||
|
else:
|
||||||
|
pools = self._list("/scheduler-stats/get_pools", "pools")
|
||||||
|
|
||||||
|
# avoid cluttering the basic pool list with capabilities dict
|
||||||
|
for pool in pools:
|
||||||
|
if hasattr(pool, 'capabilities'):
|
||||||
|
del pool.capabilities
|
||||||
|
return pools
|
||||||
|
@ -19,4 +19,138 @@
|
|||||||
QoS Specs interface.
|
QoS Specs interface.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from cinderclient.v2.qos_specs import * # noqa
|
from cinderclient.apiclient import base as common_base
|
||||||
|
from cinderclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class QoSSpecs(base.Resource):
|
||||||
|
"""QoS specs entity represents quality-of-service parameters/requirements.
|
||||||
|
|
||||||
|
A QoS specs is a set of parameters or requirements for quality-of-service
|
||||||
|
purpose, which can be associated with volume types (for now). In future,
|
||||||
|
QoS specs may be extended to be associated other entities, such as single
|
||||||
|
volume.
|
||||||
|
"""
|
||||||
|
def __repr__(self):
|
||||||
|
return "<QoSSpecs: %s>" % self.name
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
return self.manager.delete(self)
|
||||||
|
|
||||||
|
|
||||||
|
class QoSSpecsManager(base.ManagerWithFind):
|
||||||
|
"""
|
||||||
|
Manage :class:`QoSSpecs` resources.
|
||||||
|
"""
|
||||||
|
resource_class = QoSSpecs
|
||||||
|
|
||||||
|
def list(self, search_opts=None):
|
||||||
|
"""Get a list of all qos specs.
|
||||||
|
|
||||||
|
:rtype: list of :class:`QoSSpecs`.
|
||||||
|
"""
|
||||||
|
return self._list("/qos-specs", "qos_specs")
|
||||||
|
|
||||||
|
def get(self, qos_specs):
|
||||||
|
"""Get a specific qos specs.
|
||||||
|
|
||||||
|
:param qos_specs: The ID of the :class:`QoSSpecs` to get.
|
||||||
|
:rtype: :class:`QoSSpecs`
|
||||||
|
"""
|
||||||
|
return self._get("/qos-specs/%s" % base.getid(qos_specs), "qos_specs")
|
||||||
|
|
||||||
|
def delete(self, qos_specs, force=False):
|
||||||
|
"""Delete a specific qos specs.
|
||||||
|
|
||||||
|
:param qos_specs: The ID of the :class:`QoSSpecs` to be removed.
|
||||||
|
:param force: Flag that indicates whether to delete target qos specs
|
||||||
|
if it was in-use.
|
||||||
|
"""
|
||||||
|
return self._delete("/qos-specs/%s?force=%s" %
|
||||||
|
(base.getid(qos_specs), force))
|
||||||
|
|
||||||
|
def create(self, name, specs):
|
||||||
|
"""Create a qos specs.
|
||||||
|
|
||||||
|
:param name: Descriptive name of the qos specs, must be unique
|
||||||
|
:param specs: A dict of key/value pairs to be set
|
||||||
|
:rtype: :class:`QoSSpecs`
|
||||||
|
"""
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"qos_specs": {
|
||||||
|
"name": name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body["qos_specs"].update(specs)
|
||||||
|
return self._create("/qos-specs", body, "qos_specs")
|
||||||
|
|
||||||
|
def set_keys(self, qos_specs, specs):
|
||||||
|
"""Add/Update keys in qos specs.
|
||||||
|
|
||||||
|
:param qos_specs: The ID of qos specs
|
||||||
|
:param specs: A dict of key/value pairs to be set
|
||||||
|
:rtype: :class:`QoSSpecs`
|
||||||
|
"""
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"qos_specs": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
body["qos_specs"].update(specs)
|
||||||
|
return self._update("/qos-specs/%s" % qos_specs, body)
|
||||||
|
|
||||||
|
def unset_keys(self, qos_specs, specs):
|
||||||
|
"""Remove keys from a qos specs.
|
||||||
|
|
||||||
|
:param qos_specs: The ID of qos specs
|
||||||
|
:param specs: A list of key to be unset
|
||||||
|
:rtype: :class:`QoSSpecs`
|
||||||
|
"""
|
||||||
|
|
||||||
|
body = {'keys': specs}
|
||||||
|
|
||||||
|
return self._update("/qos-specs/%s/delete_keys" % qos_specs,
|
||||||
|
body)
|
||||||
|
|
||||||
|
def get_associations(self, qos_specs):
|
||||||
|
"""Get associated entities of a qos specs.
|
||||||
|
|
||||||
|
:param qos_specs: The id of the :class: `QoSSpecs`
|
||||||
|
:return: a list of entities that associated with specific qos specs.
|
||||||
|
"""
|
||||||
|
return self._list("/qos-specs/%s/associations" % base.getid(qos_specs),
|
||||||
|
"qos_associations")
|
||||||
|
|
||||||
|
def associate(self, qos_specs, vol_type_id):
|
||||||
|
"""Associate a volume type with specific qos specs.
|
||||||
|
|
||||||
|
:param qos_specs: The qos specs to be associated with
|
||||||
|
:param vol_type_id: The volume type id to be associated with
|
||||||
|
"""
|
||||||
|
resp, body = self.api.client.get(
|
||||||
|
"/qos-specs/%s/associate?vol_type_id=%s" %
|
||||||
|
(base.getid(qos_specs), vol_type_id))
|
||||||
|
return common_base.TupleWithMeta((resp, body), resp)
|
||||||
|
|
||||||
|
def disassociate(self, qos_specs, vol_type_id):
|
||||||
|
"""Disassociate qos specs from volume type.
|
||||||
|
|
||||||
|
:param qos_specs: The qos specs to be associated with
|
||||||
|
:param vol_type_id: The volume type id to be associated with
|
||||||
|
"""
|
||||||
|
resp, body = self.api.client.get(
|
||||||
|
"/qos-specs/%s/disassociate?vol_type_id=%s" %
|
||||||
|
(base.getid(qos_specs), vol_type_id))
|
||||||
|
return common_base.TupleWithMeta((resp, body), resp)
|
||||||
|
|
||||||
|
def disassociate_all(self, qos_specs):
|
||||||
|
"""Disassociate all entities from specific qos specs.
|
||||||
|
|
||||||
|
:param qos_specs: The qos specs to be associated with
|
||||||
|
"""
|
||||||
|
resp, body = self.api.client.get(
|
||||||
|
"/qos-specs/%s/disassociate_all" %
|
||||||
|
base.getid(qos_specs))
|
||||||
|
return common_base.TupleWithMeta((resp, body), resp)
|
||||||
|
@ -13,4 +13,35 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from cinderclient.v2.quota_classes import * # noqa
|
from cinderclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaClassSet(base.Resource):
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""Needed by base.Resource to self-refresh and be indexed."""
|
||||||
|
return self.class_name
|
||||||
|
|
||||||
|
def update(self, *args, **kwargs):
|
||||||
|
return self.manager.update(self.class_name, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaClassSetManager(base.Manager):
|
||||||
|
resource_class = QuotaClassSet
|
||||||
|
|
||||||
|
def get(self, class_name):
|
||||||
|
return self._get("/os-quota-class-sets/%s" % (class_name),
|
||||||
|
"quota_class_set")
|
||||||
|
|
||||||
|
def update(self, class_name, **updates):
|
||||||
|
quota_class_set = {}
|
||||||
|
|
||||||
|
for update in updates:
|
||||||
|
quota_class_set[update] = updates[update]
|
||||||
|
|
||||||
|
result = self._update('/os-quota-class-sets/%s' % (class_name),
|
||||||
|
{'quota_class_set': quota_class_set})
|
||||||
|
return self.resource_class(self,
|
||||||
|
result['quota_class_set'], loaded=True,
|
||||||
|
resp=result.request_ids)
|
||||||
|
@ -13,10 +13,28 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from cinderclient.v2 import quotas
|
from cinderclient import base
|
||||||
|
|
||||||
|
|
||||||
class QuotaSetManager(quotas.QuotaSetManager):
|
class QuotaSet(base.Resource):
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""Needed by base.Resource to self-refresh and be indexed."""
|
||||||
|
return self.tenant_id
|
||||||
|
|
||||||
|
def update(self, *args, **kwargs):
|
||||||
|
return self.manager.update(self.tenant_id, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaSetManager(base.Manager):
|
||||||
|
resource_class = QuotaSet
|
||||||
|
|
||||||
|
def get(self, tenant_id, usage=False):
|
||||||
|
if hasattr(tenant_id, 'tenant_id'):
|
||||||
|
tenant_id = tenant_id.tenant_id
|
||||||
|
return self._get("/os-quota-sets/%s?usage=%s" % (tenant_id, usage),
|
||||||
|
"quota_set")
|
||||||
|
|
||||||
def update(self, tenant_id, **updates):
|
def update(self, tenant_id, **updates):
|
||||||
skip_validation = updates.pop('skip_validation', True)
|
skip_validation = updates.pop('skip_validation', True)
|
||||||
@ -32,3 +50,12 @@ class QuotaSetManager(quotas.QuotaSetManager):
|
|||||||
result = self._update(request_url, body)
|
result = self._update(request_url, body)
|
||||||
return self.resource_class(self, result['quota_set'], loaded=True,
|
return self.resource_class(self, result['quota_set'], loaded=True,
|
||||||
resp=result.request_ids)
|
resp=result.request_ids)
|
||||||
|
|
||||||
|
def defaults(self, tenant_id):
|
||||||
|
return self._get('/os-quota-sets/%s/defaults' % tenant_id,
|
||||||
|
'quota_set')
|
||||||
|
|
||||||
|
def delete(self, tenant_id):
|
||||||
|
if hasattr(tenant_id, 'tenant_id'):
|
||||||
|
tenant_id = tenant_id.tenant_id
|
||||||
|
return self._delete("/os-quota-sets/%s" % tenant_id)
|
||||||
|
@ -19,9 +19,12 @@ service interface
|
|||||||
|
|
||||||
from cinderclient import api_versions
|
from cinderclient import api_versions
|
||||||
from cinderclient import base
|
from cinderclient import base
|
||||||
from cinderclient.v2 import services
|
|
||||||
|
|
||||||
Service = services.Service
|
|
||||||
|
class Service(base.Resource):
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Service: binary=%s host=%s>" % (self.binary, self.host)
|
||||||
|
|
||||||
|
|
||||||
class LogLevel(base.Resource):
|
class LogLevel(base.Resource):
|
||||||
@ -30,7 +33,61 @@ class LogLevel(base.Resource):
|
|||||||
self.binary, self.host, self.prefix, self.level)
|
self.binary, self.host, self.prefix, self.level)
|
||||||
|
|
||||||
|
|
||||||
class ServiceManager(services.ServiceManager):
|
class ServiceManagerBase(base.ManagerWithFind):
|
||||||
|
resource_class = Service
|
||||||
|
|
||||||
|
def list(self, host=None, binary=None):
|
||||||
|
"""
|
||||||
|
Describes service list for host.
|
||||||
|
|
||||||
|
:param host: destination host name.
|
||||||
|
:param binary: service binary.
|
||||||
|
"""
|
||||||
|
url = "/os-services"
|
||||||
|
filters = []
|
||||||
|
if host:
|
||||||
|
filters.append("host=%s" % host)
|
||||||
|
if binary:
|
||||||
|
filters.append("binary=%s" % binary)
|
||||||
|
if filters:
|
||||||
|
url = "%s?%s" % (url, "&".join(filters))
|
||||||
|
return self._list(url, "services")
|
||||||
|
|
||||||
|
def enable(self, host, binary):
|
||||||
|
"""Enable the service specified by hostname and binary."""
|
||||||
|
body = {"host": host, "binary": binary}
|
||||||
|
result = self._update("/os-services/enable", body)
|
||||||
|
return self.resource_class(self, result, resp=result.request_ids)
|
||||||
|
|
||||||
|
def disable(self, host, binary):
|
||||||
|
"""Disable the service specified by hostname and binary."""
|
||||||
|
body = {"host": host, "binary": binary}
|
||||||
|
result = self._update("/os-services/disable", body)
|
||||||
|
return self.resource_class(self, result, resp=result.request_ids)
|
||||||
|
|
||||||
|
def disable_log_reason(self, host, binary, reason):
|
||||||
|
"""Disable the service with reason."""
|
||||||
|
body = {"host": host, "binary": binary, "disabled_reason": reason}
|
||||||
|
result = self._update("/os-services/disable-log-reason", body)
|
||||||
|
return self.resource_class(self, result, resp=result.request_ids)
|
||||||
|
|
||||||
|
def freeze_host(self, host):
|
||||||
|
"""Freeze the service specified by hostname."""
|
||||||
|
body = {"host": host}
|
||||||
|
return self._update("/os-services/freeze", body)
|
||||||
|
|
||||||
|
def thaw_host(self, host):
|
||||||
|
"""Thaw the service specified by hostname."""
|
||||||
|
body = {"host": host}
|
||||||
|
return self._update("/os-services/thaw", body)
|
||||||
|
|
||||||
|
def failover_host(self, host, backend_id):
|
||||||
|
"""Failover a replicated backend by hostname."""
|
||||||
|
body = {"host": host, "backend_id": backend_id}
|
||||||
|
return self._update("/os-services/failover_host", body)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceManager(ServiceManagerBase):
|
||||||
@api_versions.wraps("3.0")
|
@api_versions.wraps("3.0")
|
||||||
def server_api_version(self):
|
def server_api_version(self):
|
||||||
"""Returns the API Version supported by the server.
|
"""Returns the API Version supported by the server.
|
||||||
|
@ -25,7 +25,7 @@ from cinderclient import base
|
|||||||
from cinderclient import exceptions
|
from cinderclient import exceptions
|
||||||
from cinderclient import shell_utils
|
from cinderclient import shell_utils
|
||||||
from cinderclient import utils
|
from cinderclient import utils
|
||||||
from cinderclient.v2 import availability_zones
|
from cinderclient.v3 import availability_zones
|
||||||
|
|
||||||
|
|
||||||
def _translate_attachments(info):
|
def _translate_attachments(info):
|
||||||
|
@ -18,14 +18,32 @@ Volume Backups interface (v3 extension).
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from cinderclient import api_versions
|
from cinderclient import api_versions
|
||||||
|
from cinderclient.apiclient import base as common_base
|
||||||
from cinderclient import base
|
from cinderclient import base
|
||||||
from cinderclient.v2 import volume_backups
|
|
||||||
|
|
||||||
|
|
||||||
VolumeBackup = volume_backups.VolumeBackup
|
class VolumeBackup(base.Resource):
|
||||||
|
"""A volume backup is a block level backup of a volume."""
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<VolumeBackup: %s>" % self.id
|
||||||
|
|
||||||
|
def delete(self, force=False):
|
||||||
|
"""Delete this volume backup."""
|
||||||
|
return self.manager.delete(self, force)
|
||||||
|
|
||||||
|
def reset_state(self, state):
|
||||||
|
return self.manager.reset_state(self, state)
|
||||||
|
|
||||||
|
def update(self, **kwargs):
|
||||||
|
"""Update the name or description for this backup."""
|
||||||
|
return self.manager.update(self, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class VolumeBackupManager(volume_backups.VolumeBackupManager):
|
class VolumeBackupManager(base.ManagerWithFind):
|
||||||
|
"""Manage :class:`VolumeBackup` resources."""
|
||||||
|
resource_class = VolumeBackup
|
||||||
|
|
||||||
@api_versions.wraps("3.9")
|
@api_versions.wraps("3.9")
|
||||||
def update(self, backup, **kwargs):
|
def update(self, backup, **kwargs):
|
||||||
"""Update the name or description for a backup.
|
"""Update the name or description for a backup.
|
||||||
@ -124,3 +142,70 @@ class VolumeBackupManager(volume_backups.VolumeBackupManager):
|
|||||||
if availability_zone:
|
if availability_zone:
|
||||||
body['backup']['availability_zone'] = availability_zone
|
body['backup']['availability_zone'] = availability_zone
|
||||||
return self._create('/backups', body, 'backup')
|
return self._create('/backups', body, 'backup')
|
||||||
|
|
||||||
|
def get(self, backup_id):
|
||||||
|
"""Show volume backup details.
|
||||||
|
|
||||||
|
:param backup_id: The ID of the backup to display.
|
||||||
|
:rtype: :class:`VolumeBackup`
|
||||||
|
"""
|
||||||
|
return self._get("/backups/%s" % backup_id, "backup")
|
||||||
|
|
||||||
|
def list(self, detailed=True, search_opts=None, marker=None, limit=None,
|
||||||
|
sort=None):
|
||||||
|
"""Get a list of all volume backups.
|
||||||
|
|
||||||
|
:rtype: list of :class:`VolumeBackup`
|
||||||
|
"""
|
||||||
|
resource_type = "backups"
|
||||||
|
url = self._build_list_url(resource_type, detailed=detailed,
|
||||||
|
search_opts=search_opts, marker=marker,
|
||||||
|
limit=limit, sort=sort)
|
||||||
|
return self._list(url, resource_type, limit=limit)
|
||||||
|
|
||||||
|
def delete(self, backup, force=False):
|
||||||
|
"""Delete a volume backup.
|
||||||
|
|
||||||
|
:param backup: The :class:`VolumeBackup` to delete.
|
||||||
|
:param force: Allow delete in state other than error or available.
|
||||||
|
"""
|
||||||
|
if force:
|
||||||
|
return self._action('os-force_delete', backup)
|
||||||
|
else:
|
||||||
|
return self._delete("/backups/%s" % base.getid(backup))
|
||||||
|
|
||||||
|
def reset_state(self, backup, state):
|
||||||
|
"""Update the specified volume backup with the provided state."""
|
||||||
|
return self._action('os-reset_status', backup,
|
||||||
|
{'status': state} if state else {})
|
||||||
|
|
||||||
|
def _action(self, action, backup, info=None, **kwargs):
|
||||||
|
"""Perform a volume backup action."""
|
||||||
|
body = {action: info}
|
||||||
|
self.run_hooks('modify_body_for_action', body, **kwargs)
|
||||||
|
url = '/backups/%s/action' % base.getid(backup)
|
||||||
|
resp, body = self.api.client.post(url, body=body)
|
||||||
|
return common_base.TupleWithMeta((resp, body), resp)
|
||||||
|
|
||||||
|
def export_record(self, backup_id):
|
||||||
|
"""Export volume backup metadata record.
|
||||||
|
|
||||||
|
:param backup_id: The ID of the backup to export.
|
||||||
|
:rtype: A dictionary containing 'backup_url' and 'backup_service'.
|
||||||
|
"""
|
||||||
|
resp, body = \
|
||||||
|
self.api.client.get("/backups/%s/export_record" % backup_id)
|
||||||
|
return common_base.DictWithMeta(body['backup-record'], resp)
|
||||||
|
|
||||||
|
def import_record(self, backup_service, backup_url):
|
||||||
|
"""Import volume backup metadata record.
|
||||||
|
|
||||||
|
:param backup_service: Backup service to use for importing the backup
|
||||||
|
:param backup_url: Backup URL for importing the backup metadata
|
||||||
|
:rtype: A dictionary containing volume backup metadata.
|
||||||
|
"""
|
||||||
|
body = {'backup-record': {'backup_service': backup_service,
|
||||||
|
'backup_url': backup_url}}
|
||||||
|
self.run_hooks('modify_body_for_update', body, 'backup-record')
|
||||||
|
resp, body = self.api.client.post("/backups/import_record", body=body)
|
||||||
|
return common_base.DictWithMeta(body['backup'], resp)
|
||||||
|
@ -18,4 +18,27 @@
|
|||||||
This is part of the Volume Backups interface.
|
This is part of the Volume Backups interface.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from cinderclient.v2.volume_backups_restore import * # noqa
|
from cinderclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeBackupsRestore(base.Resource):
|
||||||
|
"""A Volume Backups Restore represents a restore operation."""
|
||||||
|
def __repr__(self):
|
||||||
|
return "<VolumeBackupsRestore: %s>" % self.volume_id
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeBackupRestoreManager(base.Manager):
|
||||||
|
"""Manage :class:`VolumeBackupsRestore` resources."""
|
||||||
|
resource_class = VolumeBackupsRestore
|
||||||
|
|
||||||
|
def restore(self, backup_id, volume_id=None, name=None):
|
||||||
|
"""Restore a backup to a volume.
|
||||||
|
|
||||||
|
:param backup_id: The ID of the backup to restore.
|
||||||
|
:param volume_id: The ID of the volume to restore the backup to.
|
||||||
|
:param name : The name for new volume creation to restore.
|
||||||
|
:rtype: :class:`Restore`
|
||||||
|
"""
|
||||||
|
body = {'restore': {'volume_id': volume_id, 'name': name}}
|
||||||
|
return self._create("/backups/%s/restore" % backup_id,
|
||||||
|
body, "restore")
|
||||||
|
@ -18,4 +18,88 @@
|
|||||||
Volume Encryption Type interface
|
Volume Encryption Type interface
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from cinderclient.v2.volume_encryption_types import * # noqa
|
from cinderclient.apiclient import base as common_base
|
||||||
|
from cinderclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeEncryptionType(base.Resource):
|
||||||
|
"""
|
||||||
|
A Volume Encryption Type is a collection of settings used to conduct
|
||||||
|
encryption for a specific volume type.
|
||||||
|
"""
|
||||||
|
def __repr__(self):
|
||||||
|
return "<VolumeEncryptionType: %s>" % self.encryption_id
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeEncryptionTypeManager(base.ManagerWithFind):
|
||||||
|
"""
|
||||||
|
Manage :class: `VolumeEncryptionType` resources.
|
||||||
|
"""
|
||||||
|
resource_class = VolumeEncryptionType
|
||||||
|
|
||||||
|
def list(self, search_opts=None):
|
||||||
|
"""
|
||||||
|
List all volume encryption types.
|
||||||
|
|
||||||
|
:param search_opts: Search options to filter out volume
|
||||||
|
encryption types
|
||||||
|
:return: a list of :class: VolumeEncryptionType instances
|
||||||
|
"""
|
||||||
|
# Since the encryption type is a volume type extension, we cannot get
|
||||||
|
# all encryption types without going through all volume types.
|
||||||
|
volume_types = self.api.volume_types.list()
|
||||||
|
encryption_types = []
|
||||||
|
list_of_resp = []
|
||||||
|
for volume_type in volume_types:
|
||||||
|
encryption_type = self._get("/types/%s/encryption"
|
||||||
|
% base.getid(volume_type))
|
||||||
|
if hasattr(encryption_type, 'volume_type_id'):
|
||||||
|
encryption_types.append(encryption_type)
|
||||||
|
|
||||||
|
list_of_resp.extend(encryption_type.request_ids)
|
||||||
|
|
||||||
|
return common_base.ListWithMeta(encryption_types, list_of_resp)
|
||||||
|
|
||||||
|
def get(self, volume_type):
|
||||||
|
"""
|
||||||
|
Get the volume encryption type for the specified volume type.
|
||||||
|
|
||||||
|
:param volume_type: the volume type to query
|
||||||
|
:return: an instance of :class: VolumeEncryptionType
|
||||||
|
"""
|
||||||
|
return self._get("/types/%s/encryption" % base.getid(volume_type))
|
||||||
|
|
||||||
|
def create(self, volume_type, specs):
|
||||||
|
"""
|
||||||
|
Creates encryption type for a volume type. Default: admin only.
|
||||||
|
|
||||||
|
:param volume_type: the volume type on which to add an encryption type
|
||||||
|
:param specs: the encryption type specifications to add
|
||||||
|
:return: an instance of :class: VolumeEncryptionType
|
||||||
|
"""
|
||||||
|
body = {'encryption': specs}
|
||||||
|
return self._create("/types/%s/encryption" % base.getid(volume_type),
|
||||||
|
body, "encryption")
|
||||||
|
|
||||||
|
def update(self, volume_type, specs):
|
||||||
|
"""
|
||||||
|
Update the encryption type information for the specified volume type.
|
||||||
|
|
||||||
|
:param volume_type: the volume type whose encryption type information
|
||||||
|
must be updated
|
||||||
|
:param specs: the encryption type specifications to update
|
||||||
|
:return: an instance of :class: VolumeEncryptionType
|
||||||
|
"""
|
||||||
|
body = {'encryption': specs}
|
||||||
|
return self._update("/types/%s/encryption/provider" %
|
||||||
|
base.getid(volume_type), body)
|
||||||
|
|
||||||
|
def delete(self, volume_type):
|
||||||
|
"""
|
||||||
|
Delete the encryption type information for the specified volume type.
|
||||||
|
|
||||||
|
:param volume_type: the volume type whose encryption type information
|
||||||
|
must be deleted
|
||||||
|
"""
|
||||||
|
return self._delete("/types/%s/encryption/provider" %
|
||||||
|
base.getid(volume_type))
|
||||||
|
@ -213,9 +213,17 @@ class SnapshotManager(base.ManagerWithFind):
|
|||||||
}
|
}
|
||||||
return self._create('/os-snapshot-manage', body, 'snapshot')
|
return self._create('/os-snapshot-manage', body, 'snapshot')
|
||||||
|
|
||||||
|
@api_versions.wraps("3.0")
|
||||||
|
def list_manageable(self, host, detailed=True, marker=None,
|
||||||
|
limit=None, offset=None, sort=None):
|
||||||
|
url = self._build_list_url("os-snapshot-manage", detailed=detailed,
|
||||||
|
search_opts={'host': host}, marker=marker,
|
||||||
|
limit=limit, offset=offset, sort=sort)
|
||||||
|
return self._list(url, "manageable-snapshots")
|
||||||
|
|
||||||
@api_versions.wraps('3.8')
|
@api_versions.wraps('3.8')
|
||||||
def list_manageable(self, host, detailed=True, marker=None, limit=None,
|
def list_manageable(self, host, detailed=True, marker=None, # noqa: F811
|
||||||
offset=None, sort=None, cluster=None):
|
limit=None, offset=None, sort=None, cluster=None):
|
||||||
search_opts = {'cluster': cluster} if cluster else {'host': host}
|
search_opts = {'cluster': cluster} if cluster else {'host': host}
|
||||||
url = self._build_list_url("manageable_snapshots", detailed=detailed,
|
url = self._build_list_url("manageable_snapshots", detailed=detailed,
|
||||||
search_opts=search_opts, marker=marker,
|
search_opts=search_opts, marker=marker,
|
||||||
|
@ -16,10 +16,23 @@
|
|||||||
"""Volume transfer interface (v3 extension)."""
|
"""Volume transfer interface (v3 extension)."""
|
||||||
|
|
||||||
from cinderclient import base
|
from cinderclient import base
|
||||||
from cinderclient.v2 import volume_transfers
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeTransferManager(volume_transfers.VolumeTransferManager):
|
class VolumeTransfer(base.Resource):
|
||||||
|
"""Transfer a volume from one tenant to another"""
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<VolumeTransfer: %s>" % self.id
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
"""Delete this volume transfer."""
|
||||||
|
return self.manager.delete(self)
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeTransferManager(base.ManagerWithFind):
|
||||||
|
"""Manage :class:`VolumeTransfer` resources."""
|
||||||
|
resource_class = VolumeTransfer
|
||||||
|
|
||||||
def create(self, volume_id, name=None, no_snapshots=False):
|
def create(self, volume_id, name=None, no_snapshots=False):
|
||||||
"""Creates a volume transfer.
|
"""Creates a volume transfer.
|
||||||
|
|
||||||
|
@ -14,4 +14,40 @@
|
|||||||
|
|
||||||
"""Volume type access interface."""
|
"""Volume type access interface."""
|
||||||
|
|
||||||
from cinderclient.v2.volume_type_access import * # noqa
|
from cinderclient.apiclient import base as common_base
|
||||||
|
from cinderclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeTypeAccess(base.Resource):
|
||||||
|
def __repr__(self):
|
||||||
|
return "<VolumeTypeAccess: %s>" % self.project_id
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeTypeAccessManager(base.ManagerWithFind):
|
||||||
|
"""
|
||||||
|
Manage :class:`VolumeTypeAccess` resources.
|
||||||
|
"""
|
||||||
|
resource_class = VolumeTypeAccess
|
||||||
|
|
||||||
|
def list(self, volume_type):
|
||||||
|
return self._list(
|
||||||
|
'/types/%s/os-volume-type-access' % base.getid(volume_type),
|
||||||
|
'volume_type_access')
|
||||||
|
|
||||||
|
def add_project_access(self, volume_type, project):
|
||||||
|
"""Add a project to the given volume type access list."""
|
||||||
|
info = {'project': project}
|
||||||
|
return self._action('addProjectAccess', volume_type, info)
|
||||||
|
|
||||||
|
def remove_project_access(self, volume_type, project):
|
||||||
|
"""Remove a project from the given volume type access list."""
|
||||||
|
info = {'project': project}
|
||||||
|
return self._action('removeProjectAccess', volume_type, info)
|
||||||
|
|
||||||
|
def _action(self, action, volume_type, info, **kwargs):
|
||||||
|
"""Perform a volume type action."""
|
||||||
|
body = {action: info}
|
||||||
|
self.run_hooks('modify_body_for_action', body, **kwargs)
|
||||||
|
url = '/types/%s/action' % base.getid(volume_type)
|
||||||
|
resp, body = self.api.client.post(url, body=body)
|
||||||
|
return common_base.TupleWithMeta((resp, body), resp)
|
||||||
|
@ -18,10 +18,10 @@
|
|||||||
from cinderclient import api_versions
|
from cinderclient import api_versions
|
||||||
from cinderclient.apiclient import base as common_base
|
from cinderclient.apiclient import base as common_base
|
||||||
from cinderclient import base
|
from cinderclient import base
|
||||||
from cinderclient.v2 import volumes
|
from cinderclient.v3 import volumes_base
|
||||||
|
|
||||||
|
|
||||||
class Volume(volumes.Volume):
|
class Volume(volumes_base.Volume):
|
||||||
|
|
||||||
def upload_to_image(self, force, image_name, container_format,
|
def upload_to_image(self, force, image_name, container_format,
|
||||||
disk_format, visibility=None,
|
disk_format, visibility=None,
|
||||||
@ -68,7 +68,7 @@ class Volume(volumes.Volume):
|
|||||||
cluster=cluster)
|
cluster=cluster)
|
||||||
|
|
||||||
|
|
||||||
class VolumeManager(volumes.VolumeManager):
|
class VolumeManager(volumes_base.VolumeManager):
|
||||||
resource_class = Volume
|
resource_class = Volume
|
||||||
|
|
||||||
def create(self, size, consistencygroup_id=None,
|
def create(self, size, consistencygroup_id=None,
|
||||||
@ -246,16 +246,24 @@ class VolumeManager(volumes.VolumeManager):
|
|||||||
body['volume']['cluster'] = cluster
|
body['volume']['cluster'] = cluster
|
||||||
return self._create('/os-volume-manage', body, 'volume')
|
return self._create('/os-volume-manage', body, 'volume')
|
||||||
|
|
||||||
|
@api_versions.wraps('3.0')
|
||||||
|
def list_manageable(self, host, detailed=True, marker=None,
|
||||||
|
limit=None, offset=None, sort=None):
|
||||||
|
url = self._build_list_url("os-volume-manage", detailed=detailed,
|
||||||
|
search_opts={'host': host}, marker=marker,
|
||||||
|
limit=limit, offset=offset, sort=sort)
|
||||||
|
return self._list(url, "manageable-volumes")
|
||||||
|
|
||||||
@api_versions.wraps('3.8')
|
@api_versions.wraps('3.8')
|
||||||
def list_manageable(self, host, detailed=True, marker=None, limit=None,
|
def list_manageable(self, host, detailed=True, marker=None, # noqa: F811
|
||||||
offset=None, sort=None, cluster=None):
|
limit=None, offset=None, sort=None, cluster=None):
|
||||||
search_opts = {'cluster': cluster} if cluster else {'host': host}
|
search_opts = {'cluster': cluster} if cluster else {'host': host}
|
||||||
url = self._build_list_url("manageable_volumes", detailed=detailed,
|
url = self._build_list_url("manageable_volumes", detailed=detailed,
|
||||||
search_opts=search_opts, marker=marker,
|
search_opts=search_opts, marker=marker,
|
||||||
limit=limit, offset=offset, sort=sort)
|
limit=limit, offset=offset, sort=sort)
|
||||||
return self._list(url, "manageable-volumes")
|
return self._list(url, "manageable-volumes")
|
||||||
|
|
||||||
@api_versions.wraps("2.0", "3.32")
|
@api_versions.wraps("3.0", "3.32")
|
||||||
def get_pools(self, detail):
|
def get_pools(self, detail):
|
||||||
"""Show pool information for backends."""
|
"""Show pool information for backends."""
|
||||||
query_string = ""
|
query_string = ""
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""Volume interface (v2 extension)."""
|
"""Base Volume interface."""
|
||||||
|
|
||||||
from cinderclient.apiclient import base as common_base
|
from cinderclient.apiclient import base as common_base
|
||||||
from cinderclient import base
|
from cinderclient import base
|
||||||
@ -130,24 +130,6 @@ class Volume(base.Resource):
|
|||||||
"""
|
"""
|
||||||
return self.manager.show_image_metadata(self)
|
return self.manager.show_image_metadata(self)
|
||||||
|
|
||||||
def upload_to_image(self, force, image_name, container_format,
|
|
||||||
disk_format, visibility=None,
|
|
||||||
protected=None):
|
|
||||||
"""Upload a volume to image service as an image.
|
|
||||||
|
|
||||||
:param force: Boolean to enables or disables upload of a volume that
|
|
||||||
is attached to an instance.
|
|
||||||
:param image_name: The new image name.
|
|
||||||
:param container_format: Container format type.
|
|
||||||
:param disk_format: Disk format type.
|
|
||||||
:param visibility: The accessibility of image (allowed for
|
|
||||||
3.1-latest).
|
|
||||||
:param protected: Boolean to decide whether prevents image from being
|
|
||||||
deleted (allowed for 3.1-latest).
|
|
||||||
"""
|
|
||||||
return self.manager.upload_to_image(self, force, image_name,
|
|
||||||
container_format, disk_format)
|
|
||||||
|
|
||||||
def force_delete(self):
|
def force_delete(self):
|
||||||
"""Delete the specified volume ignoring its current state.
|
"""Delete the specified volume ignoring its current state.
|
||||||
|
|
||||||
@ -175,11 +157,6 @@ class Volume(base.Resource):
|
|||||||
"""
|
"""
|
||||||
return self.manager.extend(self, new_size)
|
return self.manager.extend(self, new_size)
|
||||||
|
|
||||||
def migrate_volume(self, host, force_host_copy, lock_volume):
|
|
||||||
"""Migrate the volume to a new host."""
|
|
||||||
return self.manager.migrate_volume(self, host, force_host_copy,
|
|
||||||
lock_volume)
|
|
||||||
|
|
||||||
def retype(self, volume_type, policy):
|
def retype(self, volume_type, policy):
|
||||||
"""Change a volume's type."""
|
"""Change a volume's type."""
|
||||||
return self.manager.retype(self, volume_type, policy)
|
return self.manager.retype(self, volume_type, policy)
|
||||||
@ -197,16 +174,6 @@ class Volume(base.Resource):
|
|||||||
"""
|
"""
|
||||||
return self.manager.update_readonly_flag(self, read_only)
|
return self.manager.update_readonly_flag(self, read_only)
|
||||||
|
|
||||||
def manage(self, host, ref, name=None, description=None,
|
|
||||||
volume_type=None, availability_zone=None, metadata=None,
|
|
||||||
bootable=False):
|
|
||||||
"""Manage an existing volume."""
|
|
||||||
return self.manager.manage(host=host, ref=ref, name=name,
|
|
||||||
description=description,
|
|
||||||
volume_type=volume_type,
|
|
||||||
availability_zone=availability_zone,
|
|
||||||
metadata=metadata, bootable=bootable)
|
|
||||||
|
|
||||||
def list_manageable(self, host, detailed=True, marker=None, limit=None,
|
def list_manageable(self, host, detailed=True, marker=None, limit=None,
|
||||||
offset=None, sort=None):
|
offset=None, sort=None):
|
||||||
return self.manager.list_manageable(host, detailed=detailed,
|
return self.manager.list_manageable(host, detailed=detailed,
|
||||||
@ -226,52 +193,6 @@ class VolumeManager(base.ManagerWithFind):
|
|||||||
"""Manage :class:`Volume` resources."""
|
"""Manage :class:`Volume` resources."""
|
||||||
resource_class = Volume
|
resource_class = Volume
|
||||||
|
|
||||||
def create(self, size, consistencygroup_id=None,
|
|
||||||
snapshot_id=None,
|
|
||||||
source_volid=None, name=None, description=None,
|
|
||||||
volume_type=None, user_id=None,
|
|
||||||
project_id=None, availability_zone=None,
|
|
||||||
metadata=None, imageRef=None, scheduler_hints=None):
|
|
||||||
"""Create a volume.
|
|
||||||
|
|
||||||
:param size: Size of volume in GB
|
|
||||||
:param consistencygroup_id: ID of the consistencygroup
|
|
||||||
:param snapshot_id: ID of the snapshot
|
|
||||||
:param name: Name of the volume
|
|
||||||
:param description: Description of the volume
|
|
||||||
:param volume_type: Type of volume
|
|
||||||
:param user_id: User id derived from context (IGNORED)
|
|
||||||
:param project_id: Project id derived from context (IGNORED)
|
|
||||||
:param availability_zone: Availability Zone to use
|
|
||||||
:param metadata: Optional metadata to set on volume creation
|
|
||||||
:param imageRef: reference to an image stored in glance
|
|
||||||
:param source_volid: ID of source volume to clone from
|
|
||||||
:param scheduler_hints: (optional extension) arbitrary key-value pairs
|
|
||||||
specified by the client to help boot an instance
|
|
||||||
:rtype: :class:`Volume`
|
|
||||||
"""
|
|
||||||
if metadata is None:
|
|
||||||
volume_metadata = {}
|
|
||||||
else:
|
|
||||||
volume_metadata = metadata
|
|
||||||
|
|
||||||
body = {'volume': {'size': size,
|
|
||||||
'consistencygroup_id': consistencygroup_id,
|
|
||||||
'snapshot_id': snapshot_id,
|
|
||||||
'name': name,
|
|
||||||
'description': description,
|
|
||||||
'volume_type': volume_type,
|
|
||||||
'availability_zone': availability_zone,
|
|
||||||
'metadata': volume_metadata,
|
|
||||||
'imageRef': imageRef,
|
|
||||||
'source_volid': source_volid,
|
|
||||||
}}
|
|
||||||
|
|
||||||
if scheduler_hints:
|
|
||||||
body['OS-SCH-HNT:scheduler_hints'] = scheduler_hints
|
|
||||||
|
|
||||||
return self._create('/volumes', body, 'volume')
|
|
||||||
|
|
||||||
def get(self, volume_id):
|
def get(self, volume_id):
|
||||||
"""Get a volume.
|
"""Get a volume.
|
||||||
|
|
||||||
@ -603,13 +524,6 @@ class VolumeManager(base.ManagerWithFind):
|
|||||||
}}
|
}}
|
||||||
return self._create('/os-volume-manage', body, 'volume')
|
return self._create('/os-volume-manage', body, 'volume')
|
||||||
|
|
||||||
def list_manageable(self, host, detailed=True, marker=None, limit=None,
|
|
||||||
offset=None, sort=None):
|
|
||||||
url = self._build_list_url("os-volume-manage", detailed=detailed,
|
|
||||||
search_opts={'host': host}, marker=marker,
|
|
||||||
limit=limit, offset=offset, sort=sort)
|
|
||||||
return self._list(url, "manageable-volumes")
|
|
||||||
|
|
||||||
def unmanage(self, volume):
|
def unmanage(self, volume):
|
||||||
"""Unmanage a volume."""
|
"""Unmanage a volume."""
|
||||||
return self._action('os-unmanage', volume, None)
|
return self._action('os-unmanage', volume, None)
|
@ -29,11 +29,11 @@ Running a subset of tests using tox
|
|||||||
One common activity is to just run a single test, you can do this with tox
|
One common activity is to just run a single test, you can do this with tox
|
||||||
simply by specifying to just run py3 tests against a single test::
|
simply by specifying to just run py3 tests against a single test::
|
||||||
|
|
||||||
tox -e py3 -- -n cinderclient.tests.unit.v2.test_volumes.VolumesTest.test_attach
|
tox -e py3 -- -n cinderclient.tests.unit.v3.test_volumes.VolumesTest.test_create_volume
|
||||||
|
|
||||||
Or all tests in the test_volumes.py file::
|
Or all tests in the test_volumes.py file::
|
||||||
|
|
||||||
tox -e py3 -- -n cinderclient.tests.unit.v2.test_volumes
|
tox -e py3 -- -n cinderclient.tests.unit.v3.test_volumes
|
||||||
|
|
||||||
For more information on these options and how to run tests, please see the
|
For more information on these options and how to run tests, please see the
|
||||||
`stestr documentation <https://stestr.readthedocs.io/en/latest/index.html>`_.
|
`stestr documentation <https://stestr.readthedocs.io/en/latest/index.html>`_.
|
||||||
|
5
releasenotes/notes/drop-v2-support-e578ca21c7c6b532.yaml
Normal file
5
releasenotes/notes/drop-v2-support-e578ca21c7c6b532.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
This release drops support of the Block Storage API v2. The last version
|
||||||
|
of the python-cinderclient supporting that API is the 7.x series.
|
Loading…
Reference in New Issue
Block a user