Add availability_zone to service

Add a new attribute 'availability_zone' to the 'service' resource.
This attribute indicates the availability zone of the zun-compute
agent. Cloud administrators can customize the availability zone of
each compute node by setting the following in the config file:

  [DEFAULT]
  default_availability_zone = my-zone

If this is not set, the default availability zone is 'nova'
(we use this value for consistency with neutron and cinder's defaults).

In the future, we will add support for scheduling containers to
a specified AZ and the 'availability_zone' attribute will be used
by scheduler to check if the availability zone of the compute node
equals to the one requested by API users.

Change-Id: I4a4206b4eb0aa5149bbfc8ab72ae408a08317de4
Partial-Implements: blueprint zun-availability-zone
This commit is contained in:
Hongbin Lu 2018-02-10 22:46:23 +00:00
parent 4ef02ca76c
commit df66e4cf58
10 changed files with 108 additions and 7 deletions

View File

@ -21,9 +21,13 @@ from zun.api import servicegroup as svcgrp_api
from zun.common import exception
from zun.common import policy
from zun.common import validation
import zun.conf
from zun import objects
CONF = zun.conf.CONF
class ZunServiceCollection(collection.Collection):
fields = {
@ -41,11 +45,13 @@ class ZunServiceCollection(collection.Collection):
collection = ZunServiceCollection()
collection.services = []
for p in rpc_hsvcs:
hsvc = p.as_dict()
service = p.as_dict()
alive = servicegroup_api.service_is_up(p)
state = 'up' if alive else 'down'
hsvc['state'] = state
collection.services.append(hsvc)
service['state'] = state
collection.services.append(service)
if not service['availability_zone']:
service['availability_zone'] = CONF.default_availability_zone
next = collection.get_next(limit=None, url=None, **kwargs)
if next is not None:
collection.next = next

View File

@ -15,6 +15,7 @@
from oslo_config import cfg
from zun.conf import api
from zun.conf import availability_zone
from zun.conf import cinder_client
from zun.conf import compute
from zun.conf import container_driver
@ -61,3 +62,4 @@ pci.register_opts(CONF)
volume.register_opts(CONF)
cinder_client.register_opts(CONF)
netconf.register_opts(CONF)
availability_zone.register_opts(CONF)

View File

@ -0,0 +1,36 @@
# 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 oslo_config import cfg
availability_zone_opts = [
cfg.StrOpt('default_availability_zone',
default='nova',
help="""
Default availability zone for compute services.
This option determines the default availability zone for 'zun-compute'
services.
Possible values:
* Any string representing an existing availability zone name.
"""),
]
def register_opts(conf):
conf.register_opts(availability_zone_opts)
def list_opts():
return {'DEFAULT': availability_zone_opts}

View File

@ -0,0 +1,34 @@
# 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.
"""add availability_zone to service
Revision ID: 3f49fa520409
Revises: 50829990c965
Create Date: 2018-02-10 22:33:22.890723
"""
# revision identifiers, used by Alembic.
revision = '3f49fa520409'
down_revision = '50829990c965'
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('zun_service',
sa.Column('availability_zone', sa.String(255),
nullable=True))

View File

@ -122,6 +122,7 @@ class ZunService(Base):
last_seen_up = Column(DateTime, nullable=True)
forced_down = Column(Boolean, default=False)
report_count = Column(Integer, nullable=False, default=0)
availability_zone = Column(String(255), nullable=True)
class Container(Base):

View File

@ -21,7 +21,8 @@ class ZunService(base.ZunPersistentObject, base.ZunObject):
# Version 1.0: Initial version
# Version 1.1: Add update method
VERSION = '1.1'
# Version 1.2: Add availability_zone field
VERSION = '1.2'
fields = {
'id': fields.IntegerField(),
@ -32,6 +33,7 @@ class ZunService(base.ZunPersistentObject, base.ZunObject):
'last_seen_up': fields.DateTimeField(nullable=True),
'forced_down': fields.BooleanField(),
'report_count': fields.IntegerField(),
'availability_zone': fields.StringField(nullable=True),
}
@staticmethod

View File

@ -11,6 +11,7 @@
# limitations under the License.
import mock
from oslo_config import cfg
from zun.api import servicegroup
from zun import objects
@ -35,10 +36,10 @@ class TestZunServiceController(api_base.FunctionalTest):
response = self.get_json('/services')
self.assertEqual([], response['services'])
def _rpc_api_reply(self, count=1):
def _rpc_api_reply(self, count=1, **kwarg):
reclist = []
for i in range(count):
elem = api_utils.zservice_get_data()
elem = api_utils.zservice_get_data(**kwarg)
elem['id'] = i + 1
rec = DbRec(elem)
reclist.append(rec)
@ -55,6 +56,8 @@ class TestZunServiceController(api_base.FunctionalTest):
response = self.get_json('/services')
self.assertEqual(1, len(response['services']))
self.assertEqual(1, response['services'][0]['id'])
self.assertEqual('fake-zone',
response['services'][0]['availability_zone'])
@mock.patch('zun.common.policy.enforce')
@mock.patch.object(objects.ZunService, 'list')
@ -71,6 +74,21 @@ class TestZunServiceController(api_base.FunctionalTest):
elem = response['services'][i]
self.assertEqual(elem['id'], i + 1)
@mock.patch('zun.common.policy.enforce')
@mock.patch.object(objects.ZunService, 'list')
@mock.patch.object(servicegroup.ServiceGroup, 'service_is_up')
def test_default_availability_zone(self, svc_up, mock_list, mock_policy):
cfg.CONF.set_override("default_availability_zone", "default-zone")
mock_policy.return_value = True
mock_list.return_value = self._rpc_api_reply(availability_zone=None)
svc_up.return_value = "up"
response = self.get_json('/services')
self.assertEqual(1, len(response['services']))
self.assertEqual(1, response['services'][0]['id'])
self.assertEqual('default-zone',
response['services'][0]['availability_zone'])
@mock.patch('zun.common.policy.enforce')
@mock.patch.object(objects.ZunService, 'get_by_host_and_binary')
@mock.patch.object(objects.ZunService, 'update')

View File

@ -31,4 +31,5 @@ def zservice_get_data(**kwargs):
'last_seen_up': kwargs.get('last_seen_up', faketime),
'created_at': kwargs.get('created_at', faketime),
'updated_at': kwargs.get('updated_at', faketime),
'availability_zone': kwargs.get('availability_zone', 'fake-zone'),
}

View File

@ -198,6 +198,7 @@ def get_test_zun_service(**kwargs):
'report_count': kwargs.get('report_count', 13),
'created_at': kwargs.get('created_at'),
'updated_at': kwargs.get('updated_at'),
'availability_zone': kwargs.get('availability_zone', 'fake-zone'),
}

View File

@ -352,7 +352,7 @@ object_data = {
'NUMATopology': '1.0-b54086eda7e4b2e6145ecb6ee2c925ab',
'ResourceClass': '1.1-d661c7675b3cd5b8c3618b68ba64324e',
'ResourceProvider': '1.0-92b427359d5a4cf9ec6c72cbe630ee24',
'ZunService': '1.1-b1549134bfd5271daec417ca8cabc77e',
'ZunService': '1.2-deff2a74a9ce23baa231ae12f39a6189',
'Capsule': '1.5-cbdaffa78fa68c26cf4a61d8f75dd32d',
'PciDevice': '1.1-6e3f0851ad1cf12583e6af4df1883979',
'ComputeNode': '1.9-e8536102d3b28cb3378e9e26f508cd72',