diff --git a/.gitignore b/.gitignore index af8f1342ba..e7e23667d7 100644 --- a/.gitignore +++ b/.gitignore @@ -51,4 +51,4 @@ ChangeLog # Editors *~ .*.swp -.*sw? +.*sw? \ No newline at end of file diff --git a/doc/source/dev/dev-quickstart.rst b/doc/source/dev/dev-quickstart.rst index a27a4dbb25..f684b9dfe6 100644 --- a/doc/source/dev/dev-quickstart.rst +++ b/doc/source/dev/dev-quickstart.rst @@ -175,7 +175,7 @@ Magnum in which way to construct a bay.:: --keypair-id testkey \ --external-network-id $NIC_ID \ --dns-nameserver 8.8.8.8 --flavor-id m1.small \ - --docker-volume-size 5 + --docker-volume-size 5 --coe kubernetes Next create a bay. Use the baymodel UUID as a template for bay creation. This bay will result in one master kubernetes node and two minion nodes.:: @@ -264,25 +264,16 @@ replicating data between one another. Building and using a Swarm bay ============================== - -First, we will need to reconfigure Magnum. We need to set 'cluster_coe' in -the 'k8s_heat' section to 'swarm' in the magnum.conf. After changing -magnum.conf restart magnum-api and magnum-conductor.:: - - sudo cat >> /etc/magnum/magnum.conf << END_CONFIG - [k8s_heat] - cluster_coe=swarm - END_CONFIG - - -Next, create a baymodel, it is very similar to the Kubernetes baymodel, -it is only missing some Kubernetes specific arguments.:: +Create a baymodel. It is very similar to the Kubernetes baymodel, +it is only missing some Kubernetes specific arguments and uses 'swarm' as the +coe. :: NIC_ID=$(neutron net-show public | awk '/ id /{print $4}') magnum baymodel-create --name swarmbaymodel --image-id fedora-21-atomic-3 \ --keypair-id testkey \ --external-network-id $NIC_ID \ - --dns-nameserver 8.8.8.8 --flavor-id m1.small + --dns-nameserver 8.8.8.8 --flavor-id m1.small \ + --coe swarm Finally, create the bay. Use the baymodel 'swarmbaymodel' as a template for bay creation. This bay will result in one swarm manager node and two extra diff --git a/magnum/api/controllers/v1/baymodel.py b/magnum/api/controllers/v1/baymodel.py index 50db6076e6..f9973be14c 100644 --- a/magnum/api/controllers/v1/baymodel.py +++ b/magnum/api/controllers/v1/baymodel.py @@ -42,12 +42,26 @@ class BayModel(base.APIBase): between the internal object model and the API representation of a baymodel. """ + _coe = None + + def _get_coe(self): + return self._coe + + def _set_coe(self, value): + if value and self._coe != value: + self._coe = value + elif value == wtypes.Unset: + self._coe = wtypes.Unset + uuid = types.uuid """Unique UUID for this baymodel""" name = wtypes.text """The name of the bay model""" + coe = wsme.wsproperty(wtypes.text, _get_coe, _set_coe, mandatory=True) + """The Container Orchestration Engine for this bay model""" + image_id = wtypes.text """The image name or UUID to use as a base image for this baymodel""" @@ -97,7 +111,7 @@ class BayModel(base.APIBase): def _convert_with_links(baymodel, url, expand=True): if not expand: baymodel.unset_fields_except(['uuid', 'name', 'image_id', - 'apiserver_port']) + 'apiserver_port', 'coe']) baymodel.links = [link.Link.make_link('self', url, 'baymodels', baymodel.uuid), @@ -128,6 +142,7 @@ class BayModel(base.APIBase): docker_volume_size=25, cluster_distro='fedora-atomic', ssh_authorized_key='ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAB', + coe='kubernetes', created_at=datetime.datetime.utcnow(), updated_at=datetime.datetime.utcnow()) return cls._convert_with_links(sample, 'http://localhost:9511', expand) diff --git a/magnum/conductor/handlers/bay_k8s_heat.py b/magnum/conductor/handlers/bay_k8s_heat.py index 6a08a83cee..c4f34c6d5c 100644 --- a/magnum/conductor/handlers/bay_k8s_heat.py +++ b/magnum/conductor/handlers/bay_k8s_heat.py @@ -29,10 +29,6 @@ from magnum.openstack.common import loopingcall k8s_heat_opts = [ - cfg.StrOpt('cluster_coe', - default='kubernetes', - help=_('Container Orchestration Environments are ' - 'kubernetes or swarm. ')), cfg.IntOpt('max_attempts', default=2000, help=('Number of attempts to query the Heat stack for ' @@ -57,12 +53,16 @@ cfg.CONF.register_opts(k8s_heat_opts, group='k8s_heat') LOG = logging.getLogger(__name__) -def _extract_template_definition(context, bay): +def _get_baymodel(context, bay): baymodel = objects.BayModel.get_by_uuid(context, bay.baymodel_id) + return baymodel + + +def _extract_template_definition(context, bay): + baymodel = _get_baymodel(context, bay) cluster_distro = baymodel.cluster_distro - cluster_coe = cfg.CONF.k8s_heat.cluster_coe - definition = TDef.get_template_definition('vm', - cluster_distro, + cluster_coe = baymodel.coe + definition = TDef.get_template_definition('vm', cluster_distro, cluster_coe) return definition.extract_definition(baymodel, bay) @@ -107,11 +107,10 @@ def _update_stack(context, osc, bay): def _update_stack_outputs(context, stack, bay): - baymodel = objects.BayModel.get_by_uuid(context, bay.baymodel_id) + baymodel = _get_baymodel(context, bay) cluster_distro = baymodel.cluster_distro - cluster_coe = cfg.CONF.k8s_heat.cluster_coe - definition = TDef.get_template_definition('vm', - cluster_distro, + cluster_coe = baymodel.coe + definition = TDef.get_template_definition('vm', cluster_distro, cluster_coe) return definition.update_outputs(stack, bay) @@ -222,6 +221,7 @@ class HeatPoller(object): raise loopingcall.LoopingCallDone() if (stack.stack_status in ['CREATE_COMPLETE', 'UPDATE_COMPLETE']): _update_stack_outputs(self.context, stack, self.bay) + self.bay.status = stack.stack_status self.bay.save() raise loopingcall.LoopingCallDone() diff --git a/magnum/db/sqlalchemy/alembic/versions/592131657ca1_add_coe_column_to_baymodel.py b/magnum/db/sqlalchemy/alembic/versions/592131657ca1_add_coe_column_to_baymodel.py new file mode 100644 index 0000000000..4fd1de7f99 --- /dev/null +++ b/magnum/db/sqlalchemy/alembic/versions/592131657ca1_add_coe_column_to_baymodel.py @@ -0,0 +1,52 @@ +# 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 coe column to BayModel + +Revision ID: 592131657ca1 +Revises: 4956f03cabad +Create Date: 2015-04-17 14:20:17.620995 + +""" + +# revision identifiers, used by Alembic. +revision = '592131657ca1' +down_revision = '4956f03cabad' + +from alembic import op +from oslo_config import cfg +import sqlalchemy as sa + +from magnum.openstack.common._i18n import _ + + +k8s_heat_opts = [ + cfg.StrOpt('cluster_coe', + default='kubernetes', + help=_('Container Orchestration Environments are ' + 'kubernetes or swarm. ')), +] + +cfg.CONF.register_opts(k8s_heat_opts, group='k8s_heat') + + +def upgrade(): + op.add_column('baymodel', sa.Column('coe', sa.String(length=255), + nullable=True)) + + baymodel = sa.sql.table('baymodel', + sa.sql.column('coe', sa.String(length=255))) + op.execute( + baymodel.update().values({'coe': + op.inline_literal( + cfg.CONF.k8s_heat.cluster_coe)}) + ) diff --git a/magnum/db/sqlalchemy/models.py b/magnum/db/sqlalchemy/models.py index 170b84401c..5aee708a80 100644 --- a/magnum/db/sqlalchemy/models.py +++ b/magnum/db/sqlalchemy/models.py @@ -169,6 +169,7 @@ class BayModel(Base): docker_volume_size = Column(Integer()) ssh_authorized_key = Column(Text) cluster_distro = Column(String(255)) + coe = Column(String(255)) class Container(Base): diff --git a/magnum/objects/baymodel.py b/magnum/objects/baymodel.py index d3dfeef3cb..6a530dd54d 100644 --- a/magnum/objects/baymodel.py +++ b/magnum/objects/baymodel.py @@ -43,6 +43,7 @@ class BayModel(base.MagnumObject): 'docker_volume_size': obj_utils.int_or_none, 'ssh_authorized_key': obj_utils.str_or_none, 'cluster_distro': obj_utils.str_or_none, + 'coe': obj_utils.str_or_none, } @staticmethod diff --git a/magnum/tests/unit/api/controllers/v1/test_baymodel.py b/magnum/tests/unit/api/controllers/v1/test_baymodel.py index c61b2df68e..f2c0ae0a2e 100644 --- a/magnum/tests/unit/api/controllers/v1/test_baymodel.py +++ b/magnum/tests/unit/api/controllers/v1/test_baymodel.py @@ -67,6 +67,7 @@ class TestListBayModel(api_base.FunctionalTest): self.assertIn('fixed_network', response) self.assertIn('docker_volume_size', response) self.assertIn('ssh_authorized_key', response) + self.assertIn('coe', response) def test_get_one_by_name(self): baymodel = obj_utils.create_test_baymodel(self.context) @@ -79,6 +80,7 @@ class TestListBayModel(api_base.FunctionalTest): self.assertIn('external_network_id', response) self.assertIn('fixed_network', response) self.assertIn('docker_volume_size', response) + self.assertIn('coe', response) def test_get_one_by_name_not_found(self): response = self.get_json('/baymodels/not_found', @@ -110,6 +112,7 @@ class TestListBayModel(api_base.FunctionalTest): self.assertIn('fixed_network', response['baymodels'][0]) self.assertIn('docker_volume_size', response['baymodels'][0]) self.assertIn('ssh_authorized_key', response['baymodels'][0]) + self.assertIn('coe', response['baymodels'][0]) def test_detail_against_single(self): baymodel = obj_utils.create_test_baymodel(self.context) @@ -177,7 +180,8 @@ class TestPatch(api_base.FunctionalTest): 'DYucqbeuM7nmJi+8Hb55y1xWoOZI' 'KMa71G5/4EOQxuQ/sgW965OOO2Hq' 'X8vjlQUnTK0HijrbSTLxp/9kazWW' - 'FrfsdB8RtZBN digambar@magnum' + 'FrfsdB8RtZBN digambar@magnum', + coe='swarm' ) def test_update_not_found(self): @@ -219,6 +223,8 @@ class TestPatch(api_base.FunctionalTest): response['docker_volume_size']) self.assertEqual(self.baymodel.ssh_authorized_key, response['ssh_authorized_key']) + self.assertEqual(self.baymodel.coe, + response['coe']) def test_remove_singular(self): baymodel = obj_utils.create_test_baymodel(self.context, @@ -243,6 +249,8 @@ class TestPatch(api_base.FunctionalTest): response['docker_volume_size']) self.assertEqual(self.baymodel.ssh_authorized_key, response['ssh_authorized_key']) + self.assertEqual(self.baymodel.coe, + response['coe']) def test_remove_non_existent_property_fail(self): response = self.patch_json('/baymodels/%s' % self.baymodel.uuid, @@ -268,6 +276,8 @@ class TestPatch(api_base.FunctionalTest): response['apiserver_port']) self.assertEqual(self.baymodel.docker_volume_size, response['docker_volume_size']) + self.assertEqual(self.baymodel.coe, + response['coe']) def test_add_root_non_existent(self): response = self.patch_json('/baymodels/%s' % self.baymodel.uuid, @@ -307,6 +317,8 @@ class TestPatch(api_base.FunctionalTest): response['docker_volume_size']) self.assertEqual(self.baymodel.ssh_authorized_key, response['ssh_authorized_key']) + self.assertEqual(self.baymodel.coe, + response['coe']) def test_remove_uuid(self): response = self.patch_json('/baymodels/%s' % self.baymodel.uuid, diff --git a/magnum/tests/unit/conductor/handlers/test_bay_k8s_heat.py b/magnum/tests/unit/conductor/handlers/test_bay_k8s_heat.py index 690a6b2d66..389374428f 100644 --- a/magnum/tests/unit/conductor/handlers/test_bay_k8s_heat.py +++ b/magnum/tests/unit/conductor/handlers/test_bay_k8s_heat.py @@ -41,6 +41,7 @@ class TestBayK8sHeat(base.TestCase): 'docker_volume_size': 20, 'cluster_distro': 'fedora-atomic', 'ssh_authorized_key': 'ssh_authorized_key', + 'coe': 'kubernetes', 'token': None, } self.bay_dict = { @@ -52,6 +53,15 @@ class TestBayK8sHeat(base.TestCase): 'node_count': 1, } + @patch('magnum.objects.BayModel.get_by_uuid') + def test_get_baymodel(self, mock_objects_baymodel_get_by_uuid): + baymodel = objects.BayModel(self.context, **self.baymodel_dict) + mock_objects_baymodel_get_by_uuid.return_value = baymodel + bay = objects.Bay(self.context, **self.bay_dict) + + fetched_baymodel = bay_k8s_heat._get_baymodel(self.context, bay) + self.assertEqual(baymodel, fetched_baymodel) + @patch('magnum.objects.BayModel.get_by_uuid') def test_extract_template_definition(self, mock_objects_baymodel_get_by_uuid): @@ -167,7 +177,7 @@ class TestBayK8sHeat(base.TestCase): 'master_flavor': 'master_flavor_id', 'number_of_minions': '1', 'fixed_network': 'private', - 'docker_volume_size': 20 + 'docker_volume_size': 20, } self.assertEqual(expected, definition) @@ -192,7 +202,7 @@ class TestBayK8sHeat(base.TestCase): 'master_flavor': 'master_flavor_id', 'number_of_minions': '1', 'fixed_network': 'private', - 'docker_volume_size': 20 + 'docker_volume_size': 20, } self.assertEqual(expected, definition) @@ -700,6 +710,9 @@ class TestHandler(db_base.DbTestCase): def setUp(self): super(TestHandler, self).setUp() self.handler = bay_k8s_heat.Handler() + baymodel_dict = utils.get_test_baymodel() + self.baymodel = objects.BayModel(self.context, **baymodel_dict) + self.baymodel.create() bay_dict = utils.get_test_bay(node_count=1) self.bay = objects.Bay(self.context, **bay_dict) self.bay.create() @@ -775,6 +788,7 @@ class TestBayK8sHeatSwarm(base.TestCase): 'external_network_id': 'external_network_id', 'fixed_network': '10.2.0.0/22', 'cluster_distro': 'fedora-atomic', + 'coe': 'swarm' } self.bay_dict = { 'id': 1, @@ -791,7 +805,6 @@ class TestBayK8sHeatSwarm(base.TestCase): @patch('magnum.objects.BayModel.get_by_uuid') def test_extract_template_definition_all_values(self, mock_objects_baymodel_get_by_uuid): - cfg.CONF.set_override('cluster_coe', 'swarm', group='k8s_heat') baymodel = objects.BayModel(self.context, **self.baymodel_dict) mock_objects_baymodel_get_by_uuid.return_value = baymodel bay = objects.Bay(self.context, **self.bay_dict) @@ -815,7 +828,6 @@ class TestBayK8sHeatSwarm(base.TestCase): @patch('magnum.objects.BayModel.get_by_uuid') def test_extract_template_definition_only_required(self, mock_objects_baymodel_get_by_uuid): - cfg.CONF.set_override('cluster_coe', 'swarm', group='k8s_heat') cfg.CONF.set_override('public_swarm_discovery', False, group='bay') cfg.CONF.set_override('swarm_discovery_url_format', 'test_discovery', group='bay') diff --git a/magnum/tests/unit/db/utils.py b/magnum/tests/unit/db/utils.py index 59efe29105..7539fc8acf 100644 --- a/magnum/tests/unit/db/utils.py +++ b/magnum/tests/unit/db/utils.py @@ -44,6 +44,7 @@ def get_test_baymodel(**kw): 'KMa71G5/4EOQxuQ/sgW965OOO2Hq' 'X8vjlQUnTK0HijrbSTLxp/9kazWW' 'FrfsdB8RtZBN digambar@magnum'), + 'coe': kw.get('coe', 'swarm'), 'created_at': kw.get('created_at'), 'updated_at': kw.get('updated_at'), }