Merge "Add availability-zone-list command"

This commit is contained in:
Jenkins
2013-07-29 16:05:16 +00:00
committed by Gerrit Code Review
12 changed files with 516 additions and 0 deletions

View File

@@ -558,3 +558,58 @@ class FakeHTTPClient(base_client.HTTPClient):
def put_os_services_disable(self, body, **kw): def put_os_services_disable(self, body, **kw):
return (200, {}, {'host': body['host'], 'binary': body['binary'], return (200, {}, {'host': body['host'], 'binary': body['binary'],
'status': 'enabled'}) 'status': 'enabled'})
def get_os_availability_zone(self, **kw):
return (200, {}, {
"availabilityZoneInfo": [
{
"zoneName": "zone-1",
"zoneState": {"available": True},
"hosts": None,
},
{
"zoneName": "zone-2",
"zoneState": {"available": False},
"hosts": None,
},
]
})
def get_os_availability_zone_detail(self, **kw):
return (200, {}, {
"availabilityZoneInfo": [
{
"zoneName": "zone-1",
"zoneState": {"available": True},
"hosts": {
"fake_host-1": {
"cinder-volume": {
"active": True,
"available": True,
"updated_at":
datetime(2012, 12, 26, 14, 45, 25, 0)
}
}
}
},
{
"zoneName": "internal",
"zoneState": {"available": True},
"hosts": {
"fake_host-1": {
"cinder-sched": {
"active": True,
"available": True,
"updated_at":
datetime(2012, 12, 26, 14, 45, 24, 0)
}
}
}
},
{
"zoneName": "zone-2",
"zoneState": {"available": False},
"hosts": None,
},
]
})

View File

@@ -0,0 +1,87 @@
# 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.
import six
from cinderclient.v1 import availability_zones
from cinderclient.v1 import shell
from cinderclient.tests import utils
from cinderclient.tests.v1 import fakes
cs = fakes.FakeClient()
class AvailabilityZoneTest(utils.TestCase):
def _assertZone(self, zone, name, status):
self.assertEqual(zone.zoneName, name)
self.assertEqual(zone.zoneState, status)
def test_list_availability_zone(self):
zones = cs.availability_zones.list(detailed=False)
cs.assert_called('GET', '/os-availability-zone')
for zone in zones:
self.assertTrue(isinstance(zone,
availability_zones.AvailabilityZone))
self.assertEqual(2, len(zones))
l0 = [six.u('zone-1'), six.u('available')]
l1 = [six.u('zone-2'), six.u('not available')]
z0 = shell._treeizeAvailabilityZone(zones[0])
z1 = shell._treeizeAvailabilityZone(zones[1])
self.assertEqual((len(z0), len(z1)), (1, 1))
self._assertZone(z0[0], l0[0], l0[1])
self._assertZone(z1[0], l1[0], l1[1])
def test_detail_availability_zone(self):
zones = cs.availability_zones.list(detailed=True)
cs.assert_called('GET', '/os-availability-zone/detail')
for zone in zones:
self.assertTrue(isinstance(zone,
availability_zones.AvailabilityZone))
self.assertEqual(3, len(zones))
l0 = [six.u('zone-1'), six.u('available')]
l1 = [six.u('|- fake_host-1'), six.u('')]
l2 = [six.u('| |- cinder-volume'),
six.u('enabled :-) 2012-12-26 14:45:25')]
l3 = [six.u('internal'), six.u('available')]
l4 = [six.u('|- fake_host-1'), six.u('')]
l5 = [six.u('| |- cinder-sched'),
six.u('enabled :-) 2012-12-26 14:45:24')]
l6 = [six.u('zone-2'), six.u('not available')]
z0 = shell._treeizeAvailabilityZone(zones[0])
z1 = shell._treeizeAvailabilityZone(zones[1])
z2 = shell._treeizeAvailabilityZone(zones[2])
self.assertEqual((len(z0), len(z1), len(z2)), (3, 3, 1))
self._assertZone(z0[0], l0[0], l0[1])
self._assertZone(z0[1], l1[0], l1[1])
self._assertZone(z0[2], l2[0], l2[1])
self._assertZone(z1[0], l3[0], l3[1])
self._assertZone(z1[1], l4[0], l4[1])
self._assertZone(z1[2], l5[0], l5[1])
self._assertZone(z2[0], l6[0], l6[1])

View File

@@ -105,6 +105,10 @@ class ShellTest(utils.TestCase):
self.run_command('list --all-tenants=1') self.run_command('list --all-tenants=1')
self.assert_called('GET', '/volumes/detail?all_tenants=1') self.assert_called('GET', '/volumes/detail?all_tenants=1')
def test_list_availability_zone(self):
self.run_command('availability-zone-list')
self.assert_called('GET', '/os-availability-zone')
def test_show(self): def test_show(self):
self.run_command('show 1234') self.run_command('show 1234')
self.assert_called('GET', '/volumes/1234') self.assert_called('GET', '/volumes/1234')

View File

@@ -565,3 +565,58 @@ class FakeHTTPClient(base_client.HTTPClient):
def put_os_services_disable(self, body, **kw): def put_os_services_disable(self, body, **kw):
return (200, {}, {'host': body['host'], 'binary': body['binary'], return (200, {}, {'host': body['host'], 'binary': body['binary'],
'status': 'enabled'}) 'status': 'enabled'})
def get_os_availability_zone(self, **kw):
return (200, {}, {
"availabilityZoneInfo": [
{
"zoneName": "zone-1",
"zoneState": {"available": True},
"hosts": None,
},
{
"zoneName": "zone-2",
"zoneState": {"available": False},
"hosts": None,
},
]
})
def get_os_availability_zone_detail(self, **kw):
return (200, {}, {
"availabilityZoneInfo": [
{
"zoneName": "zone-1",
"zoneState": {"available": True},
"hosts": {
"fake_host-1": {
"cinder-volume": {
"active": True,
"available": True,
"updated_at":
datetime(2012, 12, 26, 14, 45, 25, 0)
}
}
}
},
{
"zoneName": "internal",
"zoneState": {"available": True},
"hosts": {
"fake_host-1": {
"cinder-sched": {
"active": True,
"available": True,
"updated_at":
datetime(2012, 12, 26, 14, 45, 24, 0)
}
}
}
},
{
"zoneName": "zone-2",
"zoneState": {"available": False},
"hosts": None,
},
]
})

View File

@@ -0,0 +1,87 @@
# 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.
import six
from cinderclient.v1 import availability_zones
from cinderclient.v1 import shell
from cinderclient.tests import utils
from cinderclient.tests.v1 import fakes
cs = fakes.FakeClient()
class AvailabilityZoneTest(utils.TestCase):
def _assertZone(self, zone, name, status):
self.assertEqual(zone.zoneName, name)
self.assertEqual(zone.zoneState, status)
def test_list_availability_zone(self):
zones = cs.availability_zones.list(detailed=False)
cs.assert_called('GET', '/os-availability-zone')
for zone in zones:
self.assertTrue(isinstance(zone,
availability_zones.AvailabilityZone))
self.assertEqual(2, len(zones))
l0 = [six.u('zone-1'), six.u('available')]
l1 = [six.u('zone-2'), six.u('not available')]
z0 = shell._treeizeAvailabilityZone(zones[0])
z1 = shell._treeizeAvailabilityZone(zones[1])
self.assertEqual((len(z0), len(z1)), (1, 1))
self._assertZone(z0[0], l0[0], l0[1])
self._assertZone(z1[0], l1[0], l1[1])
def test_detail_availability_zone(self):
zones = cs.availability_zones.list(detailed=True)
cs.assert_called('GET', '/os-availability-zone/detail')
for zone in zones:
self.assertTrue(isinstance(zone,
availability_zones.AvailabilityZone))
self.assertEqual(3, len(zones))
l0 = [six.u('zone-1'), six.u('available')]
l1 = [six.u('|- fake_host-1'), six.u('')]
l2 = [six.u('| |- cinder-volume'),
six.u('enabled :-) 2012-12-26 14:45:25')]
l3 = [six.u('internal'), six.u('available')]
l4 = [six.u('|- fake_host-1'), six.u('')]
l5 = [six.u('| |- cinder-sched'),
six.u('enabled :-) 2012-12-26 14:45:24')]
l6 = [six.u('zone-2'), six.u('not available')]
z0 = shell._treeizeAvailabilityZone(zones[0])
z1 = shell._treeizeAvailabilityZone(zones[1])
z2 = shell._treeizeAvailabilityZone(zones[2])
self.assertEqual((len(z0), len(z1), len(z2)), (3, 3, 1))
self._assertZone(z0[0], l0[0], l0[1])
self._assertZone(z0[1], l1[0], l1[1])
self._assertZone(z0[2], l2[0], l2[1])
self._assertZone(z1[0], l3[0], l3[1])
self._assertZone(z1[1], l4[0], l4[1])
self._assertZone(z1[2], l5[0], l5[1])
self._assertZone(z2[0], l6[0], l6[1])

View File

@@ -83,6 +83,10 @@ class ShellTest(utils.TestCase):
self.run_command('list --all-tenants=1') self.run_command('list --all-tenants=1')
self.assert_called('GET', '/volumes/detail?all_tenants=1') self.assert_called('GET', '/volumes/detail?all_tenants=1')
def test_list_availability_zone(self):
self.run_command('availability-zone-list')
self.assert_called('GET', '/os-availability-zone')
def test_show(self): def test_show(self):
self.run_command('show 1234') self.run_command('show 1234')
self.assert_called('GET', '/volumes/1234') self.assert_called('GET', '/volumes/1234')

View File

@@ -0,0 +1,42 @@
# 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 (v1 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):
"""Get a list of 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")

View File

@@ -14,6 +14,7 @@
# under the License. # under the License.
from cinderclient import client from cinderclient import client
from cinderclient.v1 import availability_zones
from cinderclient.v1 import limits from cinderclient.v1 import limits
from cinderclient.v1 import quota_classes from cinderclient.v1 import quota_classes
from cinderclient.v1 import quotas from cinderclient.v1 import quotas
@@ -64,6 +65,8 @@ class Client(object):
self.restores = volume_backups_restore.VolumeBackupRestoreManager(self) self.restores = volume_backups_restore.VolumeBackupRestoreManager(self)
self.transfers = volume_transfers.VolumeTransferManager(self) self.transfers = volume_transfers.VolumeTransferManager(self)
self.services = services.ServiceManager(self) self.services = services.ServiceManager(self)
self.availability_zones = \
availability_zones.AvailabilityZoneManager(self)
# Add in any extensions... # Add in any extensions...
if extensions: if extensions:

View File

@@ -18,12 +18,14 @@
from __future__ import print_function from __future__ import print_function
import argparse import argparse
import copy
import os import os
import sys import sys
import time import time
from cinderclient import exceptions from cinderclient import exceptions
from cinderclient import utils from cinderclient import utils
from cinderclient.v1 import availability_zones
def _poll_for_status(poll_fn, obj_id, action, final_ok_states, def _poll_for_status(poll_fn, obj_id, action, final_ok_states,
@@ -108,6 +110,11 @@ def _translate_volume_snapshot_keys(collection):
_translate_keys(collection, convert) _translate_keys(collection, convert)
def _translate_availability_zone_keys(collection):
convert = [('zoneName', 'name'), ('zoneState', 'status')]
_translate_keys(collection, convert)
def _extract_metadata(args): def _extract_metadata(args):
metadata = {} metadata = {}
for metadatum in args.metadata: for metadatum in args.metadata:
@@ -863,3 +870,63 @@ def do_service_enable(cs, args):
def do_service_disable(cs, args): def do_service_disable(cs, args):
"""Disable the service.""" """Disable the service."""
cs.services.disable(args.host, args.binary) cs.services.disable(args.host, args.binary)
def _treeizeAvailabilityZone(zone):
"""Build a tree view for availability zones."""
AvailabilityZone = availability_zones.AvailabilityZone
az = AvailabilityZone(zone.manager,
copy.deepcopy(zone._info), zone._loaded)
result = []
# Zone tree view item
az.zoneName = zone.zoneName
az.zoneState = ('available'
if zone.zoneState['available'] else 'not available')
az._info['zoneName'] = az.zoneName
az._info['zoneState'] = az.zoneState
result.append(az)
if getattr(zone, "hosts", None) and zone.hosts is not None:
for (host, services) in zone.hosts.items():
# Host tree view item
az = AvailabilityZone(zone.manager,
copy.deepcopy(zone._info), zone._loaded)
az.zoneName = '|- %s' % host
az.zoneState = ''
az._info['zoneName'] = az.zoneName
az._info['zoneState'] = az.zoneState
result.append(az)
for (svc, state) in services.items():
# Service tree view item
az = AvailabilityZone(zone.manager,
copy.deepcopy(zone._info), zone._loaded)
az.zoneName = '| |- %s' % svc
az.zoneState = '%s %s %s' % (
'enabled' if state['active'] else 'disabled',
':-)' if state['available'] else 'XXX',
state['updated_at'])
az._info['zoneName'] = az.zoneName
az._info['zoneState'] = az.zoneState
result.append(az)
return result
@utils.service_type('volume')
def do_availability_zone_list(cs, _args):
"""List all the availability zones."""
try:
availability_zones = cs.availability_zones.list()
except exceptions.Forbidden as e: # policy doesn't allow probably
try:
availability_zones = cs.availability_zones.list(detailed=False)
except Exception:
raise e
result = []
for zone in availability_zones:
result += _treeizeAvailabilityZone(zone)
_translate_availability_zone_keys(result)
utils.print_list(result, ['Name', 'Status'])

View File

@@ -0,0 +1,42 @@
# 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):
"""Get a list of 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")

View File

@@ -14,6 +14,7 @@
# under the License. # under the License.
from cinderclient import client from cinderclient import client
from cinderclient.v1 import availability_zones
from cinderclient.v2 import limits from cinderclient.v2 import limits
from cinderclient.v2 import quota_classes from cinderclient.v2 import quota_classes
from cinderclient.v2 import quotas from cinderclient.v2 import quotas
@@ -62,6 +63,8 @@ class Client(object):
self.restores = volume_backups_restore.VolumeBackupRestoreManager(self) self.restores = volume_backups_restore.VolumeBackupRestoreManager(self)
self.transfers = volume_transfers.VolumeTransferManager(self) self.transfers = volume_transfers.VolumeTransferManager(self)
self.services = services.ServiceManager(self) self.services = services.ServiceManager(self)
self.availability_zones = \
availability_zones.AvailabilityZoneManager(self)
# Add in any extensions... # Add in any extensions...
if extensions: if extensions:

View File

@@ -16,6 +16,7 @@
from __future__ import print_function from __future__ import print_function
import argparse import argparse
import copy
import os import os
import sys import sys
import time import time
@@ -24,6 +25,7 @@ import six
from cinderclient import exceptions from cinderclient import exceptions
from cinderclient import utils from cinderclient import utils
from cinderclient.v2 import availability_zones
def _poll_for_status(poll_fn, obj_id, action, final_ok_states, def _poll_for_status(poll_fn, obj_id, action, final_ok_states,
@@ -102,6 +104,11 @@ def _translate_volume_snapshot_keys(collection):
_translate_keys(collection, convert) _translate_keys(collection, convert)
def _translate_availability_zone_keys(collection):
convert = [('zoneName', 'name'), ('zoneState', 'status')]
_translate_keys(collection, convert)
def _extract_metadata(args): def _extract_metadata(args):
metadata = {} metadata = {}
for metadatum in args.metadata[0]: for metadatum in args.metadata[0]:
@@ -948,3 +955,63 @@ def do_service_enable(cs, args):
def do_service_disable(cs, args): def do_service_disable(cs, args):
"""Disable the service.""" """Disable the service."""
cs.services.disable(args.host, args.binary) cs.services.disable(args.host, args.binary)
def _treeizeAvailabilityZone(zone):
"""Build a tree view for availability zones."""
AvailabilityZone = availability_zones.AvailabilityZone
az = AvailabilityZone(zone.manager,
copy.deepcopy(zone._info), zone._loaded)
result = []
# Zone tree view item
az.zoneName = zone.zoneName
az.zoneState = ('available'
if zone.zoneState['available'] else 'not available')
az._info['zoneName'] = az.zoneName
az._info['zoneState'] = az.zoneState
result.append(az)
if getattr(zone, "hosts", None) and zone.hosts is not None:
for (host, services) in zone.hosts.items():
# Host tree view item
az = AvailabilityZone(zone.manager,
copy.deepcopy(zone._info), zone._loaded)
az.zoneName = '|- %s' % host
az.zoneState = ''
az._info['zoneName'] = az.zoneName
az._info['zoneState'] = az.zoneState
result.append(az)
for (svc, state) in services.items():
# Service tree view item
az = AvailabilityZone(zone.manager,
copy.deepcopy(zone._info), zone._loaded)
az.zoneName = '| |- %s' % svc
az.zoneState = '%s %s %s' % (
'enabled' if state['active'] else 'disabled',
':-)' if state['available'] else 'XXX',
state['updated_at'])
az._info['zoneName'] = az.zoneName
az._info['zoneState'] = az.zoneState
result.append(az)
return result
@utils.service_type('volume')
def do_availability_zone_list(cs, _args):
"""List all the availability zones."""
try:
availability_zones = cs.availability_zones.list()
except exceptions.Forbidden as e: # policy doesn't allow probably
try:
availability_zones = cs.availability_zones.list(detailed=False)
except Exception:
raise e
result = []
for zone in availability_zones:
result += _treeizeAvailabilityZone(zone)
_translate_availability_zone_keys(result)
utils.print_list(result, ['Name', 'Status'])