Implementation of Cluster distro for baymodel.
partially implements blueprint add-glance-image-properties-support Change-Id: I439cb1e31a32abccdb4e08fe9e03f026f77b33fb
This commit is contained in:
parent
99e86a8540
commit
2759af7bfa
|
@ -101,6 +101,7 @@ Create a new shell, and source the devstack openrc script::
|
|||
glance image-create --name fedora-21-atomic-2 \
|
||||
--is-public True \
|
||||
--disk-format qcow2 \
|
||||
--property os-distro='fedora-atomic'\
|
||||
--container-format bare < fedora-21-atomic-2.qcow2
|
||||
test -f ~/.ssh/id_rsa.pub || ssh-keygen
|
||||
nova keypair-add --pub-key ~/.ssh/id_rsa.pub testkey
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import datetime
|
||||
|
||||
import glanceclient.exc
|
||||
import pecan
|
||||
from pecan import rest
|
||||
import wsme
|
||||
|
@ -25,6 +26,7 @@ from magnum.api.controllers import link
|
|||
from magnum.api.controllers.v1 import collection
|
||||
from magnum.api.controllers.v1 import types
|
||||
from magnum.api.controllers.v1 import utils as api_utils
|
||||
from magnum.common import clients
|
||||
from magnum.common import exception
|
||||
from magnum import objects
|
||||
|
||||
|
@ -76,6 +78,9 @@ class BayModel(base.APIBase):
|
|||
ssh_authorized_key = wtypes.text
|
||||
"""The SSH Authorized Key"""
|
||||
|
||||
cluster_distro = wtypes.text
|
||||
"""The Cluster distro for the bay, ex - coreos, fedora-atomic."""
|
||||
|
||||
links = wsme.wsattr([link.Link], readonly=True)
|
||||
"""A list containing a self link and associated baymodel links"""
|
||||
|
||||
|
@ -121,6 +126,7 @@ class BayModel(base.APIBase):
|
|||
fixed_network='private',
|
||||
apiserver_port=8080,
|
||||
docker_volume_size=25,
|
||||
cluster_distro='fedora-atomic',
|
||||
ssh_authorized_key='ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAB',
|
||||
created_at=datetime.datetime.utcnow(),
|
||||
updated_at=datetime.datetime.utcnow())
|
||||
|
@ -185,6 +191,20 @@ class BayModelsController(rest.RestController):
|
|||
sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
|
||||
def _get_image_data(self, context, image_id):
|
||||
"""Retrieves os_distro and other metadata from the Glance image.
|
||||
|
||||
:param image_id: image_id of baymodel.
|
||||
"""
|
||||
try:
|
||||
cli = clients.OpenStackClients(context)
|
||||
image_data = cli.glance().images.get(image_id)
|
||||
return image_data
|
||||
except glanceclient.exc.NotFound:
|
||||
raise exception.ImageNotFound(image_id=image_id)
|
||||
except glanceclient.exc.HTTPForbidden:
|
||||
raise exception.ImageNotAuthorized(image_id=image_id)
|
||||
|
||||
@wsme_pecan.wsexpose(BayModelCollection, types.uuid,
|
||||
types.uuid, int, wtypes.text, wtypes.text)
|
||||
def get_all(self, baymodel_uuid=None, marker=None, limit=None,
|
||||
|
@ -249,6 +269,11 @@ class BayModelsController(rest.RestController):
|
|||
auth_token = context.auth_token_info['token']
|
||||
baymodel_dict['project_id'] = auth_token['project']['id']
|
||||
baymodel_dict['user_id'] = auth_token['user']['id']
|
||||
image_data = self._get_image_data(context, baymodel_dict['image_id'])
|
||||
if image_data['os_distro']:
|
||||
baymodel_dict['cluster_distro'] = image_data['os_distro']
|
||||
else:
|
||||
raise exception.OSDistroFieldNotFound(baymodel_dict['image_id'])
|
||||
new_baymodel = objects.BayModel(context, **baymodel_dict)
|
||||
new_baymodel.create()
|
||||
# Set the HTTP Location Header
|
||||
|
|
|
@ -430,3 +430,15 @@ class Urllib2InvalidScheme(MagnumException):
|
|||
|
||||
class OperationInProgress(Invalid):
|
||||
message = _("Bay %(bay_name)s already has an operation in progress.")
|
||||
|
||||
|
||||
class ImageNotFound(ResourceNotFound):
|
||||
message = _("Image %(image_id)s could not be found.")
|
||||
|
||||
|
||||
class ImageNotAuthorized(MagnumException):
|
||||
message = _("Not authorized for image %(image_id)s.")
|
||||
|
||||
|
||||
class OSDistroFieldNotFound(ResourceNotFound):
|
||||
message = _("Image %(image_id)s doesn't contain os-distro field.")
|
||||
|
|
|
@ -29,9 +29,6 @@ from magnum.openstack.common import loopingcall
|
|||
|
||||
|
||||
k8s_heat_opts = [
|
||||
cfg.StrOpt('cluster_type',
|
||||
default='fedora-atomic',
|
||||
help=_('Cluster types are fedora-atomic, coreos, ironic.')),
|
||||
cfg.StrOpt('cluster_coe',
|
||||
default='kubernetes',
|
||||
help=_('Container Orchestration Environments are '
|
||||
|
@ -62,9 +59,11 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
def _extract_template_definition(context, bay):
|
||||
baymodel = objects.BayModel.get_by_uuid(context, bay.baymodel_id)
|
||||
cluster_type = cfg.CONF.k8s_heat.cluster_type
|
||||
cluster_distro = baymodel.cluster_distro
|
||||
cluster_coe = cfg.CONF.k8s_heat.cluster_coe
|
||||
definition = TDef.get_template_definition('vm', cluster_type, cluster_coe)
|
||||
definition = TDef.get_template_definition('vm',
|
||||
cluster_distro,
|
||||
cluster_coe)
|
||||
return definition.extract_definition(baymodel, bay)
|
||||
|
||||
|
||||
|
@ -107,10 +106,13 @@ def _update_stack(context, osc, bay):
|
|||
return osc.heat().stacks.update(bay.stack_id, **fields)
|
||||
|
||||
|
||||
def _update_stack_outputs(stack, bay):
|
||||
cluster_type = cfg.CONF.k8s_heat.cluster_type
|
||||
def _update_stack_outputs(context, stack, bay):
|
||||
baymodel = objects.BayModel.get_by_uuid(context, bay.baymodel_id)
|
||||
cluster_distro = baymodel.cluster_distro
|
||||
cluster_coe = cfg.CONF.k8s_heat.cluster_coe
|
||||
definition = TDef.get_template_definition('vm', cluster_type, cluster_coe)
|
||||
definition = TDef.get_template_definition('vm',
|
||||
cluster_distro,
|
||||
cluster_coe)
|
||||
return definition.update_outputs(stack, bay)
|
||||
|
||||
|
||||
|
@ -202,6 +204,7 @@ class HeatPoller(object):
|
|||
|
||||
def __init__(self, openstack_client, bay):
|
||||
self.openstack_client = openstack_client
|
||||
self.context = self.openstack_client.context
|
||||
self.bay = bay
|
||||
self.attempts = 0
|
||||
|
||||
|
@ -218,7 +221,7 @@ class HeatPoller(object):
|
|||
self.bay.destroy()
|
||||
raise loopingcall.LoopingCallDone()
|
||||
if (stack.stack_status in ['CREATE_COMPLETE', 'UPDATE_COMPLETE']):
|
||||
_update_stack_outputs(stack, self.bay)
|
||||
_update_stack_outputs(self.context, stack, self.bay)
|
||||
self.bay.status = stack.stack_status
|
||||
self.bay.save()
|
||||
raise loopingcall.LoopingCallDone()
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# 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 cluster distro
|
||||
|
||||
Revision ID: 4956f03cabad
|
||||
Revises: 2d8657c0cdc
|
||||
Create Date: 2015-04-25 02:17:51.486547
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4956f03cabad'
|
||||
down_revision = '2d8657c0cdc'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('baymodel', sa.Column('cluster_distro',
|
||||
sa.String(length=255), nullable=True))
|
|
@ -168,6 +168,7 @@ class BayModel(Base):
|
|||
apiserver_port = Column(Integer())
|
||||
docker_volume_size = Column(Integer())
|
||||
ssh_authorized_key = Column(Text)
|
||||
cluster_distro = Column(String(255))
|
||||
|
||||
|
||||
class Container(Base):
|
||||
|
|
|
@ -42,6 +42,7 @@ class BayModel(base.MagnumObject):
|
|||
'apiserver_port': obj_utils.int_or_none,
|
||||
'docker_volume_size': obj_utils.int_or_none,
|
||||
'ssh_authorized_key': obj_utils.str_or_none,
|
||||
'cluster_distro': obj_utils.str_or_none,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -322,11 +322,14 @@ class TestPost(api_base.FunctionalTest):
|
|||
def setUp(self):
|
||||
super(TestPost, self).setUp()
|
||||
|
||||
@mock.patch('magnum.common.clients.OpenStackClients')
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_create_baymodel(self, mock_utcnow):
|
||||
def test_create_baymodel(self, mock_utcnow, mock_openstack_client):
|
||||
cdict = apiutils.baymodel_post_data()
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
test_auth_url = 'http://127.0.0.1:5000/v2.0'
|
||||
mock_openstack_client.glance.return_value = test_auth_url
|
||||
|
||||
response = self.post_json('/baymodels', cdict)
|
||||
self.assertEqual(201, response.status_int)
|
||||
|
@ -341,9 +344,12 @@ class TestPost(api_base.FunctionalTest):
|
|||
response.json['created_at']).replace(tzinfo=None)
|
||||
self.assertEqual(test_time, return_created_at)
|
||||
|
||||
def test_create_baymodel_doesnt_contain_id(self):
|
||||
@mock.patch('magnum.common.clients.OpenStackClients')
|
||||
def test_create_baymodel_doesnt_contain_id(self, mock_openstack_client):
|
||||
with mock.patch.object(self.dbapi, 'create_baymodel',
|
||||
wraps=self.dbapi.create_baymodel) as cc_mock:
|
||||
test_auth_url = 'http://127.0.0.1:5000/v2.0'
|
||||
mock_openstack_client.glance.return_value = test_auth_url
|
||||
cdict = apiutils.baymodel_post_data(image_id='my-image')
|
||||
response = self.post_json('/baymodels', cdict)
|
||||
self.assertEqual(cdict['image_id'], response.json['image_id'])
|
||||
|
@ -351,16 +357,24 @@ class TestPost(api_base.FunctionalTest):
|
|||
# Check that 'id' is not in first arg of positional args
|
||||
self.assertNotIn('id', cc_mock.call_args[0][0])
|
||||
|
||||
def test_create_baymodel_with_invalid_docker_volume_size(self):
|
||||
@mock.patch('magnum.common.clients.OpenStackClients')
|
||||
def test_create_baymodel_with_invalid_docker_volume_size(self,
|
||||
mock_openstack_client):
|
||||
with mock.patch.object(self.dbapi, 'create_baymodel',
|
||||
wraps=self.dbapi.create_baymodel) as cc_mock:
|
||||
test_auth_url = 'http://127.0.0.1:5000/v2.0'
|
||||
mock_openstack_client.glance.return_value = test_auth_url
|
||||
cdict = apiutils.baymodel_post_data(docker_volume_size='docker')
|
||||
self.assertRaises(AppError, self.post_json, '/baymodels', cdict)
|
||||
self.assertFalse(cc_mock.called)
|
||||
|
||||
def test_create_baymodel_with_docker_volume_size(self):
|
||||
@mock.patch('magnum.common.clients.OpenStackClients')
|
||||
def test_create_baymodel_with_docker_volume_size(self,
|
||||
mock_openstack_client):
|
||||
with mock.patch.object(self.dbapi, 'create_baymodel',
|
||||
wraps=self.dbapi.create_baymodel) as cc_mock:
|
||||
test_auth_url = 'http://127.0.0.1:5000/v2.0'
|
||||
mock_openstack_client.glance.return_value = test_auth_url
|
||||
cdict = apiutils.baymodel_post_data(docker_volume_size=99)
|
||||
response = self.post_json('/baymodels', cdict)
|
||||
self.assertEqual(cdict['docker_volume_size'],
|
||||
|
@ -368,7 +382,10 @@ class TestPost(api_base.FunctionalTest):
|
|||
cc_mock.assert_called_once_with(mock.ANY)
|
||||
self.assertNotIn('id', cc_mock.call_args[0][0])
|
||||
|
||||
def test_create_baymodel_generate_uuid(self):
|
||||
@mock.patch('magnum.common.clients.OpenStackClients')
|
||||
def test_create_baymodel_generate_uuid(self, mock_openstack_client):
|
||||
test_auth_url = 'http://127.0.0.1:5000/v2.0'
|
||||
mock_openstack_client.glance.return_value = test_auth_url
|
||||
cdict = apiutils.baymodel_post_data()
|
||||
del cdict['uuid']
|
||||
response = self.post_json('/baymodels', cdict)
|
||||
|
|
|
@ -39,6 +39,7 @@ class TestBayK8sHeat(base.TestCase):
|
|||
'external_network_id': 'external_network_id',
|
||||
'fixed_network': 'private',
|
||||
'docker_volume_size': 20,
|
||||
'cluster_distro': 'fedora-atomic',
|
||||
'ssh_authorized_key': 'ssh_authorized_key',
|
||||
'token': None,
|
||||
}
|
||||
|
@ -80,9 +81,8 @@ class TestBayK8sHeat(base.TestCase):
|
|||
def test_extract_template_definition_coreos_with_disovery(self,
|
||||
mock_objects_baymodel_get_by_uuid,
|
||||
reqget):
|
||||
cfg.CONF.set_override('cluster_type',
|
||||
'coreos',
|
||||
group='k8s_heat')
|
||||
baymodel_dict = self.baymodel_dict
|
||||
baymodel_dict['cluster_distro'] = 'coreos'
|
||||
cfg.CONF.set_override('coreos_discovery_token_url',
|
||||
'http://tokentest',
|
||||
group='bay')
|
||||
|
@ -116,9 +116,8 @@ class TestBayK8sHeat(base.TestCase):
|
|||
def test_extract_template_definition_coreos_no_discoveryurl(self,
|
||||
mock_objects_baymodel_get_by_uuid,
|
||||
mock_uuid):
|
||||
cfg.CONF.set_override('cluster_type',
|
||||
'coreos',
|
||||
group='k8s_heat')
|
||||
baymodel_dict = self.baymodel_dict
|
||||
baymodel_dict['cluster_distro'] = 'coreos'
|
||||
cfg.CONF.set_override('coreos_discovery_token_url',
|
||||
None,
|
||||
group='bay')
|
||||
|
@ -300,10 +299,8 @@ class TestBayK8sHeat(base.TestCase):
|
|||
@patch('magnum.objects.BayModel.get_by_uuid')
|
||||
def test_extract_template_definition_without_ssh_authorized_key(self,
|
||||
mock_objects_baymodel_get_by_uuid):
|
||||
cfg.CONF.set_override('cluster_type',
|
||||
'coreos',
|
||||
group='k8s_heat')
|
||||
baymodel_dict = self.baymodel_dict
|
||||
baymodel_dict['cluster_distro'] = 'coreos'
|
||||
baymodel_dict['ssh_authorized_key'] = None
|
||||
baymodel = objects.BayModel(self.context, **baymodel_dict)
|
||||
mock_objects_baymodel_get_by_uuid.return_value = baymodel
|
||||
|
@ -379,7 +376,12 @@ class TestBayK8sHeat(base.TestCase):
|
|||
}
|
||||
self.assertEqual(expected, definition)
|
||||
|
||||
def test_update_stack_outputs(self):
|
||||
@patch('magnum.objects.BayModel.get_by_uuid')
|
||||
def test_update_stack_outputs(self, mock_objects_baymodel_get_by_uuid):
|
||||
baymodel_dict = self.baymodel_dict
|
||||
baymodel_dict['cluster_distro'] = 'coreos'
|
||||
baymodel = objects.BayModel(self.context, **baymodel_dict)
|
||||
mock_objects_baymodel_get_by_uuid.return_value = baymodel
|
||||
expected_api_address = 'api_address'
|
||||
expected_node_addresses = ['ex_minion', 'address']
|
||||
|
||||
|
@ -399,7 +401,7 @@ class TestBayK8sHeat(base.TestCase):
|
|||
mock_stack.outputs = outputs
|
||||
mock_bay = mock.MagicMock()
|
||||
|
||||
bay_k8s_heat._update_stack_outputs(mock_stack, mock_bay)
|
||||
bay_k8s_heat._update_stack_outputs(self.context, mock_stack, mock_bay)
|
||||
|
||||
self.assertEqual(mock_bay.api_address, expected_api_address)
|
||||
self.assertEqual(mock_bay.node_addresses, expected_node_addresses)
|
||||
|
@ -765,7 +767,8 @@ class TestBayK8sHeatSwarm(base.TestCase):
|
|||
'keypair_id': 'keypair_id',
|
||||
'dns_nameserver': 'dns_nameserver',
|
||||
'external_network_id': 'external_network_id',
|
||||
'fixed_network': '10.2.0.0/22'
|
||||
'fixed_network': '10.2.0.0/22',
|
||||
'cluster_distro': 'fedora-atomic',
|
||||
}
|
||||
self.bay_dict = {
|
||||
'id': 1,
|
||||
|
|
|
@ -35,6 +35,7 @@ def get_test_baymodel(**kw):
|
|||
'dns_nameserver': kw.get('dns_nameserver', '8.8.1.1'),
|
||||
'apiserver_port': kw.get('apiserver_port', 8080),
|
||||
'docker_volume_size': kw.get('docker_volume_size', 20),
|
||||
'cluster_distro': kw.get('cluster_distro', 'fedora-atomic'),
|
||||
'ssh_authorized_key': kw.get('ssh_authorized_key',
|
||||
'ssh-rsa AAAAB3NzaC1ycEAAAADA'
|
||||
'v0XRqg3tm+jlsOKGO81lPDH+KaSJ'
|
||||
|
|
Loading…
Reference in New Issue