Add availability zone choice to launch instance

Fixes: bug #1186075
Implements: blueprint select-zone-when-creating-instance

* Updated project.instances.tests to include availability zone
  information where required

Change-Id: I14272a94b90587575776142da82413db88f5a99c
This commit is contained in:
Justin Shepherd 2013-04-27 19:49:13 -05:00 committed by galstrom21
parent 08f8050d88
commit b029961340
4 changed files with 93 additions and 9 deletions

View File

@ -344,12 +344,12 @@ def keypair_list(request):
def server_create(request, name, image, flavor, key_name, user_data,
security_groups, block_device_mapping, nics=None,
instance_count=1, admin_pass=None):
availability_zone=None, instance_count=1, admin_pass=None):
return Server(novaclient(request).servers.create(
name, image, flavor, userdata=user_data,
security_groups=security_groups,
key_name=key_name, block_device_mapping=block_device_mapping,
nics=nics,
nics=nics, availability_zone=availability_zone,
min_count=instance_count, admin_pass=admin_pass), request)
@ -571,3 +571,7 @@ def tenant_absolute_limits(request, reserved=False):
else:
limits_dict[limit.name] = limit.value
return limits_dict
def availability_zone_list(request, detailed=False):
return novaclient(request).availability_zones.list(detailed=detailed)

View File

@ -797,7 +797,8 @@ class InstanceTests(test.TestCase):
@test.create_stubs({api.nova: ('flavor_list',
'keypair_list',
'security_group_list',),
'security_group_list',
'availability_zone_list',),
cinder: ('volume_snapshot_list',
'volume_list',),
quotas: ('tenant_quota_usages',),
@ -836,6 +837,8 @@ class InstanceTests(test.TestCase):
.AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.nova.availability_zone_list(IsA(http.HttpRequest)) \
.AndReturn(self.availability_zones.list())
self.mox.ReplayAll()
@ -862,6 +865,7 @@ class InstanceTests(test.TestCase):
api.nova: ('flavor_list',
'keypair_list',
'security_group_list',
'availability_zone_list',
'server_create',),
cinder: ('volume_list',
'volume_snapshot_list',)})
@ -871,6 +875,7 @@ class InstanceTests(test.TestCase):
keypair = self.keypairs.first()
server = self.servers.first()
sec_group = self.security_groups.first()
avail_zone = self.availability_zones.first()
customization_script = 'user data'
nics = [{"net-id": self.networks.first().id, "v4-fixed-ip": ''}]
@ -880,6 +885,8 @@ class InstanceTests(test.TestCase):
.AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.nova.availability_zone_list(IsA(http.HttpRequest)) \
.AndReturn(self.availability_zones.list())
api.glance.image_list_detailed(IsA(http.HttpRequest),
filters={'is_public': True,
'status': 'active'}) \
@ -907,6 +914,7 @@ class InstanceTests(test.TestCase):
[sec_group.name],
None,
nics=nics,
availability_zone=avail_zone.zoneName,
instance_count=IsA(int),
admin_pass=u'')
@ -921,6 +929,7 @@ class InstanceTests(test.TestCase):
'project_id': self.tenants.first().id,
'user_id': self.user.id,
'groups': sec_group.name,
'availability_zone': avail_zone.zoneName,
'volume_type': '',
'network': self.networks.first().id,
'count': 1}
@ -936,6 +945,7 @@ class InstanceTests(test.TestCase):
api.nova: ('flavor_list',
'keypair_list',
'security_group_list',
'availability_zone_list',
'server_create',),
cinder: ('volume_list',
'volume_snapshot_list',)})
@ -946,6 +956,7 @@ class InstanceTests(test.TestCase):
server = self.servers.first()
volume = self.volumes.first()
sec_group = self.security_groups.first()
avail_zone = self.availability_zones.first()
customization_script = 'user data'
device_name = u'vda'
volume_choice = "%s:vol" % volume.id
@ -974,6 +985,8 @@ class InstanceTests(test.TestCase):
.AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.nova.availability_zone_list(IsA(http.HttpRequest)) \
.AndReturn(self.availability_zones.list())
cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list())
cinder.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
@ -1012,6 +1025,7 @@ class InstanceTests(test.TestCase):
api.nova: ('flavor_list',
'keypair_list',
'security_group_list',
'availability_zone_list',
'server_create',),
cinder: ('volume_list',
'volume_snapshot_list',)})
@ -1021,6 +1035,7 @@ class InstanceTests(test.TestCase):
server = self.servers.first()
volume = self.volumes.first()
sec_group = self.security_groups.first()
avail_zone = self.availability_zones.first()
customization_script = 'user data'
device_name = u'vda'
volume_choice = "%s:vol" % volume.id
@ -1033,6 +1048,8 @@ class InstanceTests(test.TestCase):
.AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.nova.availability_zone_list(IsA(http.HttpRequest)) \
.AndReturn(self.availability_zones.list())
api.glance.image_list_detailed(IsA(http.HttpRequest),
filters={'is_public': True,
'status': 'active'}) \
@ -1060,6 +1077,7 @@ class InstanceTests(test.TestCase):
[sec_group.name],
block_device_mapping,
nics=nics,
availability_zone=avail_zone.zoneName,
instance_count=IsA(int),
admin_pass=u'')
@ -1073,6 +1091,7 @@ class InstanceTests(test.TestCase):
'project_id': self.tenants.first().id,
'user_id': self.user.id,
'groups': sec_group.name,
'availability_zone': avail_zone.zoneName,
'volume_type': 'volume_id',
'volume_id': volume_choice,
'device_name': device_name,
@ -1090,7 +1109,8 @@ class InstanceTests(test.TestCase):
api.nova: ('server_create',
'flavor_list',
'keypair_list',
'security_group_list',),
'security_group_list',
'availability_zone_list',),
cinder: ('volume_list',
'volume_snapshot_list',)})
def test_launch_instance_post_no_images_available_boot_from_volume(self):
@ -1099,6 +1119,7 @@ class InstanceTests(test.TestCase):
server = self.servers.first()
volume = self.volumes.first()
sec_group = self.security_groups.first()
avail_zone = self.availability_zones.first()
customization_script = 'user data'
device_name = u'vda'
volume_choice = "%s:vol" % volume.id
@ -1111,6 +1132,8 @@ class InstanceTests(test.TestCase):
.AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.nova.availability_zone_list(IsA(http.HttpRequest)) \
.AndReturn(self.availability_zones.list())
api.glance.image_list_detailed(IsA(http.HttpRequest),
filters={'is_public': True,
'status': 'active'}) \
@ -1139,6 +1162,7 @@ class InstanceTests(test.TestCase):
[sec_group.name],
block_device_mapping,
nics=nics,
availability_zone=avail_zone.zoneName,
instance_count=IsA(int),
admin_pass=u'')
@ -1153,6 +1177,7 @@ class InstanceTests(test.TestCase):
'project_id': self.tenants.first().id,
'user_id': self.user.id,
'groups': sec_group.name,
'availability_zone': avail_zone.zoneName,
'network': self.networks.first().id,
'volume_type': 'volume_id',
'volume_id': volume_choice,
@ -1169,6 +1194,7 @@ class InstanceTests(test.TestCase):
quotas: ('tenant_quota_usages',),
api.nova: ('flavor_list',
'keypair_list',
'availability_zone_list',
'security_group_list',),
cinder: ('volume_list',
'volume_snapshot_list',)})
@ -1177,6 +1203,7 @@ class InstanceTests(test.TestCase):
keypair = self.keypairs.first()
server = self.servers.first()
sec_group = self.security_groups.first()
avail_zone = self.availability_zones.first()
customization_script = 'user data'
api.nova.flavor_list(IsA(http.HttpRequest)) \
@ -1203,6 +1230,8 @@ class InstanceTests(test.TestCase):
.AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.nova.availability_zone_list(IsA(http.HttpRequest)) \
.AndReturn(self.availability_zones.list())
cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn([])
cinder.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
@ -1218,6 +1247,7 @@ class InstanceTests(test.TestCase):
'project_id': self.tenants.first().id,
'user_id': self.user.id,
'groups': sec_group.name,
'availability_zone': avail_zone.zoneName,
'volume_type': '',
'count': 1}
url = reverse('horizon:project:instances:launch')
@ -1237,7 +1267,8 @@ class InstanceTests(test.TestCase):
'volume_snapshot_list',),
api.nova: ('flavor_list',
'keypair_list',
'security_group_list',)})
'security_group_list',
'availability_zone_list',)})
def test_launch_flavorlist_error(self):
cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list())
@ -1268,6 +1299,8 @@ class InstanceTests(test.TestCase):
.AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.nova.availability_zone_list(IsA(http.HttpRequest)) \
.AndReturn(self.availability_zones.list())
self.mox.ReplayAll()
@ -1281,6 +1314,7 @@ class InstanceTests(test.TestCase):
api.nova: ('flavor_list',
'keypair_list',
'security_group_list',
'availability_zone_list',
'server_create',),
cinder: ('volume_list',
'volume_snapshot_list',)})
@ -1290,6 +1324,7 @@ class InstanceTests(test.TestCase):
keypair = self.keypairs.first()
server = self.servers.first()
sec_group = self.security_groups.first()
avail_zone = self.availability_zones.first()
customization_script = 'userData'
nics = [{"net-id": self.networks.first().id, "v4-fixed-ip": ''}]
@ -1299,6 +1334,8 @@ class InstanceTests(test.TestCase):
api.nova.keypair_list(IgnoreArg()).AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.nova.availability_zone_list(IsA(http.HttpRequest)) \
.AndReturn(self.availability_zones.list())
api.glance.image_list_detailed(IsA(http.HttpRequest),
filters={'is_public': True,
'status': 'active'}) \
@ -1324,6 +1361,7 @@ class InstanceTests(test.TestCase):
[sec_group.name],
None,
nics=nics,
availability_zone=avail_zone.zoneName,
instance_count=IsA(int),
admin_pass='password') \
.AndRaise(self.exceptions.keystone)
@ -1333,6 +1371,7 @@ class InstanceTests(test.TestCase):
form_data = {'flavor': flavor.id,
'source_type': 'image_id',
'image_id': image.id,
'availability_zone': avail_zone.zoneName,
'keypair': keypair.name,
'name': server.name,
'customization_script': customization_script,
@ -1354,7 +1393,8 @@ class InstanceTests(test.TestCase):
quotas: ('tenant_quota_usages',),
api.nova: ('flavor_list',
'keypair_list',
'security_group_list',),
'security_group_list',
'availability_zone_list',),
cinder: ('volume_list',
'volume_snapshot_list',)})
def test_launch_form_instance_count_error(self):
@ -1364,6 +1404,7 @@ class InstanceTests(test.TestCase):
server = self.servers.first()
volume = self.volumes.first()
sec_group = self.security_groups.first()
avail_zone = self.availability_zones.first()
customization_script = 'user data'
device_name = u'vda'
volume_choice = "%s:vol" % volume.id
@ -1374,6 +1415,8 @@ class InstanceTests(test.TestCase):
.AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.nova.availability_zone_list(IsA(http.HttpRequest)) \
.AndReturn(self.availability_zones.list())
api.glance.image_list_detailed(IsA(http.HttpRequest),
filters={'is_public': True,
'status': 'active'}) \
@ -1403,6 +1446,7 @@ class InstanceTests(test.TestCase):
form_data = {'flavor': flavor.id,
'source_type': 'image_id',
'image_id': image.id,
'availability_zone': avail_zone.zoneName,
'keypair': keypair.name,
'name': server.name,
'customization_script': customization_script,
@ -1469,7 +1513,8 @@ class InstanceTests(test.TestCase):
@test.create_stubs({api.nova: ('flavor_list',
'keypair_list',
'security_group_list',),
'security_group_list',
'availability_zone_list',),
cinder: ('volume_snapshot_list',
'volume_list',),
quotas: ('tenant_quota_usages',),
@ -1509,6 +1554,8 @@ class InstanceTests(test.TestCase):
.AndReturn([keypair])
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.nova.availability_zone_list(IsA(http.HttpRequest)) \
.AndReturn(self.availability_zones.list())
self.mox.ReplayAll()

View File

@ -179,6 +179,8 @@ class SetInstanceDetailsAction(workflows.Action):
image_id = forms.ChoiceField(label=_("Image"), required=False)
instance_snapshot_id = forms.ChoiceField(label=_("Instance Snapshot"),
required=False)
availability_zone = forms.ChoiceField(label=_("Availability Zone"),
required=False)
name = forms.CharField(max_length=80, label=_("Instance Name"))
flavor = forms.ChoiceField(label=_("Flavor"),
help_text=_("Size of image to launch."))
@ -271,6 +273,23 @@ class SetInstanceDetailsAction(workflows.Action):
_('Unable to retrieve instance flavors.'))
return sorted(flavor_list)
def populate_availability_zone_choices(self, request, context):
try:
zones = api.nova.availability_zone_list(request)
except:
zones = []
exceptions.handle(request,
_('Unable to retrieve availability zones.'))
zone_list = [(zone.zoneName, zone.zoneName)
for zone in zones if zone.zoneState['available']]
zone_list.sort()
if zone_list:
zone_list.insert(0, ("", _("Any Availability Zone")))
else:
zone_list.insert(0, ("", _("No availability zones found.")))
return zone_list
def get_help_text(self):
extra = {}
try:
@ -287,7 +306,8 @@ class SetInstanceDetailsAction(workflows.Action):
class SetInstanceDetails(workflows.Step):
action_class = SetInstanceDetailsAction
contributes = ("source_type", "source_id", "name", "count", "flavor")
contributes = ("source_type", "source_id", "availability_zone",
"name", "count", "flavor")
def prepare_action_context(self, request, context):
if 'source_type' in context and 'source_id' in context:
@ -504,6 +524,8 @@ class LaunchInstance(workflows.Workflow):
else:
nics = None
avail_zone = context.get('availability_zone', None)
try:
api.nova.server_create(request,
context['name'],
@ -514,6 +536,7 @@ class LaunchInstance(workflows.Workflow):
context['security_group_ids'],
dev_mapping,
nics=nics,
availability_zone=avail_zone,
instance_count=int(context['count']),
admin_pass=context['admin_pass'])
return True

View File

@ -21,7 +21,8 @@ from novaclient.v1_1 import (flavors, keypairs, servers, volumes,
floating_ips, usage, certs,
volume_snapshots as vol_snaps,
security_group_rules as rules,
security_groups as sec_groups)
security_groups as sec_groups,
availability_zones)
from openstack_dashboard.api.base import Quota, QuotaSet as QuotaSetWrapper
from openstack_dashboard.api.nova import FloatingIp as NetFloatingIp
@ -153,6 +154,7 @@ def data(TEST):
TEST.certs = TestDataContainer()
TEST.volume_snapshots = TestDataContainer()
TEST.volume_types = TestDataContainer()
TEST.availability_zones = TestDataContainer()
# Data return by novaclient.
# It is used if API layer does data conversion.
@ -448,3 +450,11 @@ def data(TEST):
'data': 'certificate_data'}
certificate = certs.Certificate(certs.CertificateManager(None), cert_data)
TEST.certs.add(certificate)
# Availability Zones
TEST.availability_zones.add(
availability_zones.AvailabilityZone(
availability_zones.AvailabilityZoneManager(None),
{'zoneName': 'nova', 'zoneState': {'available': True}}
)
)