Added support of availability zones for compute.
models.Service got additional field availability_zone and was created ZoneScheduler that make decisions based on this field. Also replaced fake 'nova' zone in EC2 cloud api.
This commit is contained in:
1
Authors
1
Authors
@@ -15,6 +15,7 @@ Eldar Nugaev <enugaev@griddynamics.com>
|
||||
Eric Day <eday@oddments.org>
|
||||
Ewan Mellor <ewan.mellor@citrix.com>
|
||||
Hisaki Ohara <hisaki.ohara@intel.com>
|
||||
Ilya Alekseyev <ialekseev@griddynamics.com>
|
||||
Jay Pipes <jaypipes@gmail.com>
|
||||
Jesse Andrews <anotherjesse@gmail.com>
|
||||
Joe Heck <heckj@mac.com>
|
||||
|
@@ -308,6 +308,5 @@ DEFINE_string('image_service', 'nova.image.s3.S3ImageService',
|
||||
DEFINE_string('host', socket.gethostname(),
|
||||
'name of this node')
|
||||
|
||||
# UNUSED
|
||||
DEFINE_string('node_availability_zone', 'nova',
|
||||
'availability zone of this node')
|
||||
|
56
nova/scheduler/zone.py
Normal file
56
nova/scheduler/zone.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2010 Openstack, LLC.
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# 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 Scheduler implementation
|
||||
"""
|
||||
|
||||
import random
|
||||
|
||||
from nova.scheduler import driver
|
||||
from nova import db
|
||||
|
||||
|
||||
class ZoneScheduler(driver.Scheduler):
|
||||
"""Implements Scheduler as a random node selector."""
|
||||
|
||||
def hosts_up_with_zone(self, context, topic, zone):
|
||||
"""Return the list of hosts that have a running service
|
||||
for topic and availability zone (if defined).
|
||||
"""
|
||||
|
||||
if zone is None:
|
||||
return self.hosts_up(context, topic)
|
||||
|
||||
services = db.service_get_all_by_topic(context, topic)
|
||||
return [service.host
|
||||
for service in services
|
||||
if self.service_is_up(service)
|
||||
and service.availability_zone == zone]
|
||||
|
||||
def schedule(self, context, topic, *_args, **_kwargs):
|
||||
"""Picks a host that is up at random in selected
|
||||
availability zone (if defined).
|
||||
"""
|
||||
|
||||
zone = _kwargs.get('availability_zone')
|
||||
hosts = self.hosts_up_with_zone(context, topic, zone)
|
||||
if not hosts:
|
||||
raise driver.NoValidHost(_("No hosts found"))
|
||||
return hosts[int(random.random() * len(hosts))]
|
@@ -133,10 +133,35 @@ class CloudTestCase(test.TestCase):
|
||||
db.volume_destroy(self.context, vol1['id'])
|
||||
db.volume_destroy(self.context, vol2['id'])
|
||||
|
||||
def test_describe_availability_zones(self):
|
||||
"""Makes sure describe_availability_zones works and filters results."""
|
||||
service1 = db.service_create(self.context, {'host': 'host1_zones',
|
||||
'binary': "nova-compute",
|
||||
'topic': 'compute',
|
||||
'report_count': 0,
|
||||
'availability_zone': "zone1"})
|
||||
service2 = db.service_create(self.context, {'host': 'host2_zones',
|
||||
'binary': "nova-compute",
|
||||
'topic': 'compute',
|
||||
'report_count': 0,
|
||||
'availability_zone': "zone2"})
|
||||
result = self.cloud.describe_availability_zones(self.context)
|
||||
self.assertEqual(len(result['availabilityZoneInfo']), 3)
|
||||
db.service_destroy(self.context, service1['id'])
|
||||
db.service_destroy(self.context, service2['id'])
|
||||
|
||||
def test_describe_instances(self):
|
||||
"""Makes sure describe_instances works and filters results."""
|
||||
inst1 = db.instance_create(self.context, {'reservation_id': 'a'})
|
||||
inst2 = db.instance_create(self.context, {'reservation_id': 'a'})
|
||||
inst1 = db.instance_create(self.context, {'reservation_id': 'a',
|
||||
'host': 'host1'})
|
||||
inst2 = db.instance_create(self.context, {'reservation_id': 'a',
|
||||
'host': 'host2'})
|
||||
comp1 = db.service_create(self.context, {'host': 'host1',
|
||||
'availability_zone': 'zone1',
|
||||
'topic': "compute"})
|
||||
comp2 = db.service_create(self.context, {'host': 'host2',
|
||||
'availability_zone': 'zone2',
|
||||
'topic': "compute"})
|
||||
result = self.cloud.describe_instances(self.context)
|
||||
result = result['reservationSet'][0]
|
||||
self.assertEqual(len(result['instancesSet']), 2)
|
||||
@@ -147,8 +172,12 @@ class CloudTestCase(test.TestCase):
|
||||
self.assertEqual(len(result['instancesSet']), 1)
|
||||
self.assertEqual(result['instancesSet'][0]['instanceId'],
|
||||
instance_id)
|
||||
self.assertEqual(result['instancesSet'][0]
|
||||
['placement']['availabilityZone'], 'zone2')
|
||||
db.instance_destroy(self.context, inst1['id'])
|
||||
db.instance_destroy(self.context, inst2['id'])
|
||||
db.service_destroy(self.context, comp1['id'])
|
||||
db.service_destroy(self.context, comp2['id'])
|
||||
|
||||
def test_console_output(self):
|
||||
image_id = FLAGS.default_image
|
||||
@@ -241,6 +270,19 @@ class CloudTestCase(test.TestCase):
|
||||
LOG.debug(_("Terminating instance %s"), instance_id)
|
||||
rv = self.compute.terminate_instance(instance_id)
|
||||
|
||||
def test_describe_instances(self):
|
||||
"""Makes sure describe_instances works."""
|
||||
instance1 = db.instance_create(self.context, {'host': 'host2'})
|
||||
comp1 = db.service_create(self.context, {'host': 'host2',
|
||||
'availability_zone': 'zone1',
|
||||
'topic': "compute"})
|
||||
result = self.cloud.describe_instances(self.context)
|
||||
self.assertEqual(result['reservationSet'][0]
|
||||
['instancesSet'][0]
|
||||
['placement']['availabilityZone'], 'zone1')
|
||||
db.instance_destroy(self.context, instance1['id'])
|
||||
db.service_destroy(self.context, comp1['id'])
|
||||
|
||||
def test_instance_update_state(self):
|
||||
def instance(num):
|
||||
return {
|
||||
|
@@ -21,6 +21,7 @@ Tests For Scheduler
|
||||
|
||||
import datetime
|
||||
|
||||
from mox import IgnoreArg
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import flags
|
||||
@@ -76,6 +77,59 @@ class SchedulerTestCase(test.TestCase):
|
||||
scheduler.named_method(ctxt, 'topic', num=7)
|
||||
|
||||
|
||||
class ZoneSchedulerTestCase(test.TestCase):
|
||||
"""Test case for zone scheduler"""
|
||||
def setUp(self):
|
||||
super(ZoneSchedulerTestCase, self).setUp()
|
||||
self.flags(scheduler_driver='nova.scheduler.zone.ZoneScheduler')
|
||||
|
||||
def _create_service_model(self, **kwargs):
|
||||
service = db.sqlalchemy.models.Service()
|
||||
service.host = kwargs['host']
|
||||
service.disabled = False
|
||||
service.deleted = False
|
||||
service.report_count = 0
|
||||
service.binary = 'nova-compute'
|
||||
service.topic = 'compute'
|
||||
service.id = kwargs['id']
|
||||
service.availability_zone = kwargs['zone']
|
||||
service.created_at = datetime.datetime.utcnow()
|
||||
return service
|
||||
|
||||
def test_with_two_zones(self):
|
||||
scheduler = manager.SchedulerManager()
|
||||
ctxt = context.get_admin_context()
|
||||
service_list = [self._create_service_model(id=1,
|
||||
host='host1',
|
||||
zone='zone1'),
|
||||
self._create_service_model(id=2,
|
||||
host='host2',
|
||||
zone='zone2'),
|
||||
self._create_service_model(id=3,
|
||||
host='host3',
|
||||
zone='zone2'),
|
||||
self._create_service_model(id=4,
|
||||
host='host4',
|
||||
zone='zone2'),
|
||||
self._create_service_model(id=5,
|
||||
host='host5',
|
||||
zone='zone2')]
|
||||
self.mox.StubOutWithMock(db, 'service_get_all_by_topic')
|
||||
arg = IgnoreArg()
|
||||
db.service_get_all_by_topic(arg, arg).AndReturn(service_list)
|
||||
self.mox.StubOutWithMock(rpc, 'cast', use_mock_anything=True)
|
||||
rpc.cast(ctxt,
|
||||
'compute.host1',
|
||||
{'method': 'run_instance',
|
||||
'args': {'instance_id': 'i-ffffffff',
|
||||
'availability_zone': 'zone1'}})
|
||||
self.mox.ReplayAll()
|
||||
scheduler.run_instance(ctxt,
|
||||
'compute',
|
||||
instance_id='i-ffffffff',
|
||||
availability_zone='zone1')
|
||||
|
||||
|
||||
class SimpleDriverTestCase(test.TestCase):
|
||||
"""Test case for simple driver"""
|
||||
def setUp(self):
|
||||
|
Reference in New Issue
Block a user