Merge "Add test and refactoring on bay_create"

changes/21/145121/1
Jenkins 2015-01-06 01:10:10 +00:00 committed by Gerrit Code Review
commit 4cc3f9ed81
4 changed files with 200 additions and 44 deletions

View File

@ -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

View File

View File

@ -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)