Merge "Bay driver implementation"
This commit is contained in:
commit
cb99c64614
@ -19,7 +19,7 @@ from cliff import commandmanager
|
|||||||
from cliff import lister
|
from cliff import lister
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
from magnum.conductor import template_definition as tdef
|
from magnum.drivers.common import template_def as tdef
|
||||||
from magnum import version
|
from magnum import version
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
@ -29,8 +29,8 @@ from magnum.common import short_id
|
|||||||
from magnum.conductor.handlers.common import cert_manager
|
from magnum.conductor.handlers.common import cert_manager
|
||||||
from magnum.conductor.handlers.common import trust_manager
|
from magnum.conductor.handlers.common import trust_manager
|
||||||
from magnum.conductor import scale_manager
|
from magnum.conductor import scale_manager
|
||||||
from magnum.conductor.template_definition import TemplateDefinition as TDef
|
|
||||||
from magnum.conductor import utils as conductor_utils
|
from magnum.conductor import utils as conductor_utils
|
||||||
|
from magnum.drivers.common.template_def import TemplateDefinition as TDef
|
||||||
from magnum.i18n import _
|
from magnum.i18n import _
|
||||||
from magnum.i18n import _LE
|
from magnum.i18n import _LE
|
||||||
from magnum.i18n import _LI
|
from magnum.i18n import _LI
|
||||||
|
@ -11,28 +11,18 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
import abc
|
|
||||||
import ast
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from pkg_resources import iter_entry_points
|
|
||||||
import requests
|
|
||||||
from requests import exceptions as req_exceptions
|
|
||||||
import six
|
|
||||||
|
|
||||||
from magnum.common import clients
|
|
||||||
from magnum.common import exception
|
|
||||||
from magnum.common import paths
|
from magnum.common import paths
|
||||||
|
from magnum.drivers.common import template_def
|
||||||
from magnum.i18n import _
|
from magnum.i18n import _
|
||||||
from magnum.i18n import _LW
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
KUBE_SECURE_PORT = '6443'
|
KUBE_SECURE_PORT = '6443'
|
||||||
KUBE_INSECURE_PORT = '8080'
|
KUBE_INSECURE_PORT = '8080'
|
||||||
DOCKER_PORT = '2376'
|
|
||||||
|
|
||||||
template_def_opts = [
|
template_def_opts = [
|
||||||
cfg.StrOpt('k8s_atomic_template_path',
|
cfg.StrOpt('k8s_atomic_template_path',
|
||||||
@ -50,11 +40,6 @@ template_def_opts = [
|
|||||||
cfg.StrOpt('etcd_discovery_service_endpoint_format',
|
cfg.StrOpt('etcd_discovery_service_endpoint_format',
|
||||||
default='https://discovery.etcd.io/new?size=%(size)d',
|
default='https://discovery.etcd.io/new?size=%(size)d',
|
||||||
help=_('Url for etcd public discovery endpoint.')),
|
help=_('Url for etcd public discovery endpoint.')),
|
||||||
cfg.StrOpt('swarm_atomic_template_path',
|
|
||||||
default=paths.basedir_def('templates/swarm/'
|
|
||||||
'swarmcluster.yaml'),
|
|
||||||
help=_('Location of template to build a swarm '
|
|
||||||
'cluster on atomic.')),
|
|
||||||
cfg.StrOpt('mesos_ubuntu_template_path',
|
cfg.StrOpt('mesos_ubuntu_template_path',
|
||||||
default=paths.basedir_def('templates/mesos/'
|
default=paths.basedir_def('templates/mesos/'
|
||||||
'mesoscluster.yaml'),
|
'mesoscluster.yaml'),
|
||||||
@ -80,385 +65,7 @@ CONF.register_opts(template_def_opts, group='bay')
|
|||||||
CONF.register_opts(docker_registry_opts, group='docker_registry')
|
CONF.register_opts(docker_registry_opts, group='docker_registry')
|
||||||
|
|
||||||
|
|
||||||
class ParameterMapping(object):
|
class K8sApiAddressOutputMapping(template_def.OutputMapping):
|
||||||
"""A mapping associating heat param and bay/baymodel attr.
|
|
||||||
|
|
||||||
A ParameterMapping is an association of a Heat parameter name with
|
|
||||||
an attribute on a Bay, Baymodel, or both.
|
|
||||||
|
|
||||||
In the case of both baymodel_attr and bay_attr being set, the Baymodel
|
|
||||||
will be checked first and then Bay if the attribute isn't set on the
|
|
||||||
Baymodel.
|
|
||||||
|
|
||||||
Parameters can also be set as 'required'. If a required parameter
|
|
||||||
isn't set, a RequiredArgumentNotProvided exception will be raised.
|
|
||||||
"""
|
|
||||||
def __init__(self, heat_param, baymodel_attr=None,
|
|
||||||
bay_attr=None, required=False,
|
|
||||||
param_type=lambda x: x):
|
|
||||||
self.heat_param = heat_param
|
|
||||||
self.baymodel_attr = baymodel_attr
|
|
||||||
self.bay_attr = bay_attr
|
|
||||||
self.required = required
|
|
||||||
self.param_type = param_type
|
|
||||||
|
|
||||||
def set_param(self, params, baymodel, bay):
|
|
||||||
value = None
|
|
||||||
|
|
||||||
if (self.baymodel_attr and
|
|
||||||
getattr(baymodel, self.baymodel_attr, None) is not None):
|
|
||||||
value = getattr(baymodel, self.baymodel_attr)
|
|
||||||
elif (self.bay_attr and
|
|
||||||
getattr(bay, self.bay_attr, None) is not None):
|
|
||||||
value = getattr(bay, self.bay_attr)
|
|
||||||
elif self.required:
|
|
||||||
kwargs = dict(heat_param=self.heat_param)
|
|
||||||
raise exception.RequiredParameterNotProvided(**kwargs)
|
|
||||||
|
|
||||||
if value is not None:
|
|
||||||
value = self.param_type(value)
|
|
||||||
params[self.heat_param] = value
|
|
||||||
|
|
||||||
|
|
||||||
class OutputMapping(object):
|
|
||||||
"""A mapping associating heat outputs and bay attr.
|
|
||||||
|
|
||||||
An OutputMapping is an association of a Heat output with a key
|
|
||||||
Magnum understands.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, heat_output, bay_attr=None):
|
|
||||||
self.bay_attr = bay_attr
|
|
||||||
self.heat_output = heat_output
|
|
||||||
|
|
||||||
def set_output(self, stack, baymodel, bay):
|
|
||||||
if self.bay_attr is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
output_value = self.get_output_value(stack)
|
|
||||||
if output_value is not None:
|
|
||||||
setattr(bay, self.bay_attr, output_value)
|
|
||||||
|
|
||||||
def matched(self, output_key):
|
|
||||||
return self.heat_output == output_key
|
|
||||||
|
|
||||||
def get_output_value(self, stack):
|
|
||||||
for output in stack.to_dict().get('outputs', []):
|
|
||||||
if output['output_key'] == self.heat_output:
|
|
||||||
return output['output_value']
|
|
||||||
|
|
||||||
LOG.warning(_LW('stack does not have output_key %s'), self.heat_output)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
|
||||||
class TemplateDefinition(object):
|
|
||||||
'''A mapping between Magnum objects and Heat templates.
|
|
||||||
|
|
||||||
A TemplateDefinition is essentially a mapping between Magnum objects
|
|
||||||
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 bay 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 bay_type in def_class.provides:
|
|
||||||
bay_type_tuple = (bay_type['server_type'],
|
|
||||||
bay_type['os'],
|
|
||||||
bay_type['coe'])
|
|
||||||
providers = cls.definitions.setdefault(bay_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
|
|
||||||
bay_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 bay definition
|
|
||||||
will build on
|
|
||||||
:param os: The operation system the bay definition will build on
|
|
||||||
:param coe: The Container Orchestration Environment the bay will
|
|
||||||
produce
|
|
||||||
|
|
||||||
:return: class
|
|
||||||
'''
|
|
||||||
|
|
||||||
definition_map = cls.get_template_definitions()
|
|
||||||
bay_type = (server_type, os, coe)
|
|
||||||
|
|
||||||
if bay_type not in definition_map:
|
|
||||||
raise exception.BayTypeNotSupported(
|
|
||||||
server_type=server_type,
|
|
||||||
os=os,
|
|
||||||
coe=coe)
|
|
||||||
type_definitions = definition_map[bay_type]
|
|
||||||
|
|
||||||
for name in cfg.CONF.bay.enabled_definitions:
|
|
||||||
if name in type_definitions:
|
|
||||||
return type_definitions[name]()
|
|
||||||
|
|
||||||
raise exception.BayTypeNotEnabled(
|
|
||||||
server_type=server_type, os=os, coe=coe)
|
|
||||||
|
|
||||||
def add_parameter(self, *args, **kwargs):
|
|
||||||
param = ParameterMapping(*args, **kwargs)
|
|
||||||
self.param_mappings.append(param)
|
|
||||||
|
|
||||||
def add_output(self, *args, **kwargs):
|
|
||||||
mapping_type = kwargs.pop('mapping_type', OutputMapping)
|
|
||||||
output = mapping_type(*args, **kwargs)
|
|
||||||
self.output_mappings.append(output)
|
|
||||||
|
|
||||||
def get_output(self, *args, **kwargs):
|
|
||||||
for output in self.output_mappings:
|
|
||||||
if output.matched(*args, **kwargs):
|
|
||||||
return output
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_params(self, context, baymodel, bay, **kwargs):
|
|
||||||
"""Pulls template parameters from Baymodel and/or Bay.
|
|
||||||
|
|
||||||
:param context: Context to pull template parameters for
|
|
||||||
:param baymodel: Baymodel to pull template parameters from
|
|
||||||
:param bay: Bay to pull template parameters from
|
|
||||||
:param extra_params: Any extra params to be provided to the template
|
|
||||||
|
|
||||||
:return: dict of template parameters
|
|
||||||
"""
|
|
||||||
template_params = dict()
|
|
||||||
|
|
||||||
for mapping in self.param_mappings:
|
|
||||||
mapping.set_param(template_params, baymodel, bay)
|
|
||||||
|
|
||||||
if 'extra_params' in kwargs:
|
|
||||||
template_params.update(kwargs.get('extra_params'))
|
|
||||||
|
|
||||||
return template_params
|
|
||||||
|
|
||||||
def get_env_files(self, baymodel):
|
|
||||||
"""Collects stack environment files based upon Baymodel attributes.
|
|
||||||
|
|
||||||
Base implementation returns no files (empty list). Meant to be
|
|
||||||
overridden by subclasses.
|
|
||||||
|
|
||||||
:param baymodel: Baymodel to collect environment files for
|
|
||||||
|
|
||||||
:return: list of relative paths to environment files
|
|
||||||
"""
|
|
||||||
return []
|
|
||||||
|
|
||||||
def get_heat_param(self, bay_attr=None, baymodel_attr=None):
|
|
||||||
"""Returns stack param name.
|
|
||||||
|
|
||||||
Return stack param name using bay and baymodel attributes
|
|
||||||
:param bay_attr bay attribute from which it maps to stack attribute
|
|
||||||
:param baymodel_attr baymodel attribute from which it maps
|
|
||||||
to stack attribute
|
|
||||||
|
|
||||||
:return stack parameter name or None
|
|
||||||
"""
|
|
||||||
for mapping in self.param_mappings:
|
|
||||||
if (mapping.bay_attr == bay_attr and
|
|
||||||
mapping.baymodel_attr == baymodel_attr):
|
|
||||||
return mapping.heat_param
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def update_outputs(self, stack, baymodel, bay):
|
|
||||||
for output in self.output_mappings:
|
|
||||||
output.set_output(stack, baymodel, bay)
|
|
||||||
|
|
||||||
@abc.abstractproperty
|
|
||||||
def template_path(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def extract_definition(self, context, baymodel, bay, **kwargs):
|
|
||||||
return (self.template_path,
|
|
||||||
self.get_params(context, baymodel, bay, **kwargs),
|
|
||||||
self.get_env_files(baymodel))
|
|
||||||
|
|
||||||
|
|
||||||
class BaseTemplateDefinition(TemplateDefinition):
|
|
||||||
def __init__(self):
|
|
||||||
super(BaseTemplateDefinition, self).__init__()
|
|
||||||
self._osc = None
|
|
||||||
|
|
||||||
self.add_parameter('ssh_key_name',
|
|
||||||
baymodel_attr='keypair_id',
|
|
||||||
required=True)
|
|
||||||
self.add_parameter('server_image',
|
|
||||||
baymodel_attr='image_id')
|
|
||||||
self.add_parameter('dns_nameserver',
|
|
||||||
baymodel_attr='dns_nameserver')
|
|
||||||
self.add_parameter('http_proxy',
|
|
||||||
baymodel_attr='http_proxy')
|
|
||||||
self.add_parameter('https_proxy',
|
|
||||||
baymodel_attr='https_proxy')
|
|
||||||
self.add_parameter('no_proxy',
|
|
||||||
baymodel_attr='no_proxy')
|
|
||||||
self.add_parameter('number_of_masters',
|
|
||||||
bay_attr='master_count')
|
|
||||||
|
|
||||||
@abc.abstractproperty
|
|
||||||
def template_path(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_osc(self, context):
|
|
||||||
if not self._osc:
|
|
||||||
self._osc = clients.OpenStackClients(context)
|
|
||||||
return self._osc
|
|
||||||
|
|
||||||
def get_params(self, context, baymodel, bay, **kwargs):
|
|
||||||
osc = self.get_osc(context)
|
|
||||||
|
|
||||||
extra_params = kwargs.pop('extra_params', {})
|
|
||||||
extra_params['trustee_domain_id'] = osc.keystone().trustee_domain_id
|
|
||||||
extra_params['trustee_user_id'] = bay.trustee_user_id
|
|
||||||
extra_params['trustee_username'] = bay.trustee_username
|
|
||||||
extra_params['trustee_password'] = bay.trustee_password
|
|
||||||
extra_params['trust_id'] = bay.trust_id
|
|
||||||
extra_params['auth_url'] = context.auth_url
|
|
||||||
|
|
||||||
return super(BaseTemplateDefinition,
|
|
||||||
self).get_params(context, baymodel, bay,
|
|
||||||
extra_params=extra_params,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
def validate_discovery_url(self, discovery_url, expect_size):
|
|
||||||
url = str(discovery_url)
|
|
||||||
if url[len(url)-1] == '/':
|
|
||||||
url += '_config/size'
|
|
||||||
else:
|
|
||||||
url += '/_config/size'
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = requests.get(url).text
|
|
||||||
except req_exceptions.RequestException as err:
|
|
||||||
LOG.error(six.text_type(err))
|
|
||||||
raise exception.GetClusterSizeFailed(
|
|
||||||
discovery_url=discovery_url)
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = ast.literal_eval(result)
|
|
||||||
except (ValueError, SyntaxError):
|
|
||||||
raise exception.InvalidBayDiscoveryURL(
|
|
||||||
discovery_url=discovery_url)
|
|
||||||
|
|
||||||
node_value = result.get('node', None)
|
|
||||||
if node_value is None:
|
|
||||||
raise exception.InvalidBayDiscoveryURL(
|
|
||||||
discovery_url=discovery_url)
|
|
||||||
|
|
||||||
value = node_value.get('value', None)
|
|
||||||
if value is None:
|
|
||||||
raise exception.InvalidBayDiscoveryURL(
|
|
||||||
discovery_url=discovery_url)
|
|
||||||
elif int(value) != expect_size:
|
|
||||||
raise exception.InvalidClusterSize(
|
|
||||||
expect_size=expect_size,
|
|
||||||
size=int(value),
|
|
||||||
discovery_url=discovery_url)
|
|
||||||
|
|
||||||
def get_discovery_url(self, bay):
|
|
||||||
if hasattr(bay, 'discovery_url') and bay.discovery_url:
|
|
||||||
if getattr(bay, 'master_count', None) is not None:
|
|
||||||
self.validate_discovery_url(bay.discovery_url,
|
|
||||||
bay.master_count)
|
|
||||||
else:
|
|
||||||
self.validate_discovery_url(bay.discovery_url, 1)
|
|
||||||
discovery_url = bay.discovery_url
|
|
||||||
else:
|
|
||||||
discovery_endpoint = (
|
|
||||||
cfg.CONF.bay.etcd_discovery_service_endpoint_format %
|
|
||||||
{'size': bay.master_count})
|
|
||||||
try:
|
|
||||||
discovery_url = requests.get(discovery_endpoint).text
|
|
||||||
except req_exceptions.RequestException as err:
|
|
||||||
LOG.error(six.text_type(err))
|
|
||||||
raise exception.GetDiscoveryUrlFailed(
|
|
||||||
discovery_endpoint=discovery_endpoint)
|
|
||||||
if not discovery_url:
|
|
||||||
raise exception.InvalidDiscoveryURL(
|
|
||||||
discovery_url=discovery_url,
|
|
||||||
discovery_endpoint=discovery_endpoint)
|
|
||||||
else:
|
|
||||||
bay.discovery_url = discovery_url
|
|
||||||
return discovery_url
|
|
||||||
|
|
||||||
|
|
||||||
class K8sApiAddressOutputMapping(OutputMapping):
|
|
||||||
|
|
||||||
def set_output(self, stack, baymodel, bay):
|
def set_output(self, stack, baymodel, bay):
|
||||||
if self.bay_attr is None:
|
if self.bay_attr is None:
|
||||||
@ -482,28 +89,7 @@ class K8sApiAddressOutputMapping(OutputMapping):
|
|||||||
setattr(bay, self.bay_attr, value)
|
setattr(bay, self.bay_attr, value)
|
||||||
|
|
||||||
|
|
||||||
class SwarmApiAddressOutputMapping(OutputMapping):
|
class K8sTemplateDefinition(template_def.BaseTemplateDefinition):
|
||||||
|
|
||||||
def set_output(self, stack, baymodel, bay):
|
|
||||||
if self.bay_attr is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
output_value = self.get_output_value(stack)
|
|
||||||
if output_value is not None:
|
|
||||||
protocol = 'https'
|
|
||||||
if baymodel.tls_disabled:
|
|
||||||
protocol = 'tcp'
|
|
||||||
|
|
||||||
params = {
|
|
||||||
'protocol': protocol,
|
|
||||||
'address': output_value,
|
|
||||||
'port': DOCKER_PORT,
|
|
||||||
}
|
|
||||||
value = "%(protocol)s://%(address)s:%(port)s" % params
|
|
||||||
setattr(bay, self.bay_attr, value)
|
|
||||||
|
|
||||||
|
|
||||||
class K8sTemplateDefinition(BaseTemplateDefinition):
|
|
||||||
"""Base Kubernetes template."""
|
"""Base Kubernetes template."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -622,83 +208,7 @@ class CoreOSK8sTemplateDefinition(K8sTemplateDefinition):
|
|||||||
return cfg.CONF.bay.k8s_coreos_template_path
|
return cfg.CONF.bay.k8s_coreos_template_path
|
||||||
|
|
||||||
|
|
||||||
class AtomicSwarmTemplateDefinition(BaseTemplateDefinition):
|
class UbuntuMesosTemplateDefinition(template_def.BaseTemplateDefinition):
|
||||||
"""Docker swarm template for a Fedora Atomic VM."""
|
|
||||||
|
|
||||||
provides = [
|
|
||||||
{'server_type': 'vm', 'os': 'fedora-atomic', 'coe': 'swarm'},
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(AtomicSwarmTemplateDefinition, self).__init__()
|
|
||||||
self.add_parameter('bay_uuid',
|
|
||||||
bay_attr='uuid',
|
|
||||||
param_type=str)
|
|
||||||
self.add_parameter('number_of_nodes',
|
|
||||||
bay_attr='node_count')
|
|
||||||
self.add_parameter('master_flavor',
|
|
||||||
baymodel_attr='master_flavor_id')
|
|
||||||
self.add_parameter('node_flavor',
|
|
||||||
baymodel_attr='flavor_id')
|
|
||||||
self.add_parameter('docker_volume_size',
|
|
||||||
baymodel_attr='docker_volume_size')
|
|
||||||
self.add_parameter('external_network',
|
|
||||||
baymodel_attr='external_network_id',
|
|
||||||
required=True)
|
|
||||||
self.add_parameter('network_driver',
|
|
||||||
baymodel_attr='network_driver')
|
|
||||||
self.add_parameter('tls_disabled',
|
|
||||||
baymodel_attr='tls_disabled',
|
|
||||||
required=True)
|
|
||||||
self.add_parameter('registry_enabled',
|
|
||||||
baymodel_attr='registry_enabled')
|
|
||||||
self.add_parameter('docker_storage_driver',
|
|
||||||
baymodel_attr='docker_storage_driver')
|
|
||||||
self.add_output('api_address',
|
|
||||||
bay_attr='api_address',
|
|
||||||
mapping_type=SwarmApiAddressOutputMapping)
|
|
||||||
self.add_output('swarm_master_private',
|
|
||||||
bay_attr=None)
|
|
||||||
self.add_output('swarm_masters',
|
|
||||||
bay_attr='master_addresses')
|
|
||||||
self.add_output('swarm_nodes_private',
|
|
||||||
bay_attr=None)
|
|
||||||
self.add_output('swarm_nodes',
|
|
||||||
bay_attr='node_addresses')
|
|
||||||
self.add_output('discovery_url',
|
|
||||||
bay_attr='discovery_url')
|
|
||||||
|
|
||||||
def get_params(self, context, baymodel, bay, **kwargs):
|
|
||||||
extra_params = kwargs.pop('extra_params', {})
|
|
||||||
extra_params['discovery_url'] = self.get_discovery_url(bay)
|
|
||||||
# HACK(apmelton) - This uses the user's bearer token, ideally
|
|
||||||
# it should be replaced with an actual trust token with only
|
|
||||||
# access to do what the template needs it to do.
|
|
||||||
osc = self.get_osc(context)
|
|
||||||
extra_params['magnum_url'] = osc.magnum_url()
|
|
||||||
|
|
||||||
label_list = ['flannel_network_cidr', 'flannel_backend',
|
|
||||||
'flannel_network_subnetlen']
|
|
||||||
|
|
||||||
for label in label_list:
|
|
||||||
extra_params[label] = baymodel.labels.get(label)
|
|
||||||
|
|
||||||
if baymodel.registry_enabled:
|
|
||||||
extra_params['swift_region'] = CONF.docker_registry.swift_region
|
|
||||||
extra_params['registry_container'] = (
|
|
||||||
CONF.docker_registry.swift_registry_container)
|
|
||||||
|
|
||||||
return super(AtomicSwarmTemplateDefinition,
|
|
||||||
self).get_params(context, baymodel, bay,
|
|
||||||
extra_params=extra_params,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def template_path(self):
|
|
||||||
return cfg.CONF.bay.swarm_atomic_template_path
|
|
||||||
|
|
||||||
|
|
||||||
class UbuntuMesosTemplateDefinition(BaseTemplateDefinition):
|
|
||||||
"""Mesos template for Ubuntu VM."""
|
"""Mesos template for Ubuntu VM."""
|
||||||
|
|
||||||
provides = [
|
provides = [
|
||||||
|
0
magnum/drivers/__init__.py
Normal file
0
magnum/drivers/__init__.py
Normal file
0
magnum/drivers/common/__init__.py
Normal file
0
magnum/drivers/common/__init__.py
Normal file
452
magnum/drivers/common/template_def.py
Normal file
452
magnum/drivers/common/template_def.py
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
# 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.
|
||||||
|
import abc
|
||||||
|
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
|
||||||
|
|
||||||
|
from magnum.common import clients
|
||||||
|
from magnum.common import exception
|
||||||
|
from magnum.common import paths
|
||||||
|
from magnum.i18n import _
|
||||||
|
from magnum.i18n import _LW
|
||||||
|
|
||||||
|
from requests import exceptions as req_exceptions
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
template_def_opts = [
|
||||||
|
cfg.StrOpt('k8s_atomic_template_path',
|
||||||
|
default=paths.basedir_def('templates/kubernetes/'
|
||||||
|
'kubecluster.yaml'),
|
||||||
|
deprecated_name='template_path',
|
||||||
|
deprecated_group='bay_heat',
|
||||||
|
help=_(
|
||||||
|
'Location of template to build a k8s cluster on atomic.')),
|
||||||
|
cfg.StrOpt('k8s_coreos_template_path',
|
||||||
|
default=paths.basedir_def('templates/kubernetes/'
|
||||||
|
'kubecluster-coreos.yaml'),
|
||||||
|
help=_(
|
||||||
|
'Location of template to build a k8s cluster on CoreOS.')),
|
||||||
|
cfg.StrOpt('etcd_discovery_service_endpoint_format',
|
||||||
|
default='https://discovery.etcd.io/new?size=%(size)d',
|
||||||
|
help=_('Url for etcd public discovery endpoint.')),
|
||||||
|
cfg.StrOpt('mesos_ubuntu_template_path',
|
||||||
|
default=paths.basedir_def('templates/mesos/'
|
||||||
|
'mesoscluster.yaml'),
|
||||||
|
help=_('Location of template to build a Mesos cluster '
|
||||||
|
'on Ubuntu.')),
|
||||||
|
cfg.ListOpt('enabled_definitions',
|
||||||
|
default=['magnum_vm_atomic_k8s', 'magnum_vm_coreos_k8s',
|
||||||
|
'magnum_vm_atomic_swarm', 'magnum_vm_ubuntu_mesos'],
|
||||||
|
help=_('Enabled bay definition entry points.')),
|
||||||
|
]
|
||||||
|
|
||||||
|
docker_registry_opts = [
|
||||||
|
cfg.StrOpt('swift_region',
|
||||||
|
help=_('Region name of Swift')),
|
||||||
|
cfg.StrOpt('swift_registry_container',
|
||||||
|
default='docker_registry',
|
||||||
|
help=_('Name of the container in Swift which docker registry '
|
||||||
|
'stores images in'))
|
||||||
|
]
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
CONF.register_opts(template_def_opts, group='bay')
|
||||||
|
CONF.register_opts(docker_registry_opts, group='docker_registry')
|
||||||
|
CONF.import_opt('trustee_domain_id', 'magnum.common.keystone', group='trust')
|
||||||
|
|
||||||
|
|
||||||
|
class ParameterMapping(object):
|
||||||
|
"""A mapping associating heat param and bay/baymodel attr.
|
||||||
|
|
||||||
|
A ParameterMapping is an association of a Heat parameter name with
|
||||||
|
an attribute on a Bay, Baymodel, or both.
|
||||||
|
|
||||||
|
In the case of both baymodel_attr and bay_attr being set, the Baymodel
|
||||||
|
will be checked first and then Bay if the attribute isn't set on the
|
||||||
|
Baymodel.
|
||||||
|
|
||||||
|
Parameters can also be set as 'required'. If a required parameter
|
||||||
|
isn't set, a RequiredArgumentNotProvided exception will be raised.
|
||||||
|
"""
|
||||||
|
def __init__(self, heat_param, baymodel_attr=None,
|
||||||
|
bay_attr=None, required=False,
|
||||||
|
param_type=lambda x: x):
|
||||||
|
self.heat_param = heat_param
|
||||||
|
self.baymodel_attr = baymodel_attr
|
||||||
|
self.bay_attr = bay_attr
|
||||||
|
self.required = required
|
||||||
|
self.param_type = param_type
|
||||||
|
|
||||||
|
def set_param(self, params, baymodel, bay):
|
||||||
|
value = None
|
||||||
|
|
||||||
|
if (self.baymodel_attr and
|
||||||
|
getattr(baymodel, self.baymodel_attr, None) is not None):
|
||||||
|
value = getattr(baymodel, self.baymodel_attr)
|
||||||
|
elif (self.bay_attr and
|
||||||
|
getattr(bay, self.bay_attr, None) is not None):
|
||||||
|
value = getattr(bay, self.bay_attr)
|
||||||
|
elif self.required:
|
||||||
|
kwargs = dict(heat_param=self.heat_param)
|
||||||
|
raise exception.RequiredParameterNotProvided(**kwargs)
|
||||||
|
|
||||||
|
if value is not None:
|
||||||
|
value = self.param_type(value)
|
||||||
|
params[self.heat_param] = value
|
||||||
|
|
||||||
|
|
||||||
|
class OutputMapping(object):
|
||||||
|
"""A mapping associating heat outputs and bay attr.
|
||||||
|
|
||||||
|
An OutputMapping is an association of a Heat output with a key
|
||||||
|
Magnum understands.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, heat_output, bay_attr=None):
|
||||||
|
self.bay_attr = bay_attr
|
||||||
|
self.heat_output = heat_output
|
||||||
|
|
||||||
|
def set_output(self, stack, baymodel, bay):
|
||||||
|
if self.bay_attr is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
output_value = self.get_output_value(stack)
|
||||||
|
if output_value is not None:
|
||||||
|
setattr(bay, self.bay_attr, output_value)
|
||||||
|
|
||||||
|
def matched(self, output_key):
|
||||||
|
return self.heat_output == output_key
|
||||||
|
|
||||||
|
def get_output_value(self, stack):
|
||||||
|
for output in stack.to_dict().get('outputs', []):
|
||||||
|
if output['output_key'] == self.heat_output:
|
||||||
|
return output['output_value']
|
||||||
|
|
||||||
|
LOG.warning(_LW('stack does not have output_key %s'), self.heat_output)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
|
class TemplateDefinition(object):
|
||||||
|
'''A mapping between Magnum objects and Heat templates.
|
||||||
|
|
||||||
|
A TemplateDefinition is essentially a mapping between Magnum objects
|
||||||
|
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 bay 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 bay_type in def_class.provides:
|
||||||
|
bay_type_tuple = (bay_type['server_type'],
|
||||||
|
bay_type['os'],
|
||||||
|
bay_type['coe'])
|
||||||
|
providers = cls.definitions.setdefault(bay_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
|
||||||
|
bay_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 bay definition
|
||||||
|
will build on
|
||||||
|
:param os: The operation system the bay definition will build on
|
||||||
|
:param coe: The Container Orchestration Environment the bay will
|
||||||
|
produce
|
||||||
|
|
||||||
|
:return: class
|
||||||
|
'''
|
||||||
|
|
||||||
|
definition_map = cls.get_template_definitions()
|
||||||
|
bay_type = (server_type, os, coe)
|
||||||
|
|
||||||
|
if bay_type not in definition_map:
|
||||||
|
raise exception.BayTypeNotSupported(
|
||||||
|
server_type=server_type,
|
||||||
|
os=os,
|
||||||
|
coe=coe)
|
||||||
|
type_definitions = definition_map[bay_type]
|
||||||
|
|
||||||
|
for name in cfg.CONF.bay.enabled_definitions:
|
||||||
|
if name in type_definitions:
|
||||||
|
return type_definitions[name]()
|
||||||
|
|
||||||
|
raise exception.BayTypeNotEnabled(
|
||||||
|
server_type=server_type, os=os, coe=coe)
|
||||||
|
|
||||||
|
def add_parameter(self, *args, **kwargs):
|
||||||
|
param = ParameterMapping(*args, **kwargs)
|
||||||
|
self.param_mappings.append(param)
|
||||||
|
|
||||||
|
def add_output(self, *args, **kwargs):
|
||||||
|
mapping_type = kwargs.pop('mapping_type', OutputMapping)
|
||||||
|
output = mapping_type(*args, **kwargs)
|
||||||
|
self.output_mappings.append(output)
|
||||||
|
|
||||||
|
def get_output(self, *args, **kwargs):
|
||||||
|
for output in self.output_mappings:
|
||||||
|
if output.matched(*args, **kwargs):
|
||||||
|
return output
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_params(self, context, baymodel, bay, **kwargs):
|
||||||
|
"""Pulls template parameters from Baymodel and/or Bay.
|
||||||
|
|
||||||
|
:param context: Context to pull template parameters for
|
||||||
|
:param baymodel: Baymodel to pull template parameters from
|
||||||
|
:param bay: Bay to pull template parameters from
|
||||||
|
:param extra_params: Any extra params to be provided to the template
|
||||||
|
|
||||||
|
:return: dict of template parameters
|
||||||
|
"""
|
||||||
|
template_params = dict()
|
||||||
|
|
||||||
|
for mapping in self.param_mappings:
|
||||||
|
mapping.set_param(template_params, baymodel, bay)
|
||||||
|
|
||||||
|
if 'extra_params' in kwargs:
|
||||||
|
template_params.update(kwargs.get('extra_params'))
|
||||||
|
|
||||||
|
return template_params
|
||||||
|
|
||||||
|
def get_env_files(self, baymodel):
|
||||||
|
"""Collects stack environment files based upon Baymodel attributes.
|
||||||
|
|
||||||
|
Base implementation returns no files (empty list). Meant to be
|
||||||
|
overridden by subclasses.
|
||||||
|
|
||||||
|
:param baymodel: Baymodel to collect environment files for
|
||||||
|
|
||||||
|
:return: list of relative paths to environment files
|
||||||
|
"""
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_heat_param(self, bay_attr=None, baymodel_attr=None):
|
||||||
|
"""Returns stack param name.
|
||||||
|
|
||||||
|
Return stack param name using bay and baymodel attributes
|
||||||
|
:param bay_attr bay attribute from which it maps to stack attribute
|
||||||
|
:param baymodel_attr baymodel attribute from which it maps
|
||||||
|
to stack attribute
|
||||||
|
|
||||||
|
:return stack parameter name or None
|
||||||
|
"""
|
||||||
|
for mapping in self.param_mappings:
|
||||||
|
if (mapping.bay_attr == bay_attr and
|
||||||
|
mapping.baymodel_attr == baymodel_attr):
|
||||||
|
return mapping.heat_param
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update_outputs(self, stack, baymodel, bay):
|
||||||
|
for output in self.output_mappings:
|
||||||
|
output.set_output(stack, baymodel, bay)
|
||||||
|
|
||||||
|
@abc.abstractproperty
|
||||||
|
def template_path(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def extract_definition(self, context, baymodel, bay, **kwargs):
|
||||||
|
return (self.template_path,
|
||||||
|
self.get_params(context, baymodel, bay, **kwargs),
|
||||||
|
self.get_env_files(baymodel))
|
||||||
|
|
||||||
|
|
||||||
|
class BaseTemplateDefinition(TemplateDefinition):
|
||||||
|
def __init__(self):
|
||||||
|
super(BaseTemplateDefinition, self).__init__()
|
||||||
|
self._osc = None
|
||||||
|
|
||||||
|
self.add_parameter('ssh_key_name',
|
||||||
|
baymodel_attr='keypair_id',
|
||||||
|
required=True)
|
||||||
|
self.add_parameter('server_image',
|
||||||
|
baymodel_attr='image_id')
|
||||||
|
self.add_parameter('dns_nameserver',
|
||||||
|
baymodel_attr='dns_nameserver')
|
||||||
|
self.add_parameter('http_proxy',
|
||||||
|
baymodel_attr='http_proxy')
|
||||||
|
self.add_parameter('https_proxy',
|
||||||
|
baymodel_attr='https_proxy')
|
||||||
|
self.add_parameter('no_proxy',
|
||||||
|
baymodel_attr='no_proxy')
|
||||||
|
self.add_parameter('number_of_masters',
|
||||||
|
bay_attr='master_count')
|
||||||
|
|
||||||
|
@abc.abstractproperty
|
||||||
|
def template_path(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_osc(self, context):
|
||||||
|
if not self._osc:
|
||||||
|
self._osc = clients.OpenStackClients(context)
|
||||||
|
return self._osc
|
||||||
|
|
||||||
|
def get_params(self, context, baymodel, bay, **kwargs):
|
||||||
|
osc = self.get_osc(context)
|
||||||
|
|
||||||
|
extra_params = kwargs.pop('extra_params', {})
|
||||||
|
extra_params['trustee_domain_id'] = osc.keystone().trustee_domain_id
|
||||||
|
extra_params['trustee_user_id'] = bay.trustee_user_id
|
||||||
|
extra_params['trustee_username'] = bay.trustee_username
|
||||||
|
extra_params['trustee_password'] = bay.trustee_password
|
||||||
|
extra_params['trust_id'] = bay.trust_id
|
||||||
|
extra_params['auth_url'] = context.auth_url
|
||||||
|
|
||||||
|
return super(BaseTemplateDefinition,
|
||||||
|
self).get_params(context, baymodel, bay,
|
||||||
|
extra_params=extra_params,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
def validate_discovery_url(self, discovery_url, expect_size):
|
||||||
|
url = str(discovery_url)
|
||||||
|
if url[len(url)-1] == '/':
|
||||||
|
url += '_config/size'
|
||||||
|
else:
|
||||||
|
url += '/_config/size'
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = requests.get(url).text
|
||||||
|
except req_exceptions.RequestException as err:
|
||||||
|
LOG.error(six.text_type(err))
|
||||||
|
raise exception.GetClusterSizeFailed(
|
||||||
|
discovery_url=discovery_url)
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = ast.literal_eval(result)
|
||||||
|
except (ValueError, SyntaxError):
|
||||||
|
raise exception.InvalidBayDiscoveryURL(
|
||||||
|
discovery_url=discovery_url)
|
||||||
|
|
||||||
|
node_value = result.get('node', None)
|
||||||
|
if node_value is None:
|
||||||
|
raise exception.InvalidBayDiscoveryURL(
|
||||||
|
discovery_url=discovery_url)
|
||||||
|
|
||||||
|
value = node_value.get('value', None)
|
||||||
|
if value is None:
|
||||||
|
raise exception.InvalidBayDiscoveryURL(
|
||||||
|
discovery_url=discovery_url)
|
||||||
|
elif int(value) != expect_size:
|
||||||
|
raise exception.InvalidClusterSize(
|
||||||
|
expect_size=expect_size,
|
||||||
|
size=int(value),
|
||||||
|
discovery_url=discovery_url)
|
||||||
|
|
||||||
|
def get_discovery_url(self, bay):
|
||||||
|
if hasattr(bay, 'discovery_url') and bay.discovery_url:
|
||||||
|
if getattr(bay, 'master_count', None) is not None:
|
||||||
|
self.validate_discovery_url(bay.discovery_url,
|
||||||
|
bay.master_count)
|
||||||
|
else:
|
||||||
|
self.validate_discovery_url(bay.discovery_url, 1)
|
||||||
|
discovery_url = bay.discovery_url
|
||||||
|
else:
|
||||||
|
discovery_endpoint = (
|
||||||
|
cfg.CONF.bay.etcd_discovery_service_endpoint_format %
|
||||||
|
{'size': bay.master_count})
|
||||||
|
try:
|
||||||
|
discovery_url = requests.get(discovery_endpoint).text
|
||||||
|
except req_exceptions.RequestException as err:
|
||||||
|
LOG.error(six.text_type(err))
|
||||||
|
raise exception.GetDiscoveryUrlFailed(
|
||||||
|
discovery_endpoint=discovery_endpoint)
|
||||||
|
if not discovery_url:
|
||||||
|
raise exception.InvalidDiscoveryURL(
|
||||||
|
discovery_url=discovery_url,
|
||||||
|
discovery_endpoint=discovery_endpoint)
|
||||||
|
else:
|
||||||
|
bay.discovery_url = discovery_url
|
||||||
|
return discovery_url
|
0
magnum/drivers/swarm_fedora_atomic_v1/__init__.py
Normal file
0
magnum/drivers/swarm_fedora_atomic_v1/__init__.py
Normal file
118
magnum/drivers/swarm_fedora_atomic_v1/template_def.py
Normal file
118
magnum/drivers/swarm_fedora_atomic_v1/template_def.py
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
# 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.
|
||||||
|
import os
|
||||||
|
|
||||||
|
from magnum.drivers.common import template_def
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
DOCKER_PORT = '2376'
|
||||||
|
|
||||||
|
|
||||||
|
class SwarmApiAddressOutputMapping(template_def.OutputMapping):
|
||||||
|
|
||||||
|
def set_output(self, stack, baymodel, bay):
|
||||||
|
if self.bay_attr is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
output_value = self.get_output_value(stack)
|
||||||
|
if output_value is not None:
|
||||||
|
protocol = 'https'
|
||||||
|
if baymodel.tls_disabled:
|
||||||
|
protocol = 'tcp'
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'protocol': protocol,
|
||||||
|
'address': output_value,
|
||||||
|
'port': DOCKER_PORT,
|
||||||
|
}
|
||||||
|
value = "%(protocol)s://%(address)s:%(port)s" % params
|
||||||
|
setattr(bay, self.bay_attr, value)
|
||||||
|
|
||||||
|
|
||||||
|
class AtomicSwarmTemplateDefinition(template_def.BaseTemplateDefinition):
|
||||||
|
"""Docker swarm template for a Fedora Atomic VM."""
|
||||||
|
|
||||||
|
provides = [
|
||||||
|
{'server_type': 'vm', 'os': 'fedora-atomic', 'coe': 'swarm'},
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(AtomicSwarmTemplateDefinition, self).__init__()
|
||||||
|
self.add_parameter('bay_uuid',
|
||||||
|
bay_attr='uuid',
|
||||||
|
param_type=str)
|
||||||
|
self.add_parameter('number_of_nodes',
|
||||||
|
bay_attr='node_count')
|
||||||
|
self.add_parameter('master_flavor',
|
||||||
|
baymodel_attr='master_flavor_id')
|
||||||
|
self.add_parameter('node_flavor',
|
||||||
|
baymodel_attr='flavor_id')
|
||||||
|
self.add_parameter('docker_volume_size',
|
||||||
|
baymodel_attr='docker_volume_size')
|
||||||
|
self.add_parameter('external_network',
|
||||||
|
baymodel_attr='external_network_id',
|
||||||
|
required=True)
|
||||||
|
self.add_parameter('network_driver',
|
||||||
|
baymodel_attr='network_driver')
|
||||||
|
self.add_parameter('tls_disabled',
|
||||||
|
baymodel_attr='tls_disabled',
|
||||||
|
required=True)
|
||||||
|
self.add_parameter('registry_enabled',
|
||||||
|
baymodel_attr='registry_enabled')
|
||||||
|
self.add_parameter('docker_storage_driver',
|
||||||
|
baymodel_attr='docker_storage_driver')
|
||||||
|
self.add_output('api_address',
|
||||||
|
bay_attr='api_address',
|
||||||
|
mapping_type=SwarmApiAddressOutputMapping)
|
||||||
|
self.add_output('swarm_master_private',
|
||||||
|
bay_attr=None)
|
||||||
|
self.add_output('swarm_masters',
|
||||||
|
bay_attr='master_addresses')
|
||||||
|
self.add_output('swarm_nodes_private',
|
||||||
|
bay_attr=None)
|
||||||
|
self.add_output('swarm_nodes',
|
||||||
|
bay_attr='node_addresses')
|
||||||
|
self.add_output('discovery_url',
|
||||||
|
bay_attr='discovery_url')
|
||||||
|
|
||||||
|
def get_params(self, context, baymodel, bay, **kwargs):
|
||||||
|
extra_params = kwargs.pop('extra_params', {})
|
||||||
|
extra_params['discovery_url'] = self.get_discovery_url(bay)
|
||||||
|
# HACK(apmelton) - This uses the user's bearer token, ideally
|
||||||
|
# it should be replaced with an actual trust token with only
|
||||||
|
# access to do what the template needs it to do.
|
||||||
|
osc = self.get_osc(context)
|
||||||
|
extra_params['magnum_url'] = osc.magnum_url()
|
||||||
|
|
||||||
|
label_list = ['flannel_network_cidr', 'flannel_backend',
|
||||||
|
'flannel_network_subnetlen']
|
||||||
|
|
||||||
|
for label in label_list:
|
||||||
|
extra_params[label] = baymodel.labels.get(label)
|
||||||
|
|
||||||
|
if baymodel.registry_enabled:
|
||||||
|
extra_params['swift_region'] = CONF.docker_registry.swift_region
|
||||||
|
extra_params['registry_container'] = (
|
||||||
|
CONF.docker_registry.swift_registry_container)
|
||||||
|
|
||||||
|
return super(AtomicSwarmTemplateDefinition,
|
||||||
|
self).get_params(context, baymodel, bay,
|
||||||
|
extra_params=extra_params,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def template_path(self):
|
||||||
|
return os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||||
|
'templates/cluster.yaml')
|
@ -0,0 +1,38 @@
|
|||||||
|
# This file contains docker storage drivers configuration for fedora
|
||||||
|
# atomic hosts. Currently, devicemapper and overlay are supported.
|
||||||
|
|
||||||
|
# Remove any existing docker-storage configuration. In case of an
|
||||||
|
# existing configuration, docker-storage-setup will fail.
|
||||||
|
clear_docker_storage_congiguration () {
|
||||||
|
if [ -f /etc/sysconfig/docker-storage ]; then
|
||||||
|
sed -i "/^DOCKER_STORAGE_OPTIONS=/ s/=.*/=/" /etc/sysconfig/docker-storage
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configure docker storage with xfs as backing filesystem.
|
||||||
|
configure_overlay () {
|
||||||
|
clear_docker_storage_congiguration
|
||||||
|
|
||||||
|
rm -rf /var/lib/docker/*
|
||||||
|
|
||||||
|
mkfs.xfs ${device_path}
|
||||||
|
echo "${device_path} /var/lib/docker xfs defaults 0 0" >> /etc/fstab
|
||||||
|
mount -a
|
||||||
|
|
||||||
|
echo "STORAGE_DRIVER=overlay" > /etc/sysconfig/docker-storage-setup
|
||||||
|
|
||||||
|
# SELinux must be enabled and in enforcing mode on the physical
|
||||||
|
# machine, but must be disabled in the container when performing
|
||||||
|
# container separation
|
||||||
|
sed -i "/^OPTIONS=/ s/--selinux-enabled/--selinux-enabled=false/" /etc/sysconfig/docker
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configure docker storage with devicemapper using direct LVM
|
||||||
|
configure_devicemapper () {
|
||||||
|
clear_docker_storage_congiguration
|
||||||
|
|
||||||
|
pvcreate ${device_path}
|
||||||
|
vgcreate docker ${device_path}
|
||||||
|
|
||||||
|
echo "VG=docker" > /etc/sysconfig/docker-storage-setup
|
||||||
|
}
|
@ -237,7 +237,7 @@ resources:
|
|||||||
config:
|
config:
|
||||||
str_replace:
|
str_replace:
|
||||||
params:
|
params:
|
||||||
$configure_docker_storage_driver: {get_file: ../common/fragments/configure_docker_storage_driver_atomic.sh}
|
$configure_docker_storage_driver: {get_file: fragments/configure_docker_storage_driver_atomic.sh}
|
||||||
template: {get_file: fragments/configure-docker-storage.sh}
|
template: {get_file: fragments/configure-docker-storage.sh}
|
||||||
|
|
||||||
make_cert:
|
make_cert:
|
||||||
@ -423,4 +423,3 @@ outputs:
|
|||||||
value: {get_attr: [swarm_master_floating, floating_ip_address]}
|
value: {get_attr: [swarm_master_floating, floating_ip_address]}
|
||||||
description: >
|
description: >
|
||||||
This is the "public" ip addresses of Swarm master.
|
This is the "public" ip addresses of Swarm master.
|
||||||
|
|
@ -237,7 +237,7 @@ resources:
|
|||||||
config:
|
config:
|
||||||
str_replace:
|
str_replace:
|
||||||
params:
|
params:
|
||||||
$configure_docker_storage_driver: {get_file: ../common/fragments/configure_docker_storage_driver_atomic.sh}
|
$configure_docker_storage_driver: {get_file: fragments/configure_docker_storage_driver_atomic.sh}
|
||||||
template: {get_file: fragments/configure-docker-storage.sh}
|
template: {get_file: fragments/configure-docker-storage.sh}
|
||||||
|
|
||||||
configure_docker_registry:
|
configure_docker_registry:
|
16
magnum/drivers/swarm_fedora_atomic_v1/version.py
Normal file
16
magnum/drivers/swarm_fedora_atomic_v1/version.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# 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 = 'swarm_atomic'
|
@ -11,7 +11,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
from magnum.conductor import template_definition as tdef
|
from magnum.drivers.common import template_def as tdef
|
||||||
from magnum.tests import base
|
from magnum.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,22 +14,25 @@
|
|||||||
|
|
||||||
import mock
|
import mock
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from requests import exceptions as req_exceptions
|
|
||||||
|
|
||||||
from magnum.common import exception
|
from magnum.common import exception
|
||||||
from magnum.conductor import template_definition as tdef
|
from magnum.conductor import template_definition as tdef
|
||||||
|
from magnum.drivers.common import template_def as cmn_tdef
|
||||||
|
from magnum.drivers.swarm_fedora_atomic_v1 import template_def as swarm_tdef
|
||||||
from magnum.tests import base
|
from magnum.tests import base
|
||||||
|
|
||||||
|
from requests import exceptions as req_exceptions
|
||||||
|
|
||||||
|
|
||||||
class TemplateDefinitionTestCase(base.TestCase):
|
class TemplateDefinitionTestCase(base.TestCase):
|
||||||
|
|
||||||
@mock.patch.object(tdef, 'iter_entry_points')
|
@mock.patch.object(cmn_tdef, 'iter_entry_points')
|
||||||
def test_load_entry_points(self, mock_iter_entry_points):
|
def test_load_entry_points(self, mock_iter_entry_points):
|
||||||
mock_entry_point = mock.MagicMock()
|
mock_entry_point = mock.MagicMock()
|
||||||
mock_entry_points = [mock_entry_point]
|
mock_entry_points = [mock_entry_point]
|
||||||
mock_iter_entry_points.return_value = mock_entry_points.__iter__()
|
mock_iter_entry_points.return_value = mock_entry_points.__iter__()
|
||||||
|
|
||||||
entry_points = tdef.TemplateDefinition.load_entry_points()
|
entry_points = cmn_tdef.TemplateDefinition.load_entry_points()
|
||||||
|
|
||||||
for (expected_entry_point,
|
for (expected_entry_point,
|
||||||
(actual_entry_point, loaded_cls)) in zip(mock_entry_points,
|
(actual_entry_point, loaded_cls)) in zip(mock_entry_points,
|
||||||
@ -38,7 +41,7 @@ class TemplateDefinitionTestCase(base.TestCase):
|
|||||||
expected_entry_point.load.assert_called_once_with(require=False)
|
expected_entry_point.load.assert_called_once_with(require=False)
|
||||||
|
|
||||||
def test_get_template_definitions(self):
|
def test_get_template_definitions(self):
|
||||||
defs = tdef.TemplateDefinition.get_template_definitions()
|
defs = cmn_tdef.TemplateDefinition.get_template_definitions()
|
||||||
|
|
||||||
vm_atomic_k8s = defs[('vm', 'fedora-atomic', 'kubernetes')]
|
vm_atomic_k8s = defs[('vm', 'fedora-atomic', 'kubernetes')]
|
||||||
vm_coreos_k8s = defs[('vm', 'coreos', 'kubernetes')]
|
vm_coreos_k8s = defs[('vm', 'coreos', 'kubernetes')]
|
||||||
@ -51,7 +54,7 @@ class TemplateDefinitionTestCase(base.TestCase):
|
|||||||
vm_coreos_k8s['magnum_vm_coreos_k8s'])
|
vm_coreos_k8s['magnum_vm_coreos_k8s'])
|
||||||
|
|
||||||
def test_get_vm_atomic_kubernetes_definition(self):
|
def test_get_vm_atomic_kubernetes_definition(self):
|
||||||
definition = tdef.TemplateDefinition.get_template_definition(
|
definition = cmn_tdef.TemplateDefinition.get_template_definition(
|
||||||
'vm',
|
'vm',
|
||||||
'fedora-atomic',
|
'fedora-atomic',
|
||||||
'kubernetes')
|
'kubernetes')
|
||||||
@ -60,7 +63,7 @@ class TemplateDefinitionTestCase(base.TestCase):
|
|||||||
tdef.AtomicK8sTemplateDefinition)
|
tdef.AtomicK8sTemplateDefinition)
|
||||||
|
|
||||||
def test_get_vm_coreos_kubernetes_definition(self):
|
def test_get_vm_coreos_kubernetes_definition(self):
|
||||||
definition = tdef.TemplateDefinition.get_template_definition(
|
definition = cmn_tdef.TemplateDefinition.get_template_definition(
|
||||||
'vm',
|
'vm',
|
||||||
'coreos',
|
'coreos',
|
||||||
'kubernetes')
|
'kubernetes')
|
||||||
@ -69,16 +72,16 @@ class TemplateDefinitionTestCase(base.TestCase):
|
|||||||
tdef.CoreOSK8sTemplateDefinition)
|
tdef.CoreOSK8sTemplateDefinition)
|
||||||
|
|
||||||
def test_get_vm_atomic_swarm_definition(self):
|
def test_get_vm_atomic_swarm_definition(self):
|
||||||
definition = tdef.TemplateDefinition.get_template_definition(
|
definition = cmn_tdef.TemplateDefinition.get_template_definition(
|
||||||
'vm',
|
'vm',
|
||||||
'fedora-atomic',
|
'fedora-atomic',
|
||||||
'swarm')
|
'swarm')
|
||||||
|
|
||||||
self.assertIsInstance(definition,
|
self.assertIsInstance(definition,
|
||||||
tdef.AtomicSwarmTemplateDefinition)
|
swarm_tdef.AtomicSwarmTemplateDefinition)
|
||||||
|
|
||||||
def test_get_vm_ubuntu_mesos_definition(self):
|
def test_get_vm_ubuntu_mesos_definition(self):
|
||||||
definition = tdef.TemplateDefinition.get_template_definition(
|
definition = cmn_tdef.TemplateDefinition.get_template_definition(
|
||||||
'vm',
|
'vm',
|
||||||
'ubuntu',
|
'ubuntu',
|
||||||
'mesos')
|
'mesos')
|
||||||
@ -88,7 +91,7 @@ class TemplateDefinitionTestCase(base.TestCase):
|
|||||||
|
|
||||||
def test_get_definition_not_supported(self):
|
def test_get_definition_not_supported(self):
|
||||||
self.assertRaises(exception.BayTypeNotSupported,
|
self.assertRaises(exception.BayTypeNotSupported,
|
||||||
tdef.TemplateDefinition.get_template_definition,
|
cmn_tdef.TemplateDefinition.get_template_definition,
|
||||||
'vm', 'not_supported', 'kubernetes')
|
'vm', 'not_supported', 'kubernetes')
|
||||||
|
|
||||||
def test_get_definition_not_enabled(self):
|
def test_get_definition_not_enabled(self):
|
||||||
@ -96,11 +99,11 @@ class TemplateDefinitionTestCase(base.TestCase):
|
|||||||
['magnum_vm_atomic_k8s'],
|
['magnum_vm_atomic_k8s'],
|
||||||
group='bay')
|
group='bay')
|
||||||
self.assertRaises(exception.BayTypeNotEnabled,
|
self.assertRaises(exception.BayTypeNotEnabled,
|
||||||
tdef.TemplateDefinition.get_template_definition,
|
cmn_tdef.TemplateDefinition.get_template_definition,
|
||||||
'vm', 'coreos', 'kubernetes')
|
'vm', 'coreos', 'kubernetes')
|
||||||
|
|
||||||
def test_required_param_not_set(self):
|
def test_required_param_not_set(self):
|
||||||
param = tdef.ParameterMapping('test', baymodel_attr='test',
|
param = cmn_tdef.ParameterMapping('test', baymodel_attr='test',
|
||||||
required=True)
|
required=True)
|
||||||
mock_baymodel = mock.MagicMock()
|
mock_baymodel = mock.MagicMock()
|
||||||
mock_baymodel.test = None
|
mock_baymodel.test = None
|
||||||
@ -125,26 +128,26 @@ class TemplateDefinitionTestCase(base.TestCase):
|
|||||||
mock_stack = mock.MagicMock()
|
mock_stack = mock.MagicMock()
|
||||||
mock_stack.to_dict.return_value = {'outputs': heat_outputs}
|
mock_stack.to_dict.return_value = {'outputs': heat_outputs}
|
||||||
|
|
||||||
output = tdef.OutputMapping('key1')
|
output = cmn_tdef.OutputMapping('key1')
|
||||||
value = output.get_output_value(mock_stack)
|
value = output.get_output_value(mock_stack)
|
||||||
self.assertEqual('value1', value)
|
self.assertEqual('value1', value)
|
||||||
|
|
||||||
output = tdef.OutputMapping('key2')
|
output = cmn_tdef.OutputMapping('key2')
|
||||||
value = output.get_output_value(mock_stack)
|
value = output.get_output_value(mock_stack)
|
||||||
self.assertEqual(["value2", "value3"], value)
|
self.assertEqual(["value2", "value3"], value)
|
||||||
|
|
||||||
output = tdef.OutputMapping('key3')
|
output = cmn_tdef.OutputMapping('key3')
|
||||||
value = output.get_output_value(mock_stack)
|
value = output.get_output_value(mock_stack)
|
||||||
self.assertIsNone(value)
|
self.assertIsNone(value)
|
||||||
|
|
||||||
# verify stack with no 'outputs' attribute
|
# verify stack with no 'outputs' attribute
|
||||||
mock_stack.to_dict.return_value = {}
|
mock_stack.to_dict.return_value = {}
|
||||||
output = tdef.OutputMapping('key1')
|
output = cmn_tdef.OutputMapping('key1')
|
||||||
value = output.get_output_value(mock_stack)
|
value = output.get_output_value(mock_stack)
|
||||||
self.assertIsNone(value)
|
self.assertIsNone(value)
|
||||||
|
|
||||||
def test_add_output_with_mapping_type(self):
|
def test_add_output_with_mapping_type(self):
|
||||||
definition = tdef.TemplateDefinition.get_template_definition(
|
definition = cmn_tdef.TemplateDefinition.get_template_definition(
|
||||||
'vm',
|
'vm',
|
||||||
'fedora-atomic',
|
'fedora-atomic',
|
||||||
'kubernetes')
|
'kubernetes')
|
||||||
@ -166,9 +169,9 @@ class AtomicK8sTemplateDefinitionTestCase(base.TestCase):
|
|||||||
@mock.patch('magnum.common.clients.OpenStackClients')
|
@mock.patch('magnum.common.clients.OpenStackClients')
|
||||||
@mock.patch('magnum.conductor.template_definition'
|
@mock.patch('magnum.conductor.template_definition'
|
||||||
'.AtomicK8sTemplateDefinition.get_discovery_url')
|
'.AtomicK8sTemplateDefinition.get_discovery_url')
|
||||||
@mock.patch('magnum.conductor.template_definition.BaseTemplateDefinition'
|
@mock.patch('magnum.drivers.common.template_def.BaseTemplateDefinition'
|
||||||
'.get_params')
|
'.get_params')
|
||||||
@mock.patch('magnum.conductor.template_definition.TemplateDefinition'
|
@mock.patch('magnum.drivers.common.template_def.TemplateDefinition'
|
||||||
'.get_output')
|
'.get_output')
|
||||||
def test_k8s_get_params(self, mock_get_output, mock_get_params,
|
def test_k8s_get_params(self, mock_get_output, mock_get_params,
|
||||||
mock_get_discovery_url, mock_osc_class):
|
mock_get_discovery_url, mock_osc_class):
|
||||||
@ -219,9 +222,9 @@ class AtomicK8sTemplateDefinitionTestCase(base.TestCase):
|
|||||||
@mock.patch('magnum.common.clients.OpenStackClients')
|
@mock.patch('magnum.common.clients.OpenStackClients')
|
||||||
@mock.patch('magnum.conductor.template_definition'
|
@mock.patch('magnum.conductor.template_definition'
|
||||||
'.AtomicK8sTemplateDefinition.get_discovery_url')
|
'.AtomicK8sTemplateDefinition.get_discovery_url')
|
||||||
@mock.patch('magnum.conductor.template_definition.BaseTemplateDefinition'
|
@mock.patch('magnum.drivers.common.template_def.BaseTemplateDefinition'
|
||||||
'.get_params')
|
'.get_params')
|
||||||
@mock.patch('magnum.conductor.template_definition.TemplateDefinition'
|
@mock.patch('magnum.drivers.common.template_def.TemplateDefinition'
|
||||||
'.get_output')
|
'.get_output')
|
||||||
def test_k8s_get_params_insecure(self, mock_get_output, mock_get_params,
|
def test_k8s_get_params_insecure(self, mock_get_output, mock_get_params,
|
||||||
mock_get_discovery_url, mock_osc_class):
|
mock_get_discovery_url, mock_osc_class):
|
||||||
@ -371,7 +374,7 @@ class AtomicK8sTemplateDefinitionTestCase(base.TestCase):
|
|||||||
|
|
||||||
def _test_update_outputs_api_address(self, coe, params, tls=True):
|
def _test_update_outputs_api_address(self, coe, params, tls=True):
|
||||||
|
|
||||||
definition = tdef.TemplateDefinition.get_template_definition(
|
definition = cmn_tdef.TemplateDefinition.get_template_definition(
|
||||||
'vm',
|
'vm',
|
||||||
'fedora-atomic',
|
'fedora-atomic',
|
||||||
coe)
|
coe)
|
||||||
@ -438,7 +441,7 @@ class AtomicK8sTemplateDefinitionTestCase(base.TestCase):
|
|||||||
|
|
||||||
def _test_update_outputs_none_api_address(self, coe, params, tls=True):
|
def _test_update_outputs_none_api_address(self, coe, params, tls=True):
|
||||||
|
|
||||||
definition = tdef.TemplateDefinition.get_template_definition(
|
definition = cmn_tdef.TemplateDefinition.get_template_definition(
|
||||||
'vm',
|
'vm',
|
||||||
'fedora-atomic',
|
'fedora-atomic',
|
||||||
coe)
|
coe)
|
||||||
@ -483,11 +486,11 @@ class AtomicK8sTemplateDefinitionTestCase(base.TestCase):
|
|||||||
class AtomicSwarmTemplateDefinitionTestCase(base.TestCase):
|
class AtomicSwarmTemplateDefinitionTestCase(base.TestCase):
|
||||||
|
|
||||||
@mock.patch('magnum.common.clients.OpenStackClients')
|
@mock.patch('magnum.common.clients.OpenStackClients')
|
||||||
@mock.patch('magnum.conductor.template_definition'
|
@mock.patch('magnum.drivers.swarm_fedora_atomic_v1.template_def'
|
||||||
'.AtomicSwarmTemplateDefinition.get_discovery_url')
|
'.AtomicSwarmTemplateDefinition.get_discovery_url')
|
||||||
@mock.patch('magnum.conductor.template_definition.BaseTemplateDefinition'
|
@mock.patch('magnum.drivers.common.template_def.BaseTemplateDefinition'
|
||||||
'.get_params')
|
'.get_params')
|
||||||
@mock.patch('magnum.conductor.template_definition.TemplateDefinition'
|
@mock.patch('magnum.drivers.common.template_def.TemplateDefinition'
|
||||||
'.get_output')
|
'.get_output')
|
||||||
def test_swarm_get_params(self, mock_get_output, mock_get_params,
|
def test_swarm_get_params(self, mock_get_output, mock_get_params,
|
||||||
mock_get_discovery_url, mock_osc_class):
|
mock_get_discovery_url, mock_osc_class):
|
||||||
@ -513,7 +516,7 @@ class AtomicSwarmTemplateDefinitionTestCase(base.TestCase):
|
|||||||
flannel_subnet = mock_baymodel.labels.get('flannel_network_subnetlen')
|
flannel_subnet = mock_baymodel.labels.get('flannel_network_subnetlen')
|
||||||
flannel_backend = mock_baymodel.labels.get('flannel_backend')
|
flannel_backend = mock_baymodel.labels.get('flannel_backend')
|
||||||
|
|
||||||
swarm_def = tdef.AtomicSwarmTemplateDefinition()
|
swarm_def = swarm_tdef.AtomicSwarmTemplateDefinition()
|
||||||
|
|
||||||
swarm_def.get_params(mock_context, mock_baymodel, mock_bay)
|
swarm_def.get_params(mock_context, mock_baymodel, mock_bay)
|
||||||
|
|
||||||
@ -582,7 +585,7 @@ class AtomicSwarmTemplateDefinitionTestCase(base.TestCase):
|
|||||||
mock_bay = mock.MagicMock()
|
mock_bay = mock.MagicMock()
|
||||||
mock_bay.discovery_url = None
|
mock_bay.discovery_url = None
|
||||||
|
|
||||||
swarm_def = tdef.AtomicSwarmTemplateDefinition()
|
swarm_def = swarm_tdef.AtomicSwarmTemplateDefinition()
|
||||||
discovery_url = swarm_def.get_discovery_url(mock_bay)
|
discovery_url = swarm_def.get_discovery_url(mock_bay)
|
||||||
|
|
||||||
mock_get.assert_called_once_with('http://etcd/test?size=1')
|
mock_get.assert_called_once_with('http://etcd/test?size=1')
|
||||||
@ -603,13 +606,13 @@ class AtomicSwarmTemplateDefinitionTestCase(base.TestCase):
|
|||||||
fake_bay)
|
fake_bay)
|
||||||
|
|
||||||
def test_swarm_get_heat_param(self):
|
def test_swarm_get_heat_param(self):
|
||||||
swarm_def = tdef.AtomicSwarmTemplateDefinition()
|
swarm_def = swarm_tdef.AtomicSwarmTemplateDefinition()
|
||||||
|
|
||||||
heat_param = swarm_def.get_heat_param(bay_attr='node_count')
|
heat_param = swarm_def.get_heat_param(bay_attr='node_count')
|
||||||
self.assertEqual('number_of_nodes', heat_param)
|
self.assertEqual('number_of_nodes', heat_param)
|
||||||
|
|
||||||
def test_update_outputs(self):
|
def test_update_outputs(self):
|
||||||
swarm_def = tdef.AtomicSwarmTemplateDefinition()
|
swarm_def = swarm_tdef.AtomicSwarmTemplateDefinition()
|
||||||
|
|
||||||
expected_api_address = 'updated_address'
|
expected_api_address = 'updated_address'
|
||||||
expected_node_addresses = ['ex_minion', 'address']
|
expected_node_addresses = ['ex_minion', 'address']
|
||||||
@ -645,9 +648,9 @@ class AtomicSwarmTemplateDefinitionTestCase(base.TestCase):
|
|||||||
class UbuntuMesosTemplateDefinitionTestCase(base.TestCase):
|
class UbuntuMesosTemplateDefinitionTestCase(base.TestCase):
|
||||||
|
|
||||||
@mock.patch('magnum.common.clients.OpenStackClients')
|
@mock.patch('magnum.common.clients.OpenStackClients')
|
||||||
@mock.patch('magnum.conductor.template_definition.BaseTemplateDefinition'
|
@mock.patch('magnum.drivers.common.template_def.BaseTemplateDefinition'
|
||||||
'.get_params')
|
'.get_params')
|
||||||
@mock.patch('magnum.conductor.template_definition.TemplateDefinition'
|
@mock.patch('magnum.drivers.common.template_def.TemplateDefinition'
|
||||||
'.get_output')
|
'.get_output')
|
||||||
def test_mesos_get_params(self, mock_get_output, mock_get_params,
|
def test_mesos_get_params(self, mock_get_output, mock_get_params,
|
||||||
mock_osc_class):
|
mock_osc_class):
|
||||||
|
@ -58,7 +58,7 @@ oslo.config.opts.defaults =
|
|||||||
magnum.template_definitions =
|
magnum.template_definitions =
|
||||||
magnum_vm_atomic_k8s = magnum.conductor.template_definition:AtomicK8sTemplateDefinition
|
magnum_vm_atomic_k8s = magnum.conductor.template_definition:AtomicK8sTemplateDefinition
|
||||||
magnum_vm_coreos_k8s = magnum.conductor.template_definition:CoreOSK8sTemplateDefinition
|
magnum_vm_coreos_k8s = magnum.conductor.template_definition:CoreOSK8sTemplateDefinition
|
||||||
magnum_vm_atomic_swarm = magnum.conductor.template_definition:AtomicSwarmTemplateDefinition
|
magnum_vm_atomic_swarm = magnum.drivers.swarm_fedora_atomic_v1.template_def:AtomicSwarmTemplateDefinition
|
||||||
magnum_vm_ubuntu_mesos = magnum.conductor.template_definition:UbuntuMesosTemplateDefinition
|
magnum_vm_ubuntu_mesos = magnum.conductor.template_definition:UbuntuMesosTemplateDefinition
|
||||||
|
|
||||||
magnum.database.migration_backend =
|
magnum.database.migration_backend =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user