Add magnum cluster templates resource
Implement support for magnum clustertemplate. - drop support for bays. Those were never tested properly in SDK and are deprecated since Newton. Change-Id: I8a7198231fd60abf5ac2dd44985961c8c47db657
This commit is contained in:
parent
289e5c2d3c
commit
b66c6cc847
@ -201,22 +201,8 @@ class CoeCloudMixin:
|
||||
:raises: ``OpenStackCloudException``: if something goes wrong during
|
||||
the OpenStack API call.
|
||||
"""
|
||||
with _utils.shade_exceptions("Error fetching cluster template list"):
|
||||
try:
|
||||
data = self._container_infra_client.get('/clustertemplates')
|
||||
# NOTE(flwang): Magnum adds /clustertemplates and /cluster
|
||||
# to deprecate /baymodels and /bay since Newton release. So
|
||||
# we're using a small tag to indicate if current
|
||||
# cloud has those two new API endpoints.
|
||||
self._container_infra_client._has_magnum_after_newton = True
|
||||
return self._normalize_cluster_templates(
|
||||
self._get_and_munchify('clustertemplates', data))
|
||||
except exc.OpenStackCloudURINotFound:
|
||||
data = self._container_infra_client.get('/baymodels/detail')
|
||||
return self._normalize_cluster_templates(
|
||||
self._get_and_munchify('baymodels', data))
|
||||
list_baymodels = list_cluster_templates
|
||||
list_coe_cluster_templates = list_cluster_templates
|
||||
return list(
|
||||
self.container_infrastructure_management.cluster_templates())
|
||||
|
||||
def search_cluster_templates(
|
||||
self, name_or_id=None, filters=None, detail=False):
|
||||
@ -235,8 +221,6 @@ class CoeCloudMixin:
|
||||
cluster_templates = self.list_cluster_templates(detail=detail)
|
||||
return _utils._filter_list(
|
||||
cluster_templates, name_or_id, filters)
|
||||
search_baymodels = search_cluster_templates
|
||||
search_coe_cluster_templates = search_cluster_templates
|
||||
|
||||
def get_cluster_template(self, name_or_id, filters=None, detail=False):
|
||||
"""Get a cluster template by name or ID.
|
||||
@ -260,10 +244,9 @@ class CoeCloudMixin:
|
||||
:returns: A cluster template dict or None if no matching
|
||||
cluster template is found.
|
||||
"""
|
||||
return _utils._get_entity(self, 'cluster_template', name_or_id,
|
||||
filters=filters, detail=detail)
|
||||
get_baymodel = get_cluster_template
|
||||
get_coe_cluster_template = get_cluster_template
|
||||
return _utils._get_entity(
|
||||
self, 'cluster_template', name_or_id,
|
||||
filters=filters, detail=detail)
|
||||
|
||||
def create_cluster_template(
|
||||
self, name, image_id=None, keypair_id=None, coe=None, **kwargs):
|
||||
@ -280,28 +263,16 @@ class CoeCloudMixin:
|
||||
:raises: ``OpenStackCloudException`` if something goes wrong during
|
||||
the OpenStack API call
|
||||
"""
|
||||
error_message = ("Error creating cluster template of name"
|
||||
" {cluster_template_name}".format(
|
||||
cluster_template_name=name))
|
||||
with _utils.shade_exceptions(error_message):
|
||||
body = kwargs.copy()
|
||||
body['name'] = name
|
||||
body['image_id'] = image_id
|
||||
body['keypair_id'] = keypair_id
|
||||
body['coe'] = coe
|
||||
cluster_template = self.container_infrastructure_management \
|
||||
.create_cluster_template(
|
||||
name=name,
|
||||
image_id=image_id,
|
||||
keypair_id=keypair_id,
|
||||
coe=coe,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
try:
|
||||
cluster_template = self._container_infra_client.post(
|
||||
'/clustertemplates', json=body)
|
||||
self._container_infra_client._has_magnum_after_newton = True
|
||||
except exc.OpenStackCloudURINotFound:
|
||||
cluster_template = self._container_infra_client.post(
|
||||
'/baymodels', json=body)
|
||||
|
||||
self.list_cluster_templates.invalidate(self)
|
||||
return self._normalize_cluster_template(cluster_template)
|
||||
create_baymodel = create_cluster_template
|
||||
create_coe_cluster_template = create_cluster_template
|
||||
return cluster_template
|
||||
|
||||
def delete_cluster_template(self, name_or_id):
|
||||
"""Delete a cluster template.
|
||||
@ -322,68 +293,31 @@ class CoeCloudMixin:
|
||||
exc_info=True)
|
||||
return False
|
||||
|
||||
with _utils.shade_exceptions("Error in deleting cluster template"):
|
||||
if getattr(self._container_infra_client,
|
||||
'_has_magnum_after_newton', False):
|
||||
self._container_infra_client.delete(
|
||||
'/clustertemplates/{id}'.format(id=cluster_template['id']))
|
||||
else:
|
||||
self._container_infra_client.delete(
|
||||
'/baymodels/{id}'.format(id=cluster_template['id']))
|
||||
self.list_cluster_templates.invalidate(self)
|
||||
|
||||
self.container_infrastructure_management.delete_cluster_template(
|
||||
cluster_template)
|
||||
return True
|
||||
delete_baymodel = delete_cluster_template
|
||||
delete_coe_cluster_template = delete_cluster_template
|
||||
|
||||
@_utils.valid_kwargs('name', 'image_id', 'flavor_id', 'master_flavor_id',
|
||||
'keypair_id', 'external_network_id', 'fixed_network',
|
||||
'dns_nameserver', 'docker_volume_size', 'labels',
|
||||
'coe', 'http_proxy', 'https_proxy', 'no_proxy',
|
||||
'network_driver', 'tls_disabled', 'public',
|
||||
'registry_enabled', 'volume_driver')
|
||||
def update_cluster_template(self, name_or_id, operation, **kwargs):
|
||||
def update_cluster_template(self, name_or_id, **kwargs):
|
||||
"""Update a cluster template.
|
||||
|
||||
:param name_or_id: Name or ID of the cluster template being updated.
|
||||
:param operation: Operation to perform - add, remove, replace.
|
||||
Other arguments will be passed with kwargs.
|
||||
|
||||
:returns: a dict representing the updated cluster template.
|
||||
:returns: an update cluster template.
|
||||
|
||||
:raises: OpenStackCloudException on operation error.
|
||||
"""
|
||||
self.list_cluster_templates.invalidate(self)
|
||||
cluster_template = self.get_cluster_template(name_or_id)
|
||||
if not cluster_template:
|
||||
raise exc.OpenStackCloudException(
|
||||
"Cluster template %s not found." % name_or_id)
|
||||
|
||||
if operation not in ['add', 'replace', 'remove']:
|
||||
raise TypeError(
|
||||
"%s operation not in 'add', 'replace', 'remove'" % operation)
|
||||
cluster_template = self.container_infrastructure_management \
|
||||
.update_cluster_template(
|
||||
cluster_template,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
patches = _utils.generate_patches_from_kwargs(operation, **kwargs)
|
||||
# No need to fire an API call if there is an empty patch
|
||||
if not patches:
|
||||
return cluster_template
|
||||
|
||||
with _utils.shade_exceptions(
|
||||
"Error updating cluster template {0}".format(name_or_id)):
|
||||
if getattr(self._container_infra_client,
|
||||
'_has_magnum_after_newton', False):
|
||||
self._container_infra_client.patch(
|
||||
'/clustertemplates/{id}'.format(id=cluster_template['id']),
|
||||
json=patches)
|
||||
else:
|
||||
self._container_infra_client.patch(
|
||||
'/baymodels/{id}'.format(id=cluster_template['id']),
|
||||
json=patches)
|
||||
|
||||
new_cluster_template = self.get_cluster_template(name_or_id)
|
||||
return new_cluster_template
|
||||
update_baymodel = update_cluster_template
|
||||
update_coe_cluster_template = update_cluster_template
|
||||
return cluster_template
|
||||
|
||||
def list_magnum_services(self):
|
||||
"""List all Magnum services.
|
||||
|
@ -13,6 +13,9 @@
|
||||
from openstack.container_infrastructure_management.v1 import (
|
||||
cluster as _cluster
|
||||
)
|
||||
from openstack.container_infrastructure_management.v1 import (
|
||||
cluster_template as _cluster_template
|
||||
)
|
||||
from openstack import proxy
|
||||
|
||||
|
||||
@ -20,6 +23,7 @@ class Proxy(proxy.Proxy):
|
||||
|
||||
_resource_registry = {
|
||||
"cluster": _cluster.Cluster,
|
||||
"cluster_template": _cluster_template.ClusterTemplate,
|
||||
}
|
||||
|
||||
def create_cluster(self, **attrs):
|
||||
@ -107,3 +111,99 @@ class Proxy(proxy.Proxy):
|
||||
:class:`~openstack.container_infrastructure_management.v1.cluster.Cluster`
|
||||
"""
|
||||
return self._update(_cluster.Cluster, cluster, **attrs)
|
||||
|
||||
# ============== Cluster Templates ==============
|
||||
def create_cluster_template(self, **attrs):
|
||||
"""Create a new cluster_template from attributes
|
||||
|
||||
:param dict attrs: Keyword arguments which will be used to create a
|
||||
:class:`~openstack.container_infrastructure_management.v1.cluster_template.ClusterTemplate`,
|
||||
comprised of the properties on the ClusterTemplate class.
|
||||
:returns: The results of cluster_template creation
|
||||
:rtype:
|
||||
:class:`~openstack.container_infrastructure_management.v1.cluster_template.ClusterTemplate`
|
||||
"""
|
||||
return self._create(_cluster_template.ClusterTemplate, **attrs)
|
||||
|
||||
def delete_cluster_template(self, cluster_template, ignore_missing=True):
|
||||
"""Delete a cluster_template
|
||||
|
||||
:param cluster_template: The value can be either the ID of a
|
||||
cluster_template or a
|
||||
:class:`~openstack.container_infrastructure_management.v1.cluster_template.ClusterTemplate`
|
||||
instance.
|
||||
:param bool ignore_missing: When set to ``False``
|
||||
:class:`~openstack.exceptions.ResourceNotFound` will be raised when
|
||||
the cluster_template does not exist. When set to ``True``, no
|
||||
exception will be set when attempting to delete a nonexistent
|
||||
cluster_template.
|
||||
:returns: ``None``
|
||||
"""
|
||||
self._delete(
|
||||
_cluster_template.ClusterTemplate,
|
||||
cluster_template,
|
||||
ignore_missing=ignore_missing,
|
||||
)
|
||||
|
||||
def find_cluster_template(self, name_or_id, ignore_missing=True):
|
||||
"""Find a single cluster_template
|
||||
|
||||
:param name_or_id: The name or ID of a cluster_template.
|
||||
:param bool ignore_missing: When set to ``False``
|
||||
:class:`~openstack.exceptions.ResourceNotFound` will be
|
||||
raised when the resource does not exist.
|
||||
When set to ``True``, None will be returned when
|
||||
attempting to find a nonexistent resource.
|
||||
:returns: One
|
||||
:class:`~openstack.container_infrastructure_management.v1.cluster_template.ClusterTemplate`
|
||||
or None
|
||||
"""
|
||||
return self._find(
|
||||
_cluster_template.ClusterTemplate,
|
||||
name_or_id,
|
||||
ignore_missing=ignore_missing,
|
||||
)
|
||||
|
||||
def get_cluster_template(self, cluster_template):
|
||||
"""Get a single cluster_template
|
||||
|
||||
:param cluster_template: The value can be the ID of a cluster_template
|
||||
or a
|
||||
:class:`~openstack.container_infrastructure_management.v1.cluster_template.ClusterTemplate`
|
||||
instance.
|
||||
|
||||
:returns: One
|
||||
:class:`~openstack.container_infrastructure_management.v1.cluster_template.ClusterTemplate`
|
||||
:raises: :class:`~openstack.exceptions.ResourceNotFound`
|
||||
when no resource can be found.
|
||||
"""
|
||||
return self._get(_cluster_template.ClusterTemplate, cluster_template)
|
||||
|
||||
def cluster_templates(self, **query):
|
||||
"""Return a generator of cluster_templates
|
||||
|
||||
:param kwargs query: Optional query parameters to be sent to limit
|
||||
the resources being returned.
|
||||
|
||||
:returns: A generator of cluster_template objects
|
||||
:rtype:
|
||||
:class:`~openstack.container_infrastructure_management.v1.cluster_template.ClusterTemplate`
|
||||
"""
|
||||
return self._list(_cluster_template.ClusterTemplate, **query)
|
||||
|
||||
def update_cluster_template(self, cluster_template, **attrs):
|
||||
"""Update a cluster_template
|
||||
|
||||
:param cluster_template: Either the id of a cluster_template or a
|
||||
:class:`~openstack.container_infrastructure_management.v1.cluster_template.ClusterTemplate`
|
||||
instance.
|
||||
:param attrs: The attributes to update on the cluster_template
|
||||
represented by ``cluster_template``.
|
||||
|
||||
:returns: The updated cluster_template
|
||||
:rtype:
|
||||
:class:`~openstack.container_infrastructure_management.v1.cluster_template.ClusterTemplate`
|
||||
"""
|
||||
return self._update(
|
||||
_cluster_template.ClusterTemplate, cluster_template, **attrs
|
||||
)
|
||||
|
@ -88,11 +88,10 @@ class Cluster(resource.Resource):
|
||||
#: the bay/cluster. The login name is specific to the bay/cluster driver.
|
||||
#: For example, with fedora-atomic image the default login name is fedora.
|
||||
keypair = resource.Body('keypair')
|
||||
#: Arbitrary labels in the form of key=value pairs. The accepted keys and
|
||||
#: valid values are defined in the bay/cluster drivers. They are used as a
|
||||
#: way to pass additional parameters that are specific to a bay/cluster
|
||||
#: driver.
|
||||
labels = resource.Body('labels', type=list)
|
||||
#: Arbitrary labels. The accepted keys and valid values are defined in the
|
||||
#: bay/cluster drivers. They are used as a way to pass additional
|
||||
#: parameters that are specific to a bay/cluster driver.
|
||||
labels = resource.Body('labels', type=dict)
|
||||
#: A list of floating IPs of all master nodes.
|
||||
master_addresses = resource.Body('master_addresses', type=list)
|
||||
#: The number of servers that will serve as master for the bay/cluster. Set
|
||||
|
@ -0,0 +1,116 @@
|
||||
# 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 openstack import resource
|
||||
|
||||
|
||||
class ClusterTemplate(resource.Resource):
|
||||
|
||||
resources_key = 'clustertemplates'
|
||||
base_path = '/clustertemplates'
|
||||
|
||||
# capabilities
|
||||
allow_create = True
|
||||
allow_fetch = True
|
||||
allow_commit = True
|
||||
allow_delete = True
|
||||
allow_list = True
|
||||
allow_patch = True
|
||||
|
||||
commit_method = 'PATCH'
|
||||
commit_jsonpatch = True
|
||||
|
||||
#: The exposed port of COE API server.
|
||||
apiserver_port = resource.Body('apiserver_port', type=int)
|
||||
#: Display the attribute os_distro defined as appropriate metadata in image
|
||||
#: for the bay/cluster driver.
|
||||
cluster_distro = resource.Body('cluster_distro')
|
||||
#: Specify the Container Orchestration Engine to use. Supported COEs
|
||||
#: include kubernetes, swarm, mesos.
|
||||
coe = resource.Body('coe')
|
||||
#: The date and time when the resource was created.
|
||||
created_at = resource.Body('created_at')
|
||||
#: The name of a driver to manage the storage for the images and the
|
||||
#: container’s writable layer.
|
||||
docker_storage_driver = resource.Body('docker_storage_driver')
|
||||
#: The size in GB for the local storage on each server for the Docker
|
||||
#: daemon to cache the images and host the containers.
|
||||
docker_volume_size = resource.Body('docker_volume_size', type=int)
|
||||
#: The DNS nameserver for the servers and containers in the bay/cluster to
|
||||
#: use.
|
||||
dns_nameserver = resource.Body('dns_nameserver')
|
||||
#: The name or network ID of a Neutron network to provide connectivity to
|
||||
#: the external internet for the bay/cluster.
|
||||
external_network_id = resource.Body('external_network_id')
|
||||
#: The name or network ID of a Neutron network to provide connectivity to
|
||||
#: the internal network for the bay/cluster.
|
||||
fixed_network = resource.Body('fixed_network')
|
||||
#: Fixed subnet that are using to allocate network address for nodes in
|
||||
#: bay/cluster.
|
||||
fixed_subnet = resource.Body('fixed_subnet')
|
||||
#: The nova flavor ID or name for booting the node servers.
|
||||
flavor_id = resource.Body('flavor_id')
|
||||
#: The IP address for a proxy to use when direct http access
|
||||
#: from the servers to sites on the external internet is blocked.
|
||||
#: This may happen in certain countries or enterprises, and the
|
||||
#: proxy allows the servers and containers to access these sites.
|
||||
#: The format is a URL including a port number. The default is
|
||||
#: None.
|
||||
http_proxy = resource.Body('http_proxy')
|
||||
#: The IP address for a proxy to use when direct https access from the
|
||||
#: servers to sites on the external internet is blocked.
|
||||
https_proxy = resource.Body('https_proxy')
|
||||
#: The name or UUID of the base image in Glance to boot the servers for the
|
||||
#: bay/cluster.
|
||||
image_id = resource.Body('image_id')
|
||||
#: The URL pointing to users’s own private insecure docker
|
||||
#: registry to deploy and run docker containers.
|
||||
insecure_registry = resource.Body('insecure_registry')
|
||||
#: Whether enable or not using the floating IP of cloud provider.
|
||||
is_floating_ip_enabled = resource.Body('floating_ip_enabled')
|
||||
#: Indicates whether the ClusterTemplate is hidden or not.
|
||||
is_hidden = resource.Body('hidden', type=bool)
|
||||
#: this option can be set to false to create a bay/cluster without the load
|
||||
#: balancer.
|
||||
is_master_lb_enabled = resource.Body('master_lb_enabled', type=bool)
|
||||
#: Specifying this parameter will disable TLS so that users can access the
|
||||
#: COE endpoints without a certificate.
|
||||
is_tls_disabled = resource.Body('tls_disabled', type=bool)
|
||||
#: Setting this flag makes the baymodel/cluster template public and
|
||||
#: accessible by other users.
|
||||
is_public = resource.Body('public', type=bool)
|
||||
#: This option provides an alternative registry based on the Registry V2
|
||||
is_registry_enabled = resource.Body('registry_enabled', type=bool)
|
||||
#: The name of the SSH keypair to configure in the bay/cluster servers for
|
||||
#: ssh access.
|
||||
keypair_id = resource.Body('keypair_id')
|
||||
#: Arbitrary labels. The accepted keys and valid values are defined in the
|
||||
#: bay/cluster drivers. They are used as a way to pass additional
|
||||
#: parameters that are specific to a bay/cluster driver.
|
||||
labels = resource.Body('labels', type=dict)
|
||||
#: The flavor of the master node for this baymodel/cluster template.
|
||||
master_flavor_id = resource.Body('master_flavor_id')
|
||||
#: The name of a network driver for providing the networks for the
|
||||
#: containers.
|
||||
network_driver = resource.Body('network_driver')
|
||||
#: When a proxy server is used, some sites should not go through the proxy
|
||||
#: and should be accessed normally.
|
||||
no_proxy = resource.Body('no_proxy')
|
||||
#: The servers in the bay/cluster can be vm or baremetal.
|
||||
server_type = resource.Body('server_type')
|
||||
#: The date and time when the resource was updated.
|
||||
updated_at = resource.Body('updated_at')
|
||||
#: The UUID of the cluster template.
|
||||
uuid = resource.Body('uuid', alternate_id=True)
|
||||
#: The name of a volume driver for managing the persistent storage for the
|
||||
#: containers.
|
||||
volume_driver = resource.Body('volume_driver')
|
@ -90,7 +90,7 @@ class TestClusterTemplate(base.BaseFunctionalTest):
|
||||
# Test we can update a field on the cluster_template and only that
|
||||
# field is updated
|
||||
cluster_template_update = self.user_cloud.update_cluster_template(
|
||||
self.ct['uuid'], 'replace', tls_disabled=True)
|
||||
self.ct, tls_disabled=True)
|
||||
self.assertEqual(
|
||||
cluster_template_update['uuid'], self.ct['uuid'])
|
||||
self.assertTrue(cluster_template_update['tls_disabled'])
|
||||
|
@ -10,14 +10,14 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import munch
|
||||
import testtools
|
||||
|
||||
from openstack.container_infrastructure_management.v1 import cluster_template
|
||||
from openstack import exceptions
|
||||
from openstack.tests.unit import base
|
||||
|
||||
|
||||
cluster_template_obj = munch.Munch(
|
||||
cluster_template_obj = dict(
|
||||
apiserver_port=12345,
|
||||
cluster_distro='fake-distro',
|
||||
coe='fake-coe',
|
||||
@ -50,6 +50,12 @@ cluster_template_obj = munch.Munch(
|
||||
|
||||
class TestClusterTemplates(base.TestCase):
|
||||
|
||||
def _compare_clustertemplates(self, exp, real):
|
||||
self.assertDictEqual(
|
||||
cluster_template.ClusterTemplate(**exp).to_dict(computed=False),
|
||||
real.to_dict(computed=False),
|
||||
)
|
||||
|
||||
def get_mock_url(
|
||||
self,
|
||||
service_type='container-infrastructure-management',
|
||||
@ -64,15 +70,12 @@ class TestClusterTemplates(base.TestCase):
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='clustertemplates'),
|
||||
status_code=404),
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='baymodels/detail'),
|
||||
json=dict(baymodels=[cluster_template_obj.toDict()]))])
|
||||
json=dict(clustertemplates=[cluster_template_obj]))])
|
||||
cluster_templates_list = self.cloud.list_cluster_templates()
|
||||
self.assertEqual(
|
||||
self._compare_clustertemplates(
|
||||
cluster_template_obj,
|
||||
cluster_templates_list[0],
|
||||
self.cloud._normalize_cluster_template(cluster_template_obj))
|
||||
)
|
||||
self.assert_calls()
|
||||
|
||||
def test_list_cluster_templates_with_detail(self):
|
||||
@ -80,15 +83,12 @@ class TestClusterTemplates(base.TestCase):
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='clustertemplates'),
|
||||
status_code=404),
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='baymodels/detail'),
|
||||
json=dict(baymodels=[cluster_template_obj.toDict()]))])
|
||||
json=dict(clustertemplates=[cluster_template_obj]))])
|
||||
cluster_templates_list = self.cloud.list_cluster_templates(detail=True)
|
||||
self.assertEqual(
|
||||
self._compare_clustertemplates(
|
||||
cluster_template_obj,
|
||||
cluster_templates_list[0],
|
||||
self.cloud._normalize_cluster_template(cluster_template_obj))
|
||||
)
|
||||
self.assert_calls()
|
||||
|
||||
def test_search_cluster_templates_by_name(self):
|
||||
@ -96,11 +96,7 @@ class TestClusterTemplates(base.TestCase):
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='clustertemplates'),
|
||||
status_code=404),
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='baymodels/detail'),
|
||||
json=dict(baymodels=[cluster_template_obj.toDict()]))])
|
||||
json=dict(clustertemplates=[cluster_template_obj]))])
|
||||
|
||||
cluster_templates = self.cloud.search_cluster_templates(
|
||||
name_or_id='fake-cluster-template')
|
||||
@ -115,11 +111,7 @@ class TestClusterTemplates(base.TestCase):
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='clustertemplates'),
|
||||
status_code=404),
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='baymodels/detail'),
|
||||
json=dict(baymodels=[cluster_template_obj.toDict()]))])
|
||||
json=dict(clustertemplates=[cluster_template_obj]))])
|
||||
|
||||
cluster_templates = self.cloud.search_cluster_templates(
|
||||
name_or_id='non-existent')
|
||||
@ -132,16 +124,14 @@ class TestClusterTemplates(base.TestCase):
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='clustertemplates'),
|
||||
status_code=404),
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='baymodels/detail'),
|
||||
json=dict(baymodels=[cluster_template_obj.toDict()]))])
|
||||
json=dict(clustertemplates=[cluster_template_obj]))])
|
||||
|
||||
r = self.cloud.get_cluster_template('fake-cluster-template')
|
||||
self.assertIsNotNone(r)
|
||||
self.assertDictEqual(
|
||||
r, self.cloud._normalize_cluster_template(cluster_template_obj))
|
||||
self._compare_clustertemplates(
|
||||
cluster_template_obj,
|
||||
r,
|
||||
)
|
||||
self.assert_calls()
|
||||
|
||||
def test_get_cluster_template_not_found(self):
|
||||
@ -149,35 +139,29 @@ class TestClusterTemplates(base.TestCase):
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='clustertemplates'),
|
||||
status_code=404),
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='baymodels/detail'),
|
||||
json=dict(baymodels=[]))])
|
||||
json=dict(clustertemplates=[]))])
|
||||
r = self.cloud.get_cluster_template('doesNotExist')
|
||||
self.assertIsNone(r)
|
||||
self.assert_calls()
|
||||
|
||||
def test_create_cluster_template(self):
|
||||
json_response = cluster_template_obj.toDict()
|
||||
kwargs = dict(name=cluster_template_obj.name,
|
||||
image_id=cluster_template_obj.image_id,
|
||||
keypair_id=cluster_template_obj.keypair_id,
|
||||
coe=cluster_template_obj.coe)
|
||||
json_response = cluster_template_obj.copy()
|
||||
kwargs = dict(name=cluster_template_obj['name'],
|
||||
image_id=cluster_template_obj['image_id'],
|
||||
keypair_id=cluster_template_obj['keypair_id'],
|
||||
coe=cluster_template_obj['coe'])
|
||||
self.register_uris([
|
||||
dict(
|
||||
method='POST',
|
||||
uri=self.get_mock_url(resource='clustertemplates'),
|
||||
status_code=404),
|
||||
dict(
|
||||
method='POST',
|
||||
uri=self.get_mock_url(resource='baymodels'),
|
||||
json=json_response,
|
||||
validate=dict(json=kwargs)),
|
||||
])
|
||||
expected = self.cloud._normalize_cluster_template(json_response)
|
||||
validate=dict(json=kwargs))])
|
||||
response = self.cloud.create_cluster_template(**kwargs)
|
||||
self.assertEqual(response, expected)
|
||||
self._compare_clustertemplates(
|
||||
json_response,
|
||||
response
|
||||
)
|
||||
|
||||
self.assert_calls()
|
||||
|
||||
def test_create_cluster_template_exception(self):
|
||||
@ -185,10 +169,6 @@ class TestClusterTemplates(base.TestCase):
|
||||
dict(
|
||||
method='POST',
|
||||
uri=self.get_mock_url(resource='clustertemplates'),
|
||||
status_code=404),
|
||||
dict(
|
||||
method='POST',
|
||||
uri=self.get_mock_url(resource='baymodels'),
|
||||
status_code=403)])
|
||||
# TODO(mordred) requests here doens't give us a great story
|
||||
# for matching the old error message text. Investigate plumbing
|
||||
@ -207,14 +187,10 @@ class TestClusterTemplates(base.TestCase):
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='clustertemplates'),
|
||||
status_code=404),
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='baymodels/detail'),
|
||||
json=dict(baymodels=[cluster_template_obj.toDict()])),
|
||||
json=dict(clustertemplates=[cluster_template_obj])),
|
||||
dict(
|
||||
method='DELETE',
|
||||
uri=self.get_mock_url(resource='baymodels/fake-uuid')),
|
||||
uri=self.get_mock_url(resource='clustertemplates/fake-uuid')),
|
||||
])
|
||||
self.cloud.delete_cluster_template('fake-uuid')
|
||||
self.assert_calls()
|
||||
@ -224,43 +200,36 @@ class TestClusterTemplates(base.TestCase):
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='clustertemplates'),
|
||||
status_code=404),
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='baymodels/detail'),
|
||||
json=dict(baymodels=[cluster_template_obj.toDict()])),
|
||||
json=dict(clustertemplates=[cluster_template_obj])),
|
||||
dict(
|
||||
method='PATCH',
|
||||
uri=self.get_mock_url(resource='baymodels/fake-uuid'),
|
||||
uri=self.get_mock_url(resource='clustertemplates/fake-uuid'),
|
||||
status_code=200,
|
||||
validate=dict(
|
||||
json=[{
|
||||
u'op': u'replace',
|
||||
u'path': u'/name',
|
||||
u'value': u'new-cluster-template'
|
||||
'op': 'replace',
|
||||
'path': '/name',
|
||||
'value': 'new-cluster-template'
|
||||
}]
|
||||
)),
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='clustertemplates'),
|
||||
# This json value is not meaningful to the test - it just has
|
||||
# to be valid.
|
||||
json=dict(baymodels=[cluster_template_obj.toDict()])),
|
||||
])
|
||||
new_name = 'new-cluster-template'
|
||||
self.cloud.update_cluster_template(
|
||||
'fake-uuid', 'replace', name=new_name)
|
||||
updated = self.cloud.update_cluster_template(
|
||||
'fake-uuid', name=new_name)
|
||||
self.assertEqual(new_name, updated.name)
|
||||
self.assert_calls()
|
||||
|
||||
def test_get_coe_cluster_template(self):
|
||||
def test_coe_get_cluster_template(self):
|
||||
self.register_uris([
|
||||
dict(
|
||||
method='GET',
|
||||
uri=self.get_mock_url(resource='clustertemplates'),
|
||||
json=dict(clustertemplates=[cluster_template_obj.toDict()]))])
|
||||
json=dict(clustertemplates=[cluster_template_obj]))])
|
||||
|
||||
r = self.cloud.get_coe_cluster_template('fake-cluster-template')
|
||||
r = self.cloud.get_cluster_template('fake-cluster-template')
|
||||
self.assertIsNotNone(r)
|
||||
self.assertDictEqual(
|
||||
r, self.cloud._normalize_cluster_template(cluster_template_obj))
|
||||
self._compare_clustertemplates(
|
||||
cluster_template_obj,
|
||||
r,
|
||||
)
|
||||
self.assert_calls()
|
||||
|
@ -19,7 +19,7 @@ EXAMPLE = {
|
||||
"discovery_url": None,
|
||||
"flavor_id": None,
|
||||
"keypair": "my_keypair",
|
||||
"labels": [],
|
||||
"labels": {},
|
||||
"master_count": 2,
|
||||
"master_flavor_id": None,
|
||||
"name": "k8s",
|
||||
|
@ -0,0 +1,98 @@
|
||||
# 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 openstack.container_infrastructure_management.v1 import cluster_template
|
||||
from openstack.tests.unit import base
|
||||
|
||||
EXAMPLE = {
|
||||
"insecure_registry": None,
|
||||
"http_proxy": "http://10.164.177.169:8080",
|
||||
"updated_at": None,
|
||||
"floating_ip_enabled": True,
|
||||
"fixed_subnet": None,
|
||||
"master_flavor_id": None,
|
||||
"uuid": "085e1c4d-4f68-4bfd-8462-74b9e14e4f39",
|
||||
"no_proxy": "10.0.0.0/8,172.0.0.0/8,192.0.0.0/8,localhost",
|
||||
"https_proxy": "http://10.164.177.169:8080",
|
||||
"tls_disabled": False,
|
||||
"keypair_id": "kp",
|
||||
"public": False,
|
||||
"labels": {},
|
||||
"docker_volume_size": 3,
|
||||
"server_type": "vm",
|
||||
"external_network_id": "public",
|
||||
"cluster_distro": "fedora-atomic",
|
||||
"image_id": "fedora-atomic-latest",
|
||||
"volume_driver": "cinder",
|
||||
"registry_enabled": False,
|
||||
"docker_storage_driver": "devicemapper",
|
||||
"apiserver_port": None,
|
||||
"name": "k8s-bm2",
|
||||
"created_at": "2016-08-29T02:08:08+00:00",
|
||||
"network_driver": "flannel",
|
||||
"fixed_network": None,
|
||||
"coe": "kubernetes",
|
||||
"flavor_id": "m1.small",
|
||||
"master_lb_enabled": True,
|
||||
"dns_nameserver": "8.8.8.8",
|
||||
"hidden": True,
|
||||
}
|
||||
|
||||
|
||||
class TestClusterTemplate(base.TestCase):
|
||||
def test_basic(self):
|
||||
sot = cluster_template.ClusterTemplate()
|
||||
self.assertIsNone(sot.resource_key)
|
||||
self.assertEqual('clustertemplates', sot.resources_key)
|
||||
self.assertEqual('/clustertemplates', sot.base_path)
|
||||
self.assertTrue(sot.allow_create)
|
||||
self.assertTrue(sot.allow_fetch)
|
||||
self.assertTrue(sot.allow_commit)
|
||||
self.assertTrue(sot.allow_delete)
|
||||
self.assertTrue(sot.allow_list)
|
||||
|
||||
def test_make_it(self):
|
||||
sot = cluster_template.ClusterTemplate(**EXAMPLE)
|
||||
|
||||
self.assertEqual(EXAMPLE['apiserver_port'], sot.apiserver_port)
|
||||
self.assertEqual(EXAMPLE['cluster_distro'], sot.cluster_distro)
|
||||
self.assertEqual(EXAMPLE['coe'], sot.coe)
|
||||
self.assertEqual(EXAMPLE['created_at'], sot.created_at)
|
||||
self.assertEqual(EXAMPLE['docker_storage_driver'],
|
||||
sot.docker_storage_driver)
|
||||
self.assertEqual(EXAMPLE['docker_volume_size'], sot.docker_volume_size)
|
||||
self.assertEqual(EXAMPLE['dns_nameserver'], sot.dns_nameserver)
|
||||
self.assertEqual(EXAMPLE['external_network_id'],
|
||||
sot.external_network_id)
|
||||
self.assertEqual(EXAMPLE['fixed_network'], sot.fixed_network)
|
||||
self.assertEqual(EXAMPLE['fixed_subnet'], sot.fixed_subnet)
|
||||
self.assertEqual(EXAMPLE['flavor_id'], sot.flavor_id)
|
||||
self.assertEqual(EXAMPLE['http_proxy'], sot.http_proxy)
|
||||
self.assertEqual(EXAMPLE['https_proxy'], sot.https_proxy)
|
||||
self.assertEqual(EXAMPLE['image_id'], sot.image_id)
|
||||
self.assertEqual(EXAMPLE['insecure_registry'], sot.insecure_registry)
|
||||
self.assertEqual(EXAMPLE['floating_ip_enabled'],
|
||||
sot.is_floating_ip_enabled)
|
||||
self.assertEqual(EXAMPLE['hidden'], sot.is_hidden)
|
||||
self.assertEqual(EXAMPLE['master_lb_enabled'],
|
||||
sot.is_master_lb_enabled)
|
||||
self.assertEqual(EXAMPLE['tls_disabled'], sot.is_tls_disabled)
|
||||
self.assertEqual(EXAMPLE['public'], sot.is_public)
|
||||
self.assertEqual(EXAMPLE['registry_enabled'], sot.is_registry_enabled)
|
||||
self.assertEqual(EXAMPLE['keypair_id'], sot.keypair_id)
|
||||
self.assertEqual(EXAMPLE['master_flavor_id'], sot.master_flavor_id)
|
||||
self.assertEqual(EXAMPLE['network_driver'], sot.network_driver)
|
||||
self.assertEqual(EXAMPLE['no_proxy'], sot.no_proxy)
|
||||
self.assertEqual(EXAMPLE['server_type'], sot.server_type)
|
||||
self.assertEqual(EXAMPLE['updated_at'], sot.updated_at)
|
||||
self.assertEqual(EXAMPLE['uuid'], sot.uuid)
|
||||
self.assertEqual(EXAMPLE['volume_driver'], sot.volume_driver)
|
@ -12,16 +12,17 @@
|
||||
|
||||
from openstack.container_infrastructure_management.v1 import _proxy
|
||||
from openstack.container_infrastructure_management.v1 import cluster
|
||||
from openstack.container_infrastructure_management.v1 import cluster_template
|
||||
from openstack.tests.unit import test_proxy_base
|
||||
|
||||
|
||||
class TestClusterProxy(test_proxy_base.TestProxyBase):
|
||||
class TestMagnumProxy(test_proxy_base.TestProxyBase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.proxy = _proxy.Proxy(self.session)
|
||||
|
||||
|
||||
class TestCluster(TestClusterProxy):
|
||||
class TestCluster(TestMagnumProxy):
|
||||
def test_cluster_get(self):
|
||||
self.verify_get(self.proxy.get_cluster, cluster.Cluster)
|
||||
|
||||
@ -49,3 +50,46 @@ class TestCluster(TestClusterProxy):
|
||||
|
||||
def test_cluster_delete_ignore(self):
|
||||
self.verify_delete(self.proxy.delete_cluster, cluster.Cluster, True)
|
||||
|
||||
|
||||
class TestClusterTemplate(TestMagnumProxy):
|
||||
def test_cluster_template_get(self):
|
||||
self.verify_get(
|
||||
self.proxy.get_cluster_template, cluster_template.ClusterTemplate
|
||||
)
|
||||
|
||||
def test_cluster_template_find(self):
|
||||
self.verify_find(
|
||||
self.proxy.find_cluster_template,
|
||||
cluster_template.ClusterTemplate,
|
||||
method_kwargs={},
|
||||
expected_kwargs={},
|
||||
)
|
||||
|
||||
def test_cluster_templates(self):
|
||||
self.verify_list(
|
||||
self.proxy.cluster_templates,
|
||||
cluster_template.ClusterTemplate,
|
||||
method_kwargs={"query": 1},
|
||||
expected_kwargs={"query": 1},
|
||||
)
|
||||
|
||||
def test_cluster_template_create_attrs(self):
|
||||
self.verify_create(
|
||||
self.proxy.create_cluster_template,
|
||||
cluster_template.ClusterTemplate,
|
||||
)
|
||||
|
||||
def test_cluster_template_delete(self):
|
||||
self.verify_delete(
|
||||
self.proxy.delete_cluster_template,
|
||||
cluster_template.ClusterTemplate,
|
||||
False,
|
||||
)
|
||||
|
||||
def test_cluster_template_delete_ignore(self):
|
||||
self.verify_delete(
|
||||
self.proxy.delete_cluster_template,
|
||||
cluster_template.ClusterTemplate,
|
||||
True,
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user