Merge "Add test and refactoring on bay_create"
commit
4cc3f9ed81
|
@ -19,6 +19,7 @@ from magnum.common import clients
|
|||
from magnum import objects
|
||||
from magnum.openstack.common._i18n import _
|
||||
from magnum.openstack.common import log as logging
|
||||
from magnum.openstack.common import loopingcall
|
||||
|
||||
|
||||
k8s_heat_opts = [
|
||||
|
@ -26,7 +27,17 @@ k8s_heat_opts = [
|
|||
default=
|
||||
'/etc/magnum/templates/heat-kubernetes/kubecluster.yaml',
|
||||
help=_(
|
||||
'Location of template to build a k8s cluster. '))]
|
||||
'Location of template to build a k8s cluster. ')),
|
||||
cfg.IntOpt('max_attempts',
|
||||
default=2000,
|
||||
help=('Number of attempts to query the Heat stack for '
|
||||
'finding out the status of the created stack and '
|
||||
'getting url of the DU created in the stack')),
|
||||
cfg.IntOpt('wait_interval',
|
||||
default=1,
|
||||
help=('Sleep time interval between two attempts of querying '
|
||||
'the Heat stack. This interval is in seconds.')),
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(k8s_heat_opts, group='k8s_heat')
|
||||
|
||||
|
@ -34,9 +45,40 @@ cfg.CONF.register_opts(k8s_heat_opts, group='k8s_heat')
|
|||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# These are the backend operations. They are executed by the backend
|
||||
# service. API calls via AMQP (within the ReST API) trigger the handlers to
|
||||
# be called.
|
||||
def _extract_bay_definition(baymodel):
|
||||
bay_definition = {
|
||||
'ssh_key_name': baymodel.keypair_id,
|
||||
'external_network_id': baymodel.external_network_id
|
||||
}
|
||||
if baymodel.dns_nameserver:
|
||||
bay_definition['dns_nameserver'] = baymodel.dns_nameserver
|
||||
if baymodel.image_id:
|
||||
bay_definition['server_image'] = baymodel.image_id
|
||||
if baymodel.flavor_id:
|
||||
bay_definition['server_flavor'] = baymodel.flavor_id
|
||||
|
||||
return bay_definition
|
||||
|
||||
|
||||
def _create_stack(ctxt, osc, bay):
|
||||
baymodel = objects.BayModel.get_by_uuid(ctxt, bay.baymodel_id)
|
||||
bay_definition = _extract_bay_definition(baymodel)
|
||||
|
||||
if bay.node_count:
|
||||
bay_definition['number_of_minions'] = str(bay.node_count)
|
||||
|
||||
tpl_files, template = template_utils.get_template_contents(
|
||||
cfg.CONF.k8s_heat.template_path)
|
||||
fields = {
|
||||
'stack_name': bay.name,
|
||||
'parameters': bay_definition,
|
||||
'template': template,
|
||||
'files': dict(list(tpl_files.items()))
|
||||
}
|
||||
created_stack = osc.heat().stacks.create(**fields)
|
||||
|
||||
return created_stack
|
||||
|
||||
|
||||
class Handler(object):
|
||||
def __init__(self):
|
||||
|
@ -49,31 +91,34 @@ class Handler(object):
|
|||
|
||||
osc = clients.OpenStackClients(ctxt)
|
||||
|
||||
# stack_name is unique on each tenant.
|
||||
stack_name = bay.name
|
||||
bay_model = objects.BayModel.get_by_uuid(ctxt, bay.baymodel_id)
|
||||
bay_definition = {
|
||||
'ssh_key_name': bay_model.keypair_id,
|
||||
'external_network_id': bay_model.external_network_id,
|
||||
'server_image': bay_model.image_id,
|
||||
'server_flavor': bay_model.flavor_id
|
||||
}
|
||||
tpl_files, template = template_utils.get_template_contents(
|
||||
cfg.CONF.k8s_heat.template_path)
|
||||
|
||||
fields = {
|
||||
'stack_name': stack_name,
|
||||
'parameters': bay_definition,
|
||||
'template': template,
|
||||
'files': dict(list(tpl_files.items()))
|
||||
}
|
||||
created_stack = osc.heat().stacks.create(**fields)
|
||||
created_stack = _create_stack(ctxt, osc, bay)
|
||||
bay.stack_id = created_stack['stack']['id']
|
||||
|
||||
bay.create()
|
||||
|
||||
# TODO(yuanying): create "node" object
|
||||
# using bay_definition['number_of_minions']
|
||||
attempts_count = 0
|
||||
|
||||
# TODO(yuanying): temporary implementation of updating master_address
|
||||
def poll_and_check():
|
||||
stack = osc.heat().stacks.get(bay.stack_id)
|
||||
if stack.stack_status == 'CREATE_COMPLETE':
|
||||
master_address = stack.outputs[2]['output_value']
|
||||
minion_addresses = stack.outputs[0]['output_value']
|
||||
bay.master_address = master_address
|
||||
bay.minions_address = minion_addresses
|
||||
bay.save()
|
||||
raise loopingcall.LoopingCallDone()
|
||||
if stack.stack_status == 'DELETE_COMPLETE':
|
||||
LOG.info('Bay has been deleted, stack_id: %s' % bay.stack_id)
|
||||
raise loopingcall.LoopingCallDone()
|
||||
if ((stack.status == 'FAILED') or
|
||||
(attempts_count > cfg.CONF.k8s_heat.max_attempts)):
|
||||
# TODO(yuanying): update status to failed
|
||||
LOG.error('Unable to create bay, stack_id: %s' % bay.stack_id)
|
||||
osc.heat().stacks.delete(bay.stack_id)
|
||||
raise loopingcall.LoopingCallDone()
|
||||
|
||||
lc = loopingcall.FixedIntervalLoopingCall(f=poll_and_check)
|
||||
lc.start(cfg.CONF.k8s_heat.wait_interval, True)
|
||||
|
||||
return bay
|
||||
|
||||
|
@ -85,21 +130,4 @@ class Handler(object):
|
|||
osc.heat().stacks.delete(stack_id)
|
||||
bay.destroy()
|
||||
|
||||
return None
|
||||
|
||||
def bay_show(self, ctxt, uuid):
|
||||
LOG.debug('k8s_heat bay_show')
|
||||
osc = clients.OpenStackClients(ctxt)
|
||||
bay = objects.Bay.get_by_uuid(ctxt, uuid)
|
||||
|
||||
stack_id = bay.stack_id
|
||||
stack = osc.heat().stacks.get(stack_id)
|
||||
|
||||
if stack.status == 'COMPLETE':
|
||||
master_address = stack['outputs'][0]['output_value']
|
||||
minion_addresses = stack['outputs'][2]['output_value']
|
||||
bay.master_address = master_address
|
||||
bay.minions_address = minion_addresses
|
||||
bay.save()
|
||||
|
||||
return bay
|
||||
return None
|
|
@ -0,0 +1,128 @@
|
|||
# Copyright 2014 NEC Corporation. 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.
|
||||
|
||||
from magnum.conductor.handlers import bay_k8s_heat
|
||||
from magnum import objects
|
||||
from magnum.tests import base
|
||||
|
||||
import mock
|
||||
from mock import patch
|
||||
|
||||
|
||||
class TestBayK8sHeat(base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestBayK8sHeat, self).setUp()
|
||||
self.baymodel_dict = {
|
||||
'image_id': 'image_id',
|
||||
'flavor_id': 'flavor_id',
|
||||
'keypair_id': 'keypair_id',
|
||||
'dns_nameserver': 'dns_nameserver',
|
||||
'external_network_id': 'external_network_id'
|
||||
}
|
||||
|
||||
def test_extract_bay_definition(self):
|
||||
baymodel = objects.BayModel({}, **self.baymodel_dict)
|
||||
bay_definition = bay_k8s_heat._extract_bay_definition(
|
||||
baymodel)
|
||||
|
||||
expected = {
|
||||
'ssh_key_name': 'keypair_id',
|
||||
'external_network_id': 'external_network_id',
|
||||
'dns_nameserver': 'dns_nameserver',
|
||||
'server_image': 'image_id',
|
||||
'server_flavor': 'flavor_id'
|
||||
}
|
||||
self.assertEqual(expected, bay_definition)
|
||||
|
||||
def test_extract_bay_definition_without_dns(self):
|
||||
baymodel_dict = self.baymodel_dict
|
||||
baymodel_dict['dns_nameserver'] = None
|
||||
baymodel = objects.BayModel({}, **baymodel_dict)
|
||||
bay_definition = bay_k8s_heat._extract_bay_definition(
|
||||
baymodel)
|
||||
|
||||
expected = {
|
||||
'ssh_key_name': 'keypair_id',
|
||||
'external_network_id': 'external_network_id',
|
||||
'server_image': 'image_id',
|
||||
'server_flavor': 'flavor_id'
|
||||
}
|
||||
self.assertEqual(expected, bay_definition)
|
||||
|
||||
def test_extract_bay_definition_without_server_image(self):
|
||||
baymodel_dict = self.baymodel_dict
|
||||
baymodel_dict['image_id'] = None
|
||||
baymodel = objects.BayModel({}, **baymodel_dict)
|
||||
bay_definition = bay_k8s_heat._extract_bay_definition(
|
||||
baymodel)
|
||||
|
||||
expected = {
|
||||
'ssh_key_name': 'keypair_id',
|
||||
'external_network_id': 'external_network_id',
|
||||
'dns_nameserver': 'dns_nameserver',
|
||||
'server_flavor': 'flavor_id'
|
||||
}
|
||||
self.assertEqual(expected, bay_definition)
|
||||
|
||||
def test_extract_bay_definition_without_server_flavor(self):
|
||||
baymodel_dict = self.baymodel_dict
|
||||
baymodel_dict['flavor_id'] = None
|
||||
baymodel = objects.BayModel({}, **baymodel_dict)
|
||||
bay_definition = bay_k8s_heat._extract_bay_definition(
|
||||
baymodel)
|
||||
|
||||
expected = {
|
||||
'ssh_key_name': 'keypair_id',
|
||||
'external_network_id': 'external_network_id',
|
||||
'dns_nameserver': 'dns_nameserver',
|
||||
'server_image': 'image_id'
|
||||
}
|
||||
self.assertEqual(expected, bay_definition)
|
||||
|
||||
@patch('heatclient.common.template_utils.get_template_contents')
|
||||
@patch('magnum.objects.BayModel.get_by_uuid')
|
||||
@patch('magnum.conductor.handlers.bay_k8s_heat._extract_bay_definition')
|
||||
def test_create_stack(self,
|
||||
mock_extract_bay_definition,
|
||||
mock_objects_baymodel_get_by_uuid,
|
||||
mock_get_template_contents):
|
||||
|
||||
expected_stack_name = 'expected_stack_name'
|
||||
expected_number_of_minions = 1
|
||||
expected_template_contents = 'template_contents'
|
||||
exptected_files = []
|
||||
|
||||
mock_tpl_files = mock.MagicMock()
|
||||
mock_tpl_files.items.return_value = exptected_files
|
||||
mock_get_template_contents.return_value = [
|
||||
mock_tpl_files, expected_template_contents]
|
||||
mock_objects_baymodel_get_by_uuid.return_value = {}
|
||||
mock_extract_bay_definition.return_value = {}
|
||||
mock_heat_client = mock.MagicMock()
|
||||
mock_osc = mock.MagicMock()
|
||||
mock_osc.heat.return_value = mock_heat_client
|
||||
mock_bay = mock.MagicMock()
|
||||
mock_bay.name = expected_stack_name
|
||||
mock_bay.node_count = expected_number_of_minions
|
||||
|
||||
bay_k8s_heat._create_stack({}, mock_osc, mock_bay)
|
||||
|
||||
expected_args = {
|
||||
'stack_name': expected_stack_name,
|
||||
'parameters': {
|
||||
'number_of_minions': str(expected_number_of_minions)},
|
||||
'template': expected_template_contents,
|
||||
'files': dict(exptected_files)
|
||||
}
|
||||
mock_heat_client.stacks.create.assert_called_once_with(**expected_args)
|
Loading…
Reference in New Issue