Add os-services extension support
Implement client bindings for Cinder os-services API extension, so client would be able to list services, enable or disable particular services. Usage: cinder service-list [--host <hostname>] [--binary <binary>] cinder service-enable <hostname> <binary> cinder service-disable <hostname> <binary> This change is depended on following change at Cinder side I7f3fa889294ca6caebdf46b8689345bcac1cdf54 Implements blueprint os-services-extension Change-Id: I4a53fd545ed3b446441302d00a429168a996a34a
This commit is contained in:
@@ -13,6 +13,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
import urlparse
|
||||
except ImportError:
|
||||
@@ -510,3 +512,49 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
transfer1 = '5678'
|
||||
return (200, {},
|
||||
{'transfer': _stub_transfer(transfer1, base_uri, tenant_id)})
|
||||
|
||||
#
|
||||
# Services
|
||||
#
|
||||
def get_os_services(self, **kw):
|
||||
host = kw.get('host', None)
|
||||
binary = kw.get('binary', None)
|
||||
services = [
|
||||
{
|
||||
'binary': 'cinder-volume',
|
||||
'host': 'host1',
|
||||
'zone': 'cinder',
|
||||
'status': 'enabled',
|
||||
'state': 'up',
|
||||
'updated_at': datetime(2012, 10, 29, 13, 42, 2)
|
||||
},
|
||||
{
|
||||
'binary': 'cinder-volume',
|
||||
'host': 'host2',
|
||||
'zone': 'cinder',
|
||||
'status': 'disabled',
|
||||
'state': 'down',
|
||||
'updated_at': datetime(2012, 9, 18, 8, 3, 38)
|
||||
},
|
||||
{
|
||||
'binary': 'cinder-scheduler',
|
||||
'host': 'host2',
|
||||
'zone': 'cinder',
|
||||
'status': 'disabled',
|
||||
'state': 'down',
|
||||
'updated_at': datetime(2012, 9, 18, 8, 3, 38)
|
||||
},
|
||||
]
|
||||
if host:
|
||||
services = filter(lambda i: i['host'] == host, services)
|
||||
if binary:
|
||||
services = filter(lambda i: i['binary'] == binary, services)
|
||||
return (200, {}, {'services': services})
|
||||
|
||||
def put_os_services_enable(self, body, **kw):
|
||||
return (200, {}, {'host': body['host'], 'binary': body['binary'],
|
||||
'status': 'disabled'})
|
||||
|
||||
def put_os_services_disable(self, body, **kw):
|
||||
return (200, {}, {'host': body['host'], 'binary': body['binary'],
|
||||
'status': 'enabled'})
|
||||
|
||||
62
cinderclient/tests/v1/test_services.py
Normal file
62
cinderclient/tests/v1/test_services.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# Copyright 2013 OpenStack LLC.
|
||||
# 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 import utils
|
||||
from cinderclient.tests.v1 import fakes
|
||||
from cinderclient.v1 import services
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
|
||||
|
||||
class ServicesTest(utils.TestCase):
|
||||
|
||||
def test_list_services(self):
|
||||
svs = cs.services.list()
|
||||
cs.assert_called('GET', '/os-services')
|
||||
self.assertEqual(len(svs), 3)
|
||||
[self.assertTrue(isinstance(s, services.Service)) for s in svs]
|
||||
|
||||
def test_list_services_with_hostname(self):
|
||||
svs = cs.services.list(host='host2')
|
||||
cs.assert_called('GET', '/os-services?host=host2')
|
||||
self.assertEqual(len(svs), 2)
|
||||
[self.assertTrue(isinstance(s, services.Service)) for s in svs]
|
||||
[self.assertEqual(s.host, 'host2') for s in svs]
|
||||
|
||||
def test_list_services_with_binary(self):
|
||||
svs = cs.services.list(binary='cinder-volume')
|
||||
cs.assert_called('GET', '/os-services?binary=cinder-volume')
|
||||
self.assertEqual(len(svs), 2)
|
||||
[self.assertTrue(isinstance(s, services.Service)) for s in svs]
|
||||
[self.assertEqual(s.binary, 'cinder-volume') for s in svs]
|
||||
|
||||
def test_list_services_with_host_binary(self):
|
||||
svs = cs.services.list('host2', 'cinder-volume')
|
||||
cs.assert_called('GET', '/os-services?host=host2&binary=cinder-volume')
|
||||
self.assertEqual(len(svs), 1)
|
||||
[self.assertTrue(isinstance(s, services.Service)) for s in svs]
|
||||
[self.assertEqual(s.host, 'host2') for s in svs]
|
||||
[self.assertEqual(s.binary, 'cinder-volume') for s in svs]
|
||||
|
||||
def test_services_enable(self):
|
||||
cs.services.enable('host1', 'cinder-volume')
|
||||
values = {"host": "host1", 'binary': 'cinder-volume'}
|
||||
cs.assert_called('PUT', '/os-services/enable', values)
|
||||
|
||||
def test_services_disable(self):
|
||||
cs.services.disable('host1', 'cinder-volume')
|
||||
values = {"host": "host1", 'binary': 'cinder-volume'}
|
||||
cs.assert_called('PUT', '/os-services/disable', values)
|
||||
@@ -12,6 +12,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
import urlparse
|
||||
except ImportError:
|
||||
@@ -517,3 +519,49 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
transfer1 = '5678'
|
||||
return (200, {},
|
||||
{'transfer': _stub_transfer(transfer1, base_uri, tenant_id)})
|
||||
|
||||
#
|
||||
# Services
|
||||
#
|
||||
def get_os_services(self, **kw):
|
||||
host = kw.get('host', None)
|
||||
binary = kw.get('binary', None)
|
||||
services = [
|
||||
{
|
||||
'binary': 'cinder-volume',
|
||||
'host': 'host1',
|
||||
'zone': 'cinder',
|
||||
'status': 'enabled',
|
||||
'state': 'up',
|
||||
'updated_at': datetime(2012, 10, 29, 13, 42, 2)
|
||||
},
|
||||
{
|
||||
'binary': 'cinder-volume',
|
||||
'host': 'host2',
|
||||
'zone': 'cinder',
|
||||
'status': 'disabled',
|
||||
'state': 'down',
|
||||
'updated_at': datetime(2012, 9, 18, 8, 3, 38)
|
||||
},
|
||||
{
|
||||
'binary': 'cinder-scheduler',
|
||||
'host': 'host2',
|
||||
'zone': 'cinder',
|
||||
'status': 'disabled',
|
||||
'state': 'down',
|
||||
'updated_at': datetime(2012, 9, 18, 8, 3, 38)
|
||||
},
|
||||
]
|
||||
if host:
|
||||
services = filter(lambda i: i['host'] == host, services)
|
||||
if binary:
|
||||
services = filter(lambda i: i['binary'] == binary, services)
|
||||
return (200, {}, {'services': services})
|
||||
|
||||
def put_os_services_enable(self, body, **kw):
|
||||
return (200, {}, {'host': body['host'], 'binary': body['binary'],
|
||||
'status': 'disabled'})
|
||||
|
||||
def put_os_services_disable(self, body, **kw):
|
||||
return (200, {}, {'host': body['host'], 'binary': body['binary'],
|
||||
'status': 'enabled'})
|
||||
|
||||
62
cinderclient/tests/v2/test_services.py
Normal file
62
cinderclient/tests/v2/test_services.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# Copyright 2013 OpenStack LLC.
|
||||
# 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 import utils
|
||||
from cinderclient.tests.v2 import fakes
|
||||
from cinderclient.v2 import services
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
|
||||
|
||||
class ServicesTest(utils.TestCase):
|
||||
|
||||
def test_list_services(self):
|
||||
svs = cs.services.list()
|
||||
cs.assert_called('GET', '/os-services')
|
||||
self.assertEqual(len(svs), 3)
|
||||
[self.assertTrue(isinstance(s, services.Service)) for s in svs]
|
||||
|
||||
def test_list_services_with_hostname(self):
|
||||
svs = cs.services.list(host='host2')
|
||||
cs.assert_called('GET', '/os-services?host=host2')
|
||||
self.assertEqual(len(svs), 2)
|
||||
[self.assertTrue(isinstance(s, services.Service)) for s in svs]
|
||||
[self.assertEqual(s.host, 'host2') for s in svs]
|
||||
|
||||
def test_list_services_with_binary(self):
|
||||
svs = cs.services.list(binary='cinder-volume')
|
||||
cs.assert_called('GET', '/os-services?binary=cinder-volume')
|
||||
self.assertEqual(len(svs), 2)
|
||||
[self.assertTrue(isinstance(s, services.Service)) for s in svs]
|
||||
[self.assertEqual(s.binary, 'cinder-volume') for s in svs]
|
||||
|
||||
def test_list_services_with_host_binary(self):
|
||||
svs = cs.services.list('host2', 'cinder-volume')
|
||||
cs.assert_called('GET', '/os-services?host=host2&binary=cinder-volume')
|
||||
self.assertEqual(len(svs), 1)
|
||||
[self.assertTrue(isinstance(s, services.Service)) for s in svs]
|
||||
[self.assertEqual(s.host, 'host2') for s in svs]
|
||||
[self.assertEqual(s.binary, 'cinder-volume') for s in svs]
|
||||
|
||||
def test_services_enable(self):
|
||||
cs.services.enable('host1', 'cinder-volume')
|
||||
values = {"host": "host1", 'binary': 'cinder-volume'}
|
||||
cs.assert_called('PUT', '/os-services/enable', values)
|
||||
|
||||
def test_services_disable(self):
|
||||
cs.services.disable('host1', 'cinder-volume')
|
||||
values = {"host": "host1", 'binary': 'cinder-volume'}
|
||||
cs.assert_called('PUT', '/os-services/disable', values)
|
||||
@@ -17,6 +17,7 @@ from cinderclient import client
|
||||
from cinderclient.v1 import limits
|
||||
from cinderclient.v1 import quota_classes
|
||||
from cinderclient.v1 import quotas
|
||||
from cinderclient.v1 import services
|
||||
from cinderclient.v1 import volumes
|
||||
from cinderclient.v1 import volume_snapshots
|
||||
from cinderclient.v1 import volume_types
|
||||
@@ -62,6 +63,7 @@ class Client(object):
|
||||
self.backups = volume_backups.VolumeBackupManager(self)
|
||||
self.restores = volume_backups_restore.VolumeBackupRestoreManager(self)
|
||||
self.transfers = volume_transfers.VolumeTransferManager(self)
|
||||
self.services = services.ServiceManager(self)
|
||||
|
||||
# Add in any extensions...
|
||||
if extensions:
|
||||
|
||||
56
cinderclient/v1/services.py
Normal file
56
cinderclient/v1/services.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# Copyright 2013 OpenStack LLC.
|
||||
# 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: %s>" % self.service
|
||||
|
||||
|
||||
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}
|
||||
self._update("/os-services/enable", body)
|
||||
|
||||
def disable(self, host, binary):
|
||||
"""Enable the service specified by hostname and binary."""
|
||||
body = {"host": host, "binary": binary}
|
||||
self._update("/os-services/disable", body)
|
||||
@@ -831,3 +831,31 @@ def do_extend(cs, args):
|
||||
"""Attempt to extend the size of an existing volume."""
|
||||
volume = _find_volume(cs, args.volume)
|
||||
cs.volumes.extend(volume, args.new_size)
|
||||
|
||||
|
||||
@utils.arg('--host', metavar='<hostname>', default=None,
|
||||
help='Name of host.')
|
||||
@utils.arg('--binary', metavar='<binary>', default=None,
|
||||
help='Service binary.')
|
||||
@utils.service_type('volume')
|
||||
def do_service_list(cs, args):
|
||||
"""List all the services. Filter by host & service binary."""
|
||||
result = cs.services.list(host=args.host, binary=args.binary)
|
||||
columns = ["Binary", "Host", "Zone", "Status", "State", "Updated_at"]
|
||||
utils.print_list(result, columns)
|
||||
|
||||
|
||||
@utils.arg('host', metavar='<hostname>', help='Name of host.')
|
||||
@utils.arg('binary', metavar='<binary>', help='Service binary.')
|
||||
@utils.service_type('volume')
|
||||
def do_service_enable(cs, args):
|
||||
"""Enable the service."""
|
||||
cs.services.enable(args.host, args.binary)
|
||||
|
||||
|
||||
@utils.arg('host', metavar='<hostname>', help='Name of host.')
|
||||
@utils.arg('binary', metavar='<binary>', help='Service binary.')
|
||||
@utils.service_type('volume')
|
||||
def do_service_disable(cs, args):
|
||||
"""Disable the service."""
|
||||
cs.services.disable(args.host, args.binary)
|
||||
|
||||
@@ -17,6 +17,7 @@ from cinderclient import client
|
||||
from cinderclient.v2 import limits
|
||||
from cinderclient.v2 import quota_classes
|
||||
from cinderclient.v2 import quotas
|
||||
from cinderclient.v2 import services
|
||||
from cinderclient.v2 import volumes
|
||||
from cinderclient.v2 import volume_snapshots
|
||||
from cinderclient.v2 import volume_types
|
||||
@@ -60,6 +61,7 @@ class Client(object):
|
||||
self.backups = volume_backups.VolumeBackupManager(self)
|
||||
self.restores = volume_backups_restore.VolumeBackupRestoreManager(self)
|
||||
self.transfers = volume_transfers.VolumeTransferManager(self)
|
||||
self.services = services.ServiceManager(self)
|
||||
|
||||
# Add in any extensions...
|
||||
if extensions:
|
||||
|
||||
56
cinderclient/v2/services.py
Normal file
56
cinderclient/v2/services.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# Copyright 2013 OpenStack LLC.
|
||||
# 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: %s>" % self.service
|
||||
|
||||
|
||||
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}
|
||||
self._update("/os-services/enable", body)
|
||||
|
||||
def disable(self, host, binary):
|
||||
"""Enable the service specified by hostname and binary."""
|
||||
body = {"host": host, "binary": binary}
|
||||
self._update("/os-services/disable", body)
|
||||
@@ -916,3 +916,31 @@ def do_extend(cs, args):
|
||||
"""Attempt to extend the size of an existing volume."""
|
||||
volume = _find_volume(cs, args.volume)
|
||||
cs.volumes.extend(volume, args.new_size)
|
||||
|
||||
|
||||
@utils.arg('--host', metavar='<hostname>', default=None,
|
||||
help='Name of host.')
|
||||
@utils.arg('--binary', metavar='<binary>', default=None,
|
||||
help='Service binary.')
|
||||
@utils.service_type('volume')
|
||||
def do_service_list(cs, args):
|
||||
"""List all the services. Filter by host & service binary."""
|
||||
result = cs.services.list(host=args.host, binary=args.binary)
|
||||
columns = ["Binary", "Host", "Zone", "Status", "State", "Updated_at"]
|
||||
utils.print_list(result, columns)
|
||||
|
||||
|
||||
@utils.arg('host', metavar='<hostname>', help='Name of host.')
|
||||
@utils.arg('binary', metavar='<binary>', help='Service binary.')
|
||||
@utils.service_type('volume')
|
||||
def do_service_enable(cs, args):
|
||||
"""Enable the service."""
|
||||
cs.services.enable(args.host, args.binary)
|
||||
|
||||
|
||||
@utils.arg('host', metavar='<hostname>', help='Name of host.')
|
||||
@utils.arg('binary', metavar='<binary>', help='Service binary.')
|
||||
@utils.service_type('volume')
|
||||
def do_service_disable(cs, args):
|
||||
"""Disable the service."""
|
||||
cs.services.disable(args.host, args.binary)
|
||||
|
||||
Reference in New Issue
Block a user