Cluster Drivers

- Dynamically load drivers using stevedore
- Changed the entry points to reference drivers instead of
  template definitions
- Implement Create and update driver operations

Change-Id: I5c3259404c796e1935c872cf3109ffecae3cee02
Partially-Implements: blueprint bay-drivers
changes/06/374906/10
murali allada 2016-08-25 10:14:21 -05:00
parent 45f071e36e
commit 104501cfe6
26 changed files with 648 additions and 435 deletions

View File

@ -12,9 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
from heatclient.common import template_utils
from heatclient import exc
from oslo_log import log as logging
from oslo_service import loopingcall
@ -24,13 +21,12 @@ import six
from magnum.common import clients
from magnum.common import exception
from magnum.common import short_id
from magnum.conductor.handlers.common import cert_manager
from magnum.conductor.handlers.common import trust_manager
from magnum.conductor import scale_manager
from magnum.conductor import utils as conductor_utils
import magnum.conf
from magnum.drivers.common import template_def
from magnum.drivers.common import driver
from magnum.i18n import _
from magnum.i18n import _LE
from magnum.i18n import _LI
@ -42,79 +38,6 @@ CONF = magnum.conf.CONF
LOG = logging.getLogger(__name__)
def _extract_template_definition(context, cluster, scale_manager=None):
cluster_template = conductor_utils.retrieve_cluster_template(context,
cluster)
cluster_distro = cluster_template.cluster_distro
cluster_coe = cluster_template.coe
cluster_server_type = cluster_template.server_type
definition = template_def.TemplateDefinition.get_template_definition(
cluster_server_type,
cluster_distro,
cluster_coe)
return definition.extract_definition(context, cluster_template, cluster,
scale_manager=scale_manager)
def _get_env_files(template_path, env_rel_paths):
template_dir = os.path.dirname(template_path)
env_abs_paths = [os.path.join(template_dir, f) for f in env_rel_paths]
environment_files = []
env_map, merged_env = (
template_utils.process_multiple_environments_and_files(
env_paths=env_abs_paths, env_list_tracker=environment_files))
return environment_files, env_map
def _create_stack(context, osc, cluster, create_timeout):
template_path, heat_params, env_files = (
_extract_template_definition(context, cluster))
tpl_files, template = template_utils.get_template_contents(template_path)
environment_files, env_map = _get_env_files(template_path, env_files)
tpl_files.update(env_map)
# Make sure no duplicate stack name
stack_name = '%s-%s' % (cluster.name, short_id.generate_id())
if create_timeout:
heat_timeout = create_timeout
else:
# no create_timeout value was passed in to the request
# so falling back on configuration file value
heat_timeout = CONF.cluster_heat.create_timeout
fields = {
'stack_name': stack_name,
'parameters': heat_params,
'environment_files': environment_files,
'template': template,
'files': tpl_files,
'timeout_mins': heat_timeout
}
created_stack = osc.heat().stacks.create(**fields)
return created_stack
def _update_stack(context, osc, cluster, scale_manager=None, rollback=False):
template_path, heat_params, env_files = _extract_template_definition(
context, cluster, scale_manager=scale_manager)
tpl_files, template = template_utils.get_template_contents(template_path)
environment_files, env_map = _get_env_files(template_path, env_files)
tpl_files.update(env_map)
fields = {
'parameters': heat_params,
'environment_files': environment_files,
'template': template,
'files': tpl_files,
'disable_rollback': not rollback
}
return osc.heat().stacks.update(cluster.stack_id, **fields)
class Handler(object):
def __init__(self):
@ -135,8 +58,14 @@ class Handler(object):
context=context)
conductor_utils.notify_about_cluster_operation(
context, taxonomy.ACTION_CREATE, taxonomy.OUTCOME_PENDING)
created_stack = _create_stack(context, osc, cluster,
create_timeout)
# Get driver
ct = conductor_utils.retrieve_cluster_template(context, cluster)
cluster_driver = driver.Driver.get_driver(ct.server_type,
ct.cluster_distro,
ct.coe)
# Create cluster
created_stack = cluster_driver.create_stack(context, osc, cluster,
create_timeout)
except Exception as e:
cluster.status = fields.ClusterStatus.CREATE_FAILED
cluster.status_reason = six.text_type(e)
@ -154,7 +83,7 @@ class Handler(object):
cluster.status = fields.ClusterStatus.CREATE_IN_PROGRESS
cluster.create()
self._poll_and_check(osc, cluster)
self._poll_and_check(osc, cluster, cluster_driver)
return cluster
@ -189,8 +118,14 @@ class Handler(object):
conductor_utils.notify_about_cluster_operation(
context, taxonomy.ACTION_UPDATE, taxonomy.OUTCOME_PENDING)
_update_stack(context, osc, cluster, manager, rollback)
self._poll_and_check(osc, cluster)
# Get driver
ct = conductor_utils.retrieve_cluster_template(context, cluster)
cluster_driver = driver.Driver.get_driver(ct.server_type,
ct.cluster_distro,
ct.coe)
# Create cluster
cluster_driver.update_stack(context, osc, cluster, manager, rollback)
self._poll_and_check(osc, cluster, cluster_driver)
return cluster
@ -241,26 +176,23 @@ class Handler(object):
return None
def _poll_and_check(self, osc, cluster):
poller = HeatPoller(osc, cluster)
def _poll_and_check(self, osc, cluster, cluster_driver=None):
poller = HeatPoller(osc, cluster, cluster_driver)
lc = loopingcall.FixedIntervalLoopingCall(f=poller.poll_and_check)
lc.start(CONF.cluster_heat.wait_interval, True)
class HeatPoller(object):
def __init__(self, openstack_client, cluster):
def __init__(self, openstack_client, cluster, cluster_driver=None):
self.openstack_client = openstack_client
self.context = self.openstack_client.context
self.cluster = cluster
self.attempts = 0
self.cluster_template = conductor_utils.retrieve_cluster_template(
self.context, cluster)
self.template_def = \
template_def.TemplateDefinition.get_template_definition(
self.cluster_template.server_type,
self.cluster_template.cluster_distro,
self.cluster_template.coe)
if cluster_driver:
self.template_def = cluster_driver.get_template_definition()
def poll_and_check(self):
# TODO(yuanying): temporary implementation to update api_address,
@ -356,11 +288,7 @@ class HeatPoller(object):
if stack_param:
self.cluster.coe_version = stack.parameters[stack_param]
tdef = template_def.TemplateDefinition.get_template_definition(
self.cluster_template.server_type,
self.cluster_template.cluster_distro, self.cluster_template.coe)
version_module_path = tdef.driver_module_path+'.version'
version_module_path = self.template_def.driver_module_path+'.version'
try:
ver = importutils.import_module(version_module_path)
container_version = ver.container_version

View File

@ -23,6 +23,10 @@ cluster_def_opts = [
help=_('Url for etcd public discovery endpoint.'),
deprecated_group='bay'),
cfg.ListOpt('enabled_definitions',
deprecated_for_removal=True,
deprecated_reason=_('This configuration option is no longer '
'used. Installing a new driver enables '
'it for use automatically.'),
default=['magnum_vm_atomic_k8s', 'magnum_bm_fedora_k8s',
'magnum_vm_coreos_k8s', 'magnum_vm_atomic_swarm',
'magnum_vm_ubuntu_mesos'],

View File

@ -0,0 +1,208 @@
# 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.
import os
from heatclient.common import template_utils
from oslo_config import cfg
from oslo_log import log as logging
from pkg_resources import iter_entry_points
from stevedore import driver
from magnum.common import exception
from magnum.common import short_id
from magnum.conductor import utils as conductor_utils
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
def _extract_template_definition(context, cluster, scale_manager=None):
cluster_template = conductor_utils.retrieve_cluster_template(context,
cluster)
cluster_driver = Driver().get_driver(cluster_template.server_type,
cluster_template.cluster_distro,
cluster_template.coe)
definition = cluster_driver.get_template_definition()
return definition.extract_definition(context, cluster_template, cluster,
scale_manager=scale_manager)
def _get_env_files(template_path, env_rel_paths):
template_dir = os.path.dirname(template_path)
env_abs_paths = [os.path.join(template_dir, f) for f in env_rel_paths]
environment_files = []
env_map, merged_env = (
template_utils.process_multiple_environments_and_files(
env_paths=env_abs_paths, env_list_tracker=environment_files))
return environment_files, env_map
class Driver(object):
definitions = None
provides = list()
@classmethod
def load_entry_points(cls):
for entry_point in iter_entry_points('magnum.drivers'):
yield entry_point, entry_point.load(require=False)
@classmethod
def get_drivers(cls):
'''Retrieves cluster drivers from python entry_points.
Example:
With the following classes:
class Driver1(Driver):
provides = [
('server_type1', 'os1', 'coe1')
]
class Driver2(Driver):
provides = [
('server_type2', 'os2', 'coe2')
]
And the following entry_points:
magnum.drivers =
driver_name_1 = some.python.path:Driver1
driver_name_2 = some.python.path:Driver2
get_drivers will return:
{
(server_type1, os1, coe1):
{'driver_name_1': Driver1},
(server_type2, os2, coe2):
{'driver_name_2': Driver2}
}
:return: dict
'''
if not cls.definitions:
cls.definitions = dict()
for entry_point, def_class in cls.load_entry_points():
for cluster_type in def_class.provides:
cluster_type_tuple = (cluster_type['server_type'],
cluster_type['os'],
cluster_type['coe'])
providers = cls.definitions.setdefault(cluster_type_tuple,
dict())
providers['entry_point_name'] = entry_point.name
providers['class'] = def_class
return cls.definitions
@classmethod
def get_driver(cls, server_type, os, coe):
'''Get Driver.
Returns the Driver class for the provided cluster_type.
With the following classes:
class Driver1(Driver):
provides = [
('server_type1', 'os1', 'coe1')
]
class Driver2(Driver):
provides = [
('server_type2', 'os2', 'coe2')
]
And the following entry_points:
magnum.drivers =
driver_name_1 = some.python.path:Driver1
driver_name_2 = some.python.path:Driver2
get_driver('server_type2', 'os2', 'coe2')
will return: Driver2
:param server_type: The server_type the cluster definition will build
on
:param os: The operating system the cluster definition will build on
:param coe: The Container Orchestration Environment the cluster will
produce
:return: class
'''
definition_map = cls.get_drivers()
cluster_type = (server_type, os, coe)
if cluster_type not in definition_map:
raise exception.ClusterTypeNotSupported(
server_type=server_type,
os=os,
coe=coe)
driver_info = definition_map[cluster_type]
# TODO(muralia): once --drivername is supported as an input during
# cluster create, change the following line to use driver name for
# loading.
return driver.DriverManager("magnum.drivers",
driver_info['entry_point_name']).driver()
def create_stack(self, context, osc, cluster, cluster_create_timeout):
template_path, heat_params, env_files = (
_extract_template_definition(context, cluster))
tpl_files, template = template_utils.get_template_contents(
template_path)
environment_files, env_map = _get_env_files(template_path, env_files)
tpl_files.update(env_map)
# Make sure no duplicate stack name
stack_name = '%s-%s' % (cluster.name, short_id.generate_id())
if cluster_create_timeout:
heat_timeout = cluster_create_timeout
else:
# no cluster_create_timeout value was passed in to the request
# so falling back on configuration file value
heat_timeout = cfg.CONF.cluster_heat.create_timeout
fields = {
'stack_name': stack_name,
'parameters': heat_params,
'environment_files': environment_files,
'template': template,
'files': tpl_files,
'timeout_mins': heat_timeout
}
created_stack = osc.heat().stacks.create(**fields)
return created_stack
def update_stack(self, context, osc, cluster, scale_manager=None,
rollback=False):
template_path, heat_params, env_files = _extract_template_definition(
context, cluster, scale_manager=scale_manager)
tpl_files, template = template_utils.get_template_contents(
template_path)
environment_files, env_map = _get_env_files(template_path, env_files)
tpl_files.update(env_map)
fields = {
'parameters': heat_params,
'environment_files': environment_files,
'template': template,
'files': tpl_files,
'disable_rollback': not rollback
}
return osc.heat().stacks.update(cluster.stack_id, **fields)

View File

@ -16,7 +16,6 @@ import ast
from oslo_config import cfg
from oslo_log import log as logging
from pkg_resources import iter_entry_points
import requests
import six
@ -126,118 +125,11 @@ class TemplateDefinition(object):
and Heat templates. Each TemplateDefinition has a mapping of Heat
parameters.
'''
definitions = None
provides = list()
def __init__(self):
self.param_mappings = list()
self.output_mappings = list()
@staticmethod
def load_entry_points():
for entry_point in iter_entry_points('magnum.template_definitions'):
yield entry_point, entry_point.load(require=False)
@classmethod
def get_template_definitions(cls):
'''Retrieves cluster definitions from python entry_points.
Example:
With the following classes:
class TemplateDefinition1(TemplateDefinition):
provides = [
('server_type1', 'os1', 'coe1')
]
class TemplateDefinition2(TemplateDefinition):
provides = [
('server_type2', 'os2', 'coe2')
]
And the following entry_points:
magnum.template_definitions =
template_name_1 = some.python.path:TemplateDefinition1
template_name_2 = some.python.path:TemplateDefinition2
get_template_definitions will return:
{
(server_type1, os1, coe1):
{'template_name_1': TemplateDefinition1},
(server_type2, os2, coe2):
{'template_name_2': TemplateDefinition2}
}
:return: dict
'''
if not cls.definitions:
cls.definitions = dict()
for entry_point, def_class in cls.load_entry_points():
for cluster_type in def_class.provides:
cluster_type_tuple = (cluster_type['server_type'],
cluster_type['os'],
cluster_type['coe'])
providers = cls.definitions.setdefault(cluster_type_tuple,
dict())
providers[entry_point.name] = def_class
return cls.definitions
@classmethod
def get_template_definition(cls, server_type, os, coe):
'''Get enabled TemplateDefinitions.
Returns the enabled TemplateDefinition class for the provided
cluster_type.
With the following classes:
class TemplateDefinition1(TemplateDefinition):
provides = [
('server_type1', 'os1', 'coe1')
]
class TemplateDefinition2(TemplateDefinition):
provides = [
('server_type2', 'os2', 'coe2')
]
And the following entry_points:
magnum.template_definitions =
template_name_1 = some.python.path:TemplateDefinition1
template_name_2 = some.python.path:TemplateDefinition2
get_template_name_1_definition('server_type2', 'os2', 'coe2')
will return: TemplateDefinition2
:param server_type: The server_type the cluster definition
will build on
:param os: The operating system the cluster definition will build on
:param coe: The Container Orchestration Environment the cluster will
produce
:return: class
'''
definition_map = cls.get_template_definitions()
cluster_type = (server_type, os, coe)
if cluster_type not in definition_map:
raise exception.ClusterTypeNotSupported(
server_type=server_type,
os=os,
coe=coe)
type_definitions = definition_map[cluster_type]
for name in CONF.cluster.enabled_definitions:
if name in type_definitions:
return type_definitions[name]()
raise exception.ClusterTypeNotEnabled(
server_type=server_type, os=os, coe=coe)
def add_parameter(self, *args, **kwargs):
param = ParameterMapping(*args, **kwargs)
self.param_mappings.append(param)

View File

@ -0,0 +1,27 @@
# Copyright 2016 Rackspace Inc. 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.drivers.common import driver
from magnum.drivers.k8s_coreos_v1 import template_def
class Driver(driver.Driver):
provides = [
{'server_type': 'vm',
'os': 'coreos',
'coe': 'kubernetes'},
]
def get_template_definition(self):
return template_def.CoreOSK8sTemplateDefinition()

View File

@ -23,12 +23,6 @@ CONF = cfg.CONF
class CoreOSK8sTemplateDefinition(k8s_template_def.K8sTemplateDefinition):
"""Kubernetes template for CoreOS VM."""
provides = [
{'server_type': 'vm',
'os': 'coreos',
'coe': 'kubernetes'},
]
def __init__(self):
super(CoreOSK8sTemplateDefinition, self).__init__()
self.add_output('kube_minions',

View File

@ -13,5 +13,5 @@
# limitations under the License.
version = '1.0.0'
driver = 'k8s_coreos'
driver = 'k8s_coreos_v1'
container_version = '1.11.2'

View File

@ -0,0 +1,27 @@
# Copyright 2016 Rackspace Inc. 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.drivers.common import driver
from magnum.drivers.k8s_fedora_atomic_v1 import template_def
class Driver(driver.Driver):
provides = [
{'server_type': 'vm',
'os': 'fedora-atomic',
'coe': 'kubernetes'},
]
def get_template_definition(self):
return template_def.AtomicK8sTemplateDefinition()

View File

@ -23,12 +23,6 @@ CONF = cfg.CONF
class AtomicK8sTemplateDefinition(kftd.K8sFedoraTemplateDefinition):
"""Kubernetes template for a Fedora Atomic VM."""
provides = [
{'server_type': 'vm',
'os': 'fedora-atomic',
'coe': 'kubernetes'},
]
@property
def driver_module_path(self):
return __name__[:__name__.rindex('.')]

View File

@ -13,5 +13,5 @@
# limitations under the License.
version = '1.0.0'
driver = 'k8s_fedora_atomic'
driver = 'k8s_fedora_atomic_v1'
container_version = '1.9.1'

View File

@ -0,0 +1,27 @@
# Copyright 2016 Rackspace Inc. 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.drivers.common import driver
from magnum.drivers.k8s_fedora_ironic_v1 import template_def
class Driver(driver.Driver):
provides = [
{'server_type': 'bm',
'os': 'fedora',
'coe': 'kubernetes'},
]
def get_template_definition(self):
return template_def.FedoraK8sIronicTemplateDefinition()

View File

@ -27,12 +27,6 @@ LOG = logging.getLogger(__name__)
class FedoraK8sIronicTemplateDefinition(kftd.K8sFedoraTemplateDefinition):
"""Kubernetes template for a Fedora Baremetal."""
provides = [
{'server_type': 'bm',
'os': 'fedora',
'coe': 'kubernetes'},
]
def __init__(self):
super(FedoraK8sIronicTemplateDefinition, self).__init__()
self.add_parameter('fixed_subnet',

View File

@ -0,0 +1,17 @@
# Copyright 2016 - Rackspace Hosting
#
# 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.
version = '1.0.0'
driver = 'k8s_fedora_ironic_v1'
container_version = '1.9.1'

View File

@ -0,0 +1,27 @@
# Copyright 2016 Rackspace Inc. 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.drivers.common import driver
from magnum.drivers.mesos_ubuntu_v1 import template_def
class Driver(driver.Driver):
provides = [
{'server_type': 'vm',
'os': 'ubuntu',
'coe': 'mesos'},
]
def get_template_definition(self):
return template_def.UbuntuMesosTemplateDefinition()

View File

@ -19,10 +19,6 @@ from magnum.drivers.common import template_def
class UbuntuMesosTemplateDefinition(template_def.BaseTemplateDefinition):
"""Mesos template for Ubuntu VM."""
provides = [
{'server_type': 'vm', 'os': 'ubuntu', 'coe': 'mesos'},
]
def __init__(self):
super(UbuntuMesosTemplateDefinition, self).__init__()
self.add_parameter('external_network',

View File

@ -13,5 +13,5 @@
# limitations under the License.
version = '1.0.0'
driver = 'mesos_ubuntu'
driver = 'mesos_ubuntu_v1'
container_version = '1.9.1'

View File

@ -0,0 +1,27 @@
# Copyright 2016 Rackspace Inc. 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.drivers.common import driver
from magnum.drivers.swarm_fedora_atomic_v1 import template_def
class Driver(driver.Driver):
provides = [
{'server_type': 'vm',
'os': 'fedora-atomic',
'coe': 'swarm'},
]
def get_template_definition(self):
return template_def.AtomicSwarmTemplateDefinition()

View File

@ -22,10 +22,6 @@ CONF = cfg.CONF
class AtomicSwarmTemplateDefinition(sftd.SwarmFedoraTemplateDefinition):
"""Docker swarm template for a Fedora Atomic VM."""
provides = [
{'server_type': 'vm', 'os': 'fedora-atomic', 'coe': 'swarm'},
]
@property
def driver_module_path(self):
return __name__[:__name__.rindex('.')]

View File

@ -13,5 +13,5 @@
# limitations under the License.
version = '1.0.0'
driver = 'swarm_atomic'
driver = 'swarm_fedora_atomic_v1'
container_version = '1.9.1'

View File

@ -1,32 +0,0 @@
# 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.drivers.common import template_def as tdef
from magnum.tests import base
class TestTemplates(base.TestCase):
def test_templates_list(self):
entry_points = list(tdef.TemplateDefinition.load_entry_points())
self.assertEqual(5, len(entry_points))
templates = []
for entry_point, def_class in entry_points:
templates.append(def_class.__name__)
self.assertEqual(['AtomicK8sTemplateDefinition',
'AtomicSwarmTemplateDefinition',
'CoreOSK8sTemplateDefinition',
'FedoraK8sIronicTemplateDefinition',
'UbuntuMesosTemplateDefinition'],
sorted(templates))

View File

@ -25,6 +25,7 @@ from pycadf import cadftaxonomy as taxonomy
from magnum.common import exception
from magnum.conductor.handlers import cluster_conductor
import magnum.conf
from magnum.drivers.k8s_fedora_atomic_v1 import driver as k8s_atomic_dr
from magnum import objects
from magnum.objects.fields import ClusterStatus as cluster_status
from magnum.tests import base
@ -51,11 +52,11 @@ class TestHandler(db_base.DbTestCase):
@patch('magnum.conductor.scale_manager.ScaleManager')
@patch(
'magnum.conductor.handlers.cluster_conductor.Handler._poll_and_check')
@patch('magnum.conductor.handlers.cluster_conductor._update_stack')
@patch('magnum.drivers.common.driver.Driver.get_driver')
@patch('magnum.common.clients.OpenStackClients')
def test_update_node_count_success(
self, mock_openstack_client_class,
mock_update_stack, mock_poll_and_check,
mock_driver, mock_poll_and_check,
mock_scale_manager):
def side_effect(*args, **kwargs):
self.cluster.node_count = 2
@ -67,6 +68,8 @@ class TestHandler(db_base.DbTestCase):
mock_heat_client.stacks.get.return_value = mock_heat_stack
mock_openstack_client = mock_openstack_client_class.return_value
mock_openstack_client.heat.return_value = mock_heat_client
mock_dr = mock.MagicMock()
mock_driver.return_value = mock_dr
self.cluster.node_count = 2
self.handler.cluster_update(self.context, self.cluster)
@ -78,7 +81,7 @@ class TestHandler(db_base.DbTestCase):
self.assertEqual(
taxonomy.OUTCOME_PENDING, notifications[0].payload['outcome'])
mock_update_stack.assert_called_once_with(
mock_dr.update_stack.assert_called_once_with(
self.context, mock_openstack_client, self.cluster,
mock_scale_manager.return_value, False)
cluster = objects.Cluster.get(self.context, self.cluster.uuid)
@ -86,11 +89,10 @@ class TestHandler(db_base.DbTestCase):
@patch(
'magnum.conductor.handlers.cluster_conductor.Handler._poll_and_check')
@patch('magnum.conductor.handlers.cluster_conductor._update_stack')
@patch('magnum.common.clients.OpenStackClients')
def test_update_node_count_failure(
self, mock_openstack_client_class,
mock_update_stack, mock_poll_and_check):
mock_poll_and_check):
def side_effect(*args, **kwargs):
self.cluster.node_count = 2
self.cluster.save()
@ -119,11 +121,11 @@ class TestHandler(db_base.DbTestCase):
@patch('magnum.conductor.scale_manager.ScaleManager')
@patch(
'magnum.conductor.handlers.cluster_conductor.Handler._poll_and_check')
@patch('magnum.conductor.handlers.cluster_conductor._update_stack')
@patch('magnum.drivers.common.driver.Driver.get_driver')
@patch('magnum.common.clients.OpenStackClients')
def _test_update_cluster_status_complete(
self, expect_status, mock_openstack_client_class,
mock_update_stack, mock_poll_and_check,
mock_driver, mock_poll_and_check,
mock_scale_manager):
def side_effect(*args, **kwargs):
self.cluster.node_count = 2
@ -135,6 +137,8 @@ class TestHandler(db_base.DbTestCase):
mock_heat_client.stacks.get.return_value = mock_heat_stack
mock_openstack_client = mock_openstack_client_class.return_value
mock_openstack_client.heat.return_value = mock_heat_client
mock_dr = mock.MagicMock()
mock_driver.return_value = mock_dr
self.cluster.node_count = 2
self.handler.cluster_update(self.context, self.cluster)
@ -146,7 +150,7 @@ class TestHandler(db_base.DbTestCase):
self.assertEqual(
taxonomy.OUTCOME_PENDING, notifications[0].payload['outcome'])
mock_update_stack.assert_called_once_with(
mock_dr.update_stack.assert_called_once_with(
self.context, mock_openstack_client, self.cluster,
mock_scale_manager.return_value, False)
cluster = objects.Cluster.get(self.context, self.cluster.uuid)
@ -183,10 +187,10 @@ class TestHandler(db_base.DbTestCase):
@patch('magnum.conductor.handlers.cluster_conductor.HeatPoller')
@patch('magnum.conductor.handlers.cluster_conductor.trust_manager')
@patch('magnum.conductor.handlers.cluster_conductor.cert_manager')
@patch('magnum.conductor.handlers.cluster_conductor._create_stack')
@patch('magnum.drivers.common.driver.Driver.get_driver')
@patch('magnum.common.clients.OpenStackClients')
def test_create(self, mock_openstack_client_class,
mock_create_stack, mock_cm, mock_trust_manager,
mock_driver, mock_cm, mock_trust_manager,
mock_heat_poller_class):
timeout = 15
mock_poller = mock.MagicMock()
@ -194,11 +198,13 @@ class TestHandler(db_base.DbTestCase):
mock_heat_poller_class.return_value = mock_poller
osc = mock.sentinel.osc
mock_openstack_client_class.return_value = osc
mock_dr = mock.MagicMock()
mock_driver.return_value = mock_dr
def create_stack_side_effect(context, osc, cluster, timeout):
return {'stack': {'id': 'stack-id'}}
mock_create_stack.side_effect = create_stack_side_effect
mock_dr.create_stack.side_effect = create_stack_side_effect
# FixMe(eliqiao): cluster_create will call cluster.create()
# again, this so bad because we have already called it in setUp
@ -221,9 +227,9 @@ class TestHandler(db_base.DbTestCase):
self.assertEqual(
taxonomy.OUTCOME_PENDING, notifications[0].payload['outcome'])
mock_create_stack.assert_called_once_with(self.context,
mock.sentinel.osc,
self.cluster, timeout)
mock_dr.create_stack.assert_called_once_with(self.context,
mock.sentinel.osc,
self.cluster, timeout)
mock_cm.generate_certificates_to_cluster.assert_called_once_with(
self.cluster, context=self.context)
self.assertEqual(cluster_status.CREATE_IN_PROGRESS, cluster.status)
@ -264,14 +270,16 @@ class TestHandler(db_base.DbTestCase):
@patch('magnum.objects.Cluster.create')
@patch('magnum.conductor.handlers.cluster_conductor.trust_manager')
@patch('magnum.conductor.handlers.cluster_conductor.cert_manager')
@patch('magnum.conductor.handlers.cluster_conductor._create_stack')
@patch('magnum.drivers.common.driver.Driver.get_driver')
@patch('magnum.common.clients.OpenStackClients')
def test_create_handles_bad_request(self, mock_openstack_client_class,
mock_create_stack,
mock_driver,
mock_cert_manager,
mock_trust_manager,
mock_cluster_create):
mock_create_stack.side_effect = exc.HTTPBadRequest
mock_dr = mock.MagicMock()
mock_driver.return_value = mock_dr
mock_dr.create_stack.side_effect = exc.HTTPBadRequest
self._test_create_failed(
mock_openstack_client_class,
@ -321,10 +329,8 @@ class TestHandler(db_base.DbTestCase):
@patch('magnum.objects.Cluster.create')
@patch('magnum.conductor.handlers.cluster_conductor.trust_manager')
@patch('magnum.conductor.handlers.cluster_conductor.cert_manager')
@patch('magnum.conductor.handlers.cluster_conductor._create_stack')
@patch('magnum.common.clients.OpenStackClients')
def test_create_with_trust_failed(self, mock_openstack_client_class,
mock_create_stack,
mock_cert_manager,
mock_trust_manager,
mock_cluster_create):
@ -350,18 +356,20 @@ class TestHandler(db_base.DbTestCase):
@patch('magnum.objects.Cluster.create')
@patch('magnum.conductor.handlers.cluster_conductor.trust_manager')
@patch('magnum.conductor.handlers.cluster_conductor.cert_manager')
@patch('magnum.conductor.handlers.cluster_conductor._create_stack')
@patch('magnum.drivers.common.driver.Driver.get_driver')
@patch('magnum.common.clients.OpenStackClients')
def test_create_with_invalid_unicode_name(self,
mock_openstack_client_class,
mock_create_stack,
mock_driver,
mock_cert_manager,
mock_trust_manager,
mock_cluster_create):
error_message = six.u("""Invalid stack name 测试集群-zoyh253geukk
must contain only alphanumeric or "_-."
characters, must start with alpha""")
mock_create_stack.side_effect = exc.HTTPBadRequest(error_message)
mock_dr = mock.MagicMock()
mock_driver.return_value = mock_dr
mock_dr.create_stack.side_effect = exc.HTTPBadRequest(error_message)
self._test_create_failed(
mock_openstack_client_class,
@ -386,28 +394,30 @@ class TestHandler(db_base.DbTestCase):
@patch('heatclient.common.template_utils'
'.process_multiple_environments_and_files')
@patch('heatclient.common.template_utils.get_template_contents')
@patch('magnum.conductor.handlers.cluster_conductor'
'._extract_template_definition')
@patch('magnum.conductor.handlers.cluster_conductor.trust_manager')
@patch('magnum.conductor.handlers.cluster_conductor.cert_manager')
@patch('magnum.conductor.handlers.cluster_conductor.short_id')
@patch('magnum.drivers.common.driver._extract_template_definition')
@patch('magnum.drivers.common.driver.Driver.get_driver')
@patch('magnum.common.clients.OpenStackClients')
@patch('magnum.common.short_id.generate_id')
def test_create_with_environment(self,
mock_openstack_client_class,
mock_short_id,
mock_openstack_client_class,
mock_driver,
mock_extract_tmpl_def,
mock_cert_manager,
mock_trust_manager,
mock_extract_tmpl_def,
mock_get_template_contents,
mock_process_mult,
mock_heat_poller_class):
timeout = 15
self.cluster.cluster_template_id = self.cluster_template.uuid
cluster_name = self.cluster.name
mock_short_id.generate_id.return_value = 'short_id'
mock_poller = mock.MagicMock()
mock_poller.poll_and_check.return_value = loopingcall.LoopingCallDone()
mock_heat_poller_class.return_value = mock_poller
mock_driver.return_value = k8s_atomic_dr.Driver()
mock_short_id.return_value = 'short_id'
mock_extract_tmpl_def.return_value = (
'the/template/path.yaml',
@ -515,7 +525,8 @@ class TestHeatPoller(base.TestCase):
@patch('magnum.conductor.utils.retrieve_cluster_template')
@patch('oslo_config.cfg')
@patch('magnum.common.clients.OpenStackClients')
def setup_poll_test(self, mock_openstack_client, cfg,
@patch('magnum.drivers.common.driver.Driver.get_driver')
def setup_poll_test(self, mock_driver, mock_openstack_client, cfg,
mock_retrieve_cluster_template):
cfg.CONF.cluster_heat.max_attempts = 10
@ -529,7 +540,9 @@ class TestHeatPoller(base.TestCase):
cluster_template = objects.ClusterTemplate(self.context,
**cluster_template_dict)
mock_retrieve_cluster_template.return_value = cluster_template
poller = cluster_conductor.HeatPoller(mock_openstack_client, cluster)
mock_driver.return_value = k8s_atomic_dr.Driver()
poller = cluster_conductor.HeatPoller(mock_openstack_client, cluster,
k8s_atomic_dr.Driver())
poller.get_version_info = mock.MagicMock()
return (mock_heat_stack, cluster, poller)

View File

@ -15,8 +15,10 @@
import mock
from mock import patch
from magnum.conductor.handlers import cluster_conductor
import magnum.conf
from magnum.drivers.common import driver
from magnum.drivers.k8s_coreos_v1 import driver as k8s_coreos_dr
from magnum.drivers.k8s_fedora_atomic_v1 import driver as k8s_dr
from magnum import objects
from magnum.tests import base
@ -88,15 +90,18 @@ class TestClusterConductorWithK8s(base.TestCase):
@patch('requests.get')
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
@patch('magnum.drivers.common.driver.Driver.get_driver')
def test_extract_template_definition(
self,
mock_driver,
mock_objects_cluster_template_get_by_uuid,
mock_get):
self._test_extract_template_definition(
mock_objects_cluster_template_get_by_uuid, mock_get)
mock_driver, mock_objects_cluster_template_get_by_uuid, mock_get)
def _test_extract_template_definition(
self,
mock_driver,
mock_objects_cluster_template_get_by_uuid,
mock_get,
missing_attr=None):
@ -114,11 +119,12 @@ class TestClusterConductorWithK8s(base.TestCase):
mock_resp.text = expected_result
mock_get.return_value = mock_resp
cluster = objects.Cluster(self.context, **self.cluster_dict)
mock_driver.return_value = k8s_dr.Driver()
(template_path,
definition,
env_files) = cluster_conductor._extract_template_definition(
self.context, cluster)
env_files) = driver._extract_template_definition(self.context,
cluster)
mapping = {
'dns_nameserver': 'dns_nameserver',
@ -191,8 +197,10 @@ class TestClusterConductorWithK8s(base.TestCase):
@patch('requests.get')
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
@patch('magnum.drivers.common.driver.Driver.get_driver')
def test_extract_template_definition_with_registry(
self,
mock_driver,
mock_objects_cluster_template_get_by_uuid,
mock_get):
self.cluster_template_dict['registry_enabled'] = True
@ -206,6 +214,7 @@ class TestClusterConductorWithK8s(base.TestCase):
mock_resp.text = expected_result
mock_get.return_value = mock_resp
cluster = objects.Cluster(self.context, **self.cluster_dict)
mock_driver.return_value = k8s_dr.Driver()
CONF.set_override('swift_region',
'RegionOne',
@ -213,8 +222,8 @@ class TestClusterConductorWithK8s(base.TestCase):
(template_path,
definition,
env_files) = cluster_conductor._extract_template_definition(
self.context, cluster)
env_files) = driver._extract_template_definition(self.context,
cluster)
expected = {
'auth_url': 'http://192.168.10.10:5000/v3',
@ -263,8 +272,10 @@ class TestClusterConductorWithK8s(base.TestCase):
@patch('requests.get')
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
@patch('magnum.drivers.common.driver.Driver.get_driver')
def test_extract_template_definition_coreos_with_disovery(
self,
mock_driver,
mock_objects_cluster_template_get_by_uuid,
mock_get):
self.cluster_template_dict['cluster_distro'] = 'coreos'
@ -278,11 +289,12 @@ class TestClusterConductorWithK8s(base.TestCase):
mock_resp.text = expected_result
mock_get.return_value = mock_resp
cluster = objects.Cluster(self.context, **self.cluster_dict)
mock_driver.return_value = k8s_coreos_dr.Driver()
(template_path,
definition,
env_files) = cluster_conductor._extract_template_definition(
self.context, cluster)
env_files) = driver._extract_template_definition(self.context,
cluster)
expected = {
'ssh_key_name': 'keypair_id',
@ -322,8 +334,10 @@ class TestClusterConductorWithK8s(base.TestCase):
@patch('requests.get')
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
@patch('magnum.drivers.common.driver.Driver.get_driver')
def test_extract_template_definition_coreos_no_discoveryurl(
self,
mock_driver,
mock_objects_cluster_template_get_by_uuid,
reqget):
self.cluster_template_dict['cluster_distro'] = 'coreos'
@ -335,11 +349,12 @@ class TestClusterConductorWithK8s(base.TestCase):
mock_objects_cluster_template_get_by_uuid.return_value = \
cluster_template
cluster = objects.Cluster(self.context, **self.cluster_dict)
mock_driver.return_value = k8s_coreos_dr.Driver()
(template_path,
definition,
env_files) = cluster_conductor._extract_template_definition(
self.context, cluster)
env_files) = driver._extract_template_definition(self.context,
cluster)
expected = {
'ssh_key_name': 'keypair_id',
@ -379,107 +394,145 @@ class TestClusterConductorWithK8s(base.TestCase):
@patch('requests.get')
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
@patch('magnum.drivers.common.driver.Driver.get_driver')
def test_extract_template_definition_without_dns(
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='dns_nameserver')
@patch('requests.get')
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
@patch('magnum.drivers.common.driver.Driver.get_driver')
def test_extract_template_definition_without_server_image(
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='image_id')
@patch('requests.get')
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
@patch('magnum.drivers.common.driver.Driver.get_driver')
def test_extract_template_definition_without_minion_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='flavor_id')
@patch('requests.get')
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
@patch('magnum.drivers.common.driver.Driver.get_driver')
def test_extract_template_definition_without_docker_volume_size(
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='docker_volume_size')
@patch('requests.get')
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
@patch('magnum.drivers.common.driver.Driver.get_driver')
def test_extract_template_definition_without_docker_storage_driver(
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='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('magnum.objects.ClusterTemplate.get_by_uuid')
@patch('magnum.drivers.common.driver.Driver.get_driver')
def test_extract_template_definition_without_apiserver_port(
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='apiserver_port')
@patch('requests.get')
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
@patch('magnum.drivers.common.driver.Driver.get_driver')
def test_extract_template_definition_without_node_count(
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,