Merge "Allow master_flavor_id on cluster create"

This commit is contained in:
Jenkins 2017-10-05 10:44:50 +00:00 committed by Gerrit Code Review
commit 2bf7e34498
18 changed files with 89 additions and 24 deletions

View File

@ -39,6 +39,7 @@ Request
- node_count: node_count - node_count: node_count
- create_timeout: create_timeout - create_timeout: create_timeout
- keypair: keypair_id - keypair: keypair_id
- master_flavor_id: master_flavor_id
.. note:: .. note::

View File

@ -5,5 +5,6 @@
"cluster_template_id":"0562d357-8641-4759-8fed-8173f02c9633", "cluster_template_id":"0562d357-8641-4759-8fed-8173f02c9633",
"node_count":2, "node_count":2,
"create_timeout":60, "create_timeout":60,
"keypair":"my_keypair" "keypair":"my_keypair",
"master_flavor_id":null
} }

View File

@ -211,7 +211,7 @@ They are loosely grouped as: mandatory, infrastructure, COE specific.
--master-flavor \<master-flavor\> --master-flavor \<master-flavor\>
The nova flavor id for booting the master or manager servers. The The nova flavor id for booting the master or manager servers. The
default is 'm1.small'. default is 'm1.small'. This value can be overridden at cluster creation.
--http-proxy \<http-proxy\> --http-proxy \<http-proxy\>
The IP address for a proxy to use when direct http access from the The IP address for a proxy to use when direct http access from the

View File

@ -95,6 +95,9 @@ class Bay(base.APIBase):
labels = wtypes.DictType(str, str) labels = wtypes.DictType(str, str)
"""One or more key/value pairs""" """One or more key/value pairs"""
master_flavor_id = wtypes.StringType(min_length=1, max_length=255)
"""The master flavor of this Bay"""
bay_create_timeout = wsme.wsattr(wtypes.IntegerType(minimum=0), default=60) bay_create_timeout = wsme.wsattr(wtypes.IntegerType(minimum=0), default=60)
"""Timeout for creating the bay in minutes. Default to 60 if not set""" """Timeout for creating the bay in minutes. Default to 60 if not set"""
@ -178,6 +181,7 @@ class Bay(base.APIBase):
if not expand: if not expand:
bay.unset_fields_except(['uuid', 'name', 'baymodel_id', bay.unset_fields_except(['uuid', 'name', 'baymodel_id',
'docker_volume_size', 'labels', 'docker_volume_size', 'labels',
'master_flavor_id',
'node_count', 'status', 'node_count', 'status',
'bay_create_timeout', 'master_count', 'bay_create_timeout', 'master_count',
'stack_id']) 'stack_id'])
@ -203,6 +207,7 @@ class Bay(base.APIBase):
master_count=1, master_count=1,
docker_volume_size=1, docker_volume_size=1,
labels={}, labels={},
master_flavor_id=None,
bay_create_timeout=15, bay_create_timeout=15,
stack_id='49dc23f5-ffc9-40c3-9d34-7be7f9e34d63', stack_id='49dc23f5-ffc9-40c3-9d34-7be7f9e34d63',
status=fields.ClusterStatus.CREATE_COMPLETE, status=fields.ClusterStatus.CREATE_COMPLETE,
@ -432,6 +437,10 @@ class BaysController(base.Controller):
if bay.labels is None: if bay.labels is None:
bay.labels = baymodel.labels bay.labels = baymodel.labels
# If master_flavor_id is not present, use baymodel value
if bay.master_flavor_id == wtypes.Unset or not bay.master_flavor_id:
bay.master_flavor_id = baymodel.master_flavor_id
bay_dict = bay.as_dict() bay_dict = bay.as_dict()
bay_dict['keypair'] = baymodel.keypair_id bay_dict['keypair'] = baymodel.keypair_id
attr_validator.validate_os_resources(context, baymodel.as_dict(), attr_validator.validate_os_resources(context, baymodel.as_dict(),

View File

@ -113,6 +113,9 @@ class Cluster(base.APIBase):
labels = wtypes.DictType(str, str) labels = wtypes.DictType(str, str)
"""One or more key/value pairs""" """One or more key/value pairs"""
master_flavor_id = wtypes.StringType(min_length=1, max_length=255)
"""The flavor of the master node for this Cluster"""
create_timeout = wsme.wsattr(wtypes.IntegerType(minimum=0), default=60) create_timeout = wsme.wsattr(wtypes.IntegerType(minimum=0), default=60)
"""Timeout for creating the cluster in minutes. Default to 60 if not set""" """Timeout for creating the cluster in minutes. Default to 60 if not set"""
@ -166,6 +169,7 @@ class Cluster(base.APIBase):
cluster.unset_fields_except(['uuid', 'name', 'cluster_template_id', cluster.unset_fields_except(['uuid', 'name', 'cluster_template_id',
'keypair', 'docker_volume_size', 'keypair', 'docker_volume_size',
'labels', 'node_count', 'status', 'labels', 'node_count', 'status',
'master_flavor_id',
'create_timeout', 'master_count', 'create_timeout', 'master_count',
'stack_id']) 'stack_id'])
@ -192,6 +196,7 @@ class Cluster(base.APIBase):
master_count=1, master_count=1,
docker_volume_size=1, docker_volume_size=1,
labels={}, labels={},
master_flavor_id='m1.small',
create_timeout=15, create_timeout=15,
stack_id='49dc23f5-ffc9-40c3-9d34-7be7f9e34d63', stack_id='49dc23f5-ffc9-40c3-9d34-7be7f9e34d63',
status=fields.ClusterStatus.CREATE_COMPLETE, status=fields.ClusterStatus.CREATE_COMPLETE,
@ -411,6 +416,11 @@ class ClustersController(base.Controller):
if cluster.labels == wtypes.Unset: if cluster.labels == wtypes.Unset:
cluster.labels = cluster_template.labels cluster.labels = cluster_template.labels
# If master_flavor_id is not present, use cluster_template value
if (cluster.master_flavor_id == wtypes.Unset or
not cluster.master_flavor_id):
cluster.master_flavor_id = cluster_template.master_flavor_id
cluster_dict = cluster.as_dict() cluster_dict = cluster.as_dict()
attr_validator.validate_os_resources(context, attr_validator.validate_os_resources(context,

View File

@ -0,0 +1,30 @@
# 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 master_flavor_id to cluster
Revision ID: 52bcaf58fecb
Revises: a0e7c8450ab1
Create Date: 2017-08-01 11:22:31.277745
"""
# revision identifiers, used by Alembic.
revision = '52bcaf58fecb'
down_revision = 'a0e7c8450ab1'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('cluster', sa.Column('master_flavor_id',
sa.String(length=255), nullable=True))

View File

@ -117,6 +117,7 @@ class Cluster(Base):
keypair = Column(String(255)) keypair = Column(String(255))
docker_volume_size = Column(Integer()) docker_volume_size = Column(Integer())
labels = Column(JSONEncodedDict) labels = Column(JSONEncodedDict)
master_flavor_id = Column(String(255))
stack_id = Column(String(255)) stack_id = Column(String(255))
api_address = Column(String(255)) api_address = Column(String(255))
node_addresses = Column(JSONEncodedList) node_addresses = Column(JSONEncodedList)

View File

@ -52,7 +52,7 @@ class K8sTemplateDefinition(template_def.BaseTemplateDefinition):
def __init__(self): def __init__(self):
super(K8sTemplateDefinition, self).__init__() super(K8sTemplateDefinition, self).__init__()
self.add_parameter('master_flavor', self.add_parameter('master_flavor',
cluster_template_attr='master_flavor_id') cluster_attr='master_flavor_id')
self.add_parameter('minion_flavor', self.add_parameter('minion_flavor',
cluster_template_attr='flavor_id') cluster_template_attr='flavor_id')
self.add_parameter('number_of_minions', self.add_parameter('number_of_minions',

View File

@ -48,7 +48,7 @@ class SwarmFedoraTemplateDefinition(template_def.BaseTemplateDefinition):
self.add_parameter('number_of_nodes', self.add_parameter('number_of_nodes',
cluster_attr='node_count') cluster_attr='node_count')
self.add_parameter('master_flavor', self.add_parameter('master_flavor',
cluster_template_attr='master_flavor_id') cluster_attr='master_flavor_id')
self.add_parameter('node_flavor', self.add_parameter('node_flavor',
cluster_template_attr='flavor_id') cluster_template_attr='flavor_id')
self.add_parameter('docker_volume_size', self.add_parameter('docker_volume_size',

View File

@ -61,7 +61,7 @@ class SwarmModeTemplateDefinition(template_def.BaseTemplateDefinition):
self.add_parameter('number_of_nodes', self.add_parameter('number_of_nodes',
cluster_attr='node_count') cluster_attr='node_count')
self.add_parameter('master_flavor', self.add_parameter('master_flavor',
cluster_template_attr='master_flavor_id') cluster_attr='master_flavor_id')
self.add_parameter('node_flavor', self.add_parameter('node_flavor',
cluster_template_attr='flavor_id') cluster_template_attr='flavor_id')
self.add_parameter('docker_volume_size', self.add_parameter('docker_volume_size',

View File

@ -31,7 +31,7 @@ class UbuntuMesosTemplateDefinition(template_def.BaseTemplateDefinition):
self.add_parameter('number_of_slaves', self.add_parameter('number_of_slaves',
cluster_attr='node_count') cluster_attr='node_count')
self.add_parameter('master_flavor', self.add_parameter('master_flavor',
cluster_template_attr='master_flavor_id') cluster_attr='master_flavor_id')
self.add_parameter('slave_flavor', self.add_parameter('slave_flavor',
cluster_template_attr='flavor_id') cluster_template_attr='flavor_id')
self.add_parameter('cluster_name', self.add_parameter('cluster_name',

View File

@ -44,8 +44,9 @@ class Cluster(base.MagnumPersistentObject, base.MagnumObject,
# Version 1.13: Added get_count_all method # Version 1.13: Added get_count_all method
# Version 1.14: Added 'docker_volume_size' field # Version 1.14: Added 'docker_volume_size' field
# Version 1.15: Added 'labels' field # Version 1.15: Added 'labels' field
# Version 1.16: Added 'master_flavor_id' field
VERSION = '1.15' VERSION = '1.16'
dbapi = dbapi.get_instance() dbapi = dbapi.get_instance()
@ -59,6 +60,7 @@ class Cluster(base.MagnumPersistentObject, base.MagnumObject,
'keypair': fields.StringField(nullable=True), 'keypair': fields.StringField(nullable=True),
'docker_volume_size': fields.IntegerField(nullable=True), 'docker_volume_size': fields.IntegerField(nullable=True),
'labels': fields.DictOfStringsField(nullable=True), 'labels': fields.DictOfStringsField(nullable=True),
'master_flavor_id': fields.StringField(nullable=True),
'stack_id': fields.StringField(nullable=True), 'stack_id': fields.StringField(nullable=True),
'status': m_fields.ClusterStatusField(nullable=True), 'status': m_fields.ClusterStatusField(nullable=True),
'status_reason': fields.StringField(nullable=True), 'status_reason': fields.StringField(nullable=True),

View File

@ -838,6 +838,24 @@ class TestPost(api_base.FunctionalTest):
self.assertEqual(400, response.status_int) self.assertEqual(400, response.status_int)
self.assertTrue(response.json['errors']) self.assertTrue(response.json['errors'])
def test_create_cluster_with_master_flavor_id(self):
bdict = apiutils.cluster_post_data()
bdict['master_flavor_id'] = 'm2.small'
response = self.post_json('/clusters', bdict)
self.assertEqual('application/json', response.content_type)
self.assertEqual(202, response.status_int)
cluster, timeout = self.mock_cluster_create.call_args
self.assertEqual('m2.small', cluster[0].master_flavor_id)
def test_create_cluster_without_master_flavor_id(self):
bdict = apiutils.cluster_post_data()
response = self.post_json('/clusters', bdict)
self.assertEqual('application/json', response.content_type)
self.assertEqual(202, response.status_int)
cluster, timeout = self.mock_cluster_create.call_args
# Verify master_flavor_id from ClusterTemplate is used
self.assertEqual('m1.small', cluster[0].master_flavor_id)
class TestDelete(api_base.FunctionalTest): class TestDelete(api_base.FunctionalTest):
def setUp(self): def setUp(self):

View File

@ -39,6 +39,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'network_driver': 'network_driver', 'network_driver': 'network_driver',
'volume_driver': 'volume_driver', 'volume_driver': 'volume_driver',
'docker_volume_size': 20, 'docker_volume_size': 20,
'master_flavor_id': 'flavor_id',
'docker_storage_driver': 'devicemapper', 'docker_storage_driver': 'devicemapper',
'cluster_distro': 'fedora-atomic', 'cluster_distro': 'fedora-atomic',
'coe': 'kubernetes', 'coe': 'kubernetes',
@ -94,6 +95,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'grafana_admin_passwd': 'fake_pwd', 'grafana_admin_passwd': 'fake_pwd',
'kube_dashboard_enabled': 'True', 'kube_dashboard_enabled': 'True',
'docker_volume_type': 'lvmdriver-1'}, 'docker_volume_type': 'lvmdriver-1'},
'master_flavor_id': 'master_flavor_id',
} }
self.context.user_name = 'fake_user' self.context.user_name = 'fake_user'
self.context.tenant = 'fake_tenant' self.context.tenant = 'fake_tenant'
@ -369,6 +371,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'cluster_uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52', 'cluster_uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52',
'discovery_url': 'https://discovery.etcd.io/test', 'discovery_url': 'https://discovery.etcd.io/test',
'docker_volume_size': 20, 'docker_volume_size': 20,
'master_flavor': 'master_flavor_id',
'external_network': 'external_network_id', 'external_network': 'external_network_id',
'flannel_backend': 'vxlan', 'flannel_backend': 'vxlan',
'flannel_network_cidr': '10.101.0.0/16', 'flannel_network_cidr': '10.101.0.0/16',
@ -611,21 +614,6 @@ class TestClusterConductorWithK8s(base.TestCase):
mock_get, mock_get,
missing_attr='docker_storage_driver') missing_attr='docker_storage_driver')
@patch('requests.get')
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
@patch('magnum.drivers.common.driver.Driver.get_driver')
def test_extract_template_definition_without_master_flavor(
self,
mock_driver,
mock_objects_cluster_template_get_by_uuid,
mock_get):
mock_driver.return_value = k8s_dr.Driver()
self._test_extract_template_definition(
mock_driver,
mock_objects_cluster_template_get_by_uuid,
mock_get,
missing_attr='master_flavor_id')
@patch('requests.get') @patch('requests.get')
@patch('magnum.objects.ClusterTemplate.get_by_uuid') @patch('magnum.objects.ClusterTemplate.get_by_uuid')
@patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.drivers.common.driver.Driver.get_driver')

View File

@ -56,6 +56,7 @@ class TestClusterConductorWithMesos(base.TestCase):
'uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52', 'uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52',
'cluster_template_id': 'xx-xx-xx-xx', 'cluster_template_id': 'xx-xx-xx-xx',
'keypair': 'keypair_id', 'keypair': 'keypair_id',
'master_flavor_id': 'master_flavor_id',
'name': 'cluster1', 'name': 'cluster1',
'stack_id': 'xx-xx-xx-xx', 'stack_id': 'xx-xx-xx-xx',
'api_address': '172.17.2.3', 'api_address': '172.17.2.3',
@ -189,7 +190,8 @@ class TestClusterConductorWithMesos(base.TestCase):
'mesos_slave_isolation': 'docker/runtime,filesystem/linux', 'mesos_slave_isolation': 'docker/runtime,filesystem/linux',
'mesos_slave_executor_env_variables': '{}', 'mesos_slave_executor_env_variables': '{}',
'mesos_slave_work_dir': '/tmp/mesos/slave', 'mesos_slave_work_dir': '/tmp/mesos/slave',
'mesos_slave_image_providers': 'docker' 'mesos_slave_image_providers': 'docker',
'master_flavor': 'master_flavor_id',
} }
self.assertEqual(expected, definition) self.assertEqual(expected, definition)
self.assertEqual( self.assertEqual(

View File

@ -63,6 +63,7 @@ class TestClusterConductorWithSwarm(base.TestCase):
'cluster_template_id': 'xx-xx-xx-xx', 'cluster_template_id': 'xx-xx-xx-xx',
'keypair': 'keypair_id', 'keypair': 'keypair_id',
'docker_volume_size': 20, 'docker_volume_size': 20,
'master_flavor_id': 'master_flavor_id',
'name': 'cluster1', 'name': 'cluster1',
'stack_id': 'xx-xx-xx-xx', 'stack_id': 'xx-xx-xx-xx',
'api_address': '172.17.2.3', 'api_address': '172.17.2.3',
@ -304,6 +305,7 @@ class TestClusterConductorWithSwarm(base.TestCase):
'rexray_preempt': 'False', 'rexray_preempt': 'False',
'docker_volume_type': 'lvmdriver-1', 'docker_volume_type': 'lvmdriver-1',
'docker_volume_size': 20, 'docker_volume_size': 20,
'master_flavor': 'master_flavor_id',
} }
self.assertEqual(expected, definition) self.assertEqual(expected, definition)
self.assertEqual( self.assertEqual(

View File

@ -98,6 +98,7 @@ def get_test_cluster(**kw):
'updated_at': kw.get('updated_at'), 'updated_at': kw.get('updated_at'),
'docker_volume_size': kw.get('docker_volume_size'), 'docker_volume_size': kw.get('docker_volume_size'),
'labels': kw.get('labels'), 'labels': kw.get('labels'),
'master_flavor_id': kw.get('master_flavor_id', None),
} }
# Only add Keystone trusts related attributes on demand since they may # Only add Keystone trusts related attributes on demand since they may

View File

@ -355,7 +355,7 @@ class TestObject(test_base.TestCase, _TestObject):
# For more information on object version testing, read # For more information on object version testing, read
# http://docs.openstack.org/developer/magnum/objects.html # http://docs.openstack.org/developer/magnum/objects.html
object_data = { object_data = {
'Cluster': '1.15-a8ed124644a6a53be1789f44578863f2', 'Cluster': '1.16-7a544c5059697c464810470980f81ba1',
'ClusterTemplate': '1.17-f1ce5212b46506360b41ab5cb7658af4', 'ClusterTemplate': '1.17-f1ce5212b46506360b41ab5cb7658af4',
'Certificate': '1.1-1924dc077daa844f0f9076332ef96815', 'Certificate': '1.1-1924dc077daa844f0f9076332ef96815',
'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd', 'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd',