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:
Artem Goncharov 2023-01-24 18:34:46 +01:00
parent 289e5c2d3c
commit b66c6cc847
9 changed files with 442 additions and 182 deletions

View File

@ -201,22 +201,8 @@ class CoeCloudMixin:
:raises: ``OpenStackCloudException``: if something goes wrong during :raises: ``OpenStackCloudException``: if something goes wrong during
the OpenStack API call. the OpenStack API call.
""" """
with _utils.shade_exceptions("Error fetching cluster template list"): return list(
try: self.container_infrastructure_management.cluster_templates())
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
def search_cluster_templates( def search_cluster_templates(
self, name_or_id=None, filters=None, detail=False): self, name_or_id=None, filters=None, detail=False):
@ -235,8 +221,6 @@ class CoeCloudMixin:
cluster_templates = self.list_cluster_templates(detail=detail) cluster_templates = self.list_cluster_templates(detail=detail)
return _utils._filter_list( return _utils._filter_list(
cluster_templates, name_or_id, filters) 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): def get_cluster_template(self, name_or_id, filters=None, detail=False):
"""Get a cluster template by name or ID. """Get a cluster template by name or ID.
@ -260,10 +244,9 @@ class CoeCloudMixin:
:returns: A cluster template dict or None if no matching :returns: A cluster template dict or None if no matching
cluster template is found. cluster template is found.
""" """
return _utils._get_entity(self, 'cluster_template', name_or_id, return _utils._get_entity(
filters=filters, detail=detail) self, 'cluster_template', name_or_id,
get_baymodel = get_cluster_template filters=filters, detail=detail)
get_coe_cluster_template = get_cluster_template
def create_cluster_template( def create_cluster_template(
self, name, image_id=None, keypair_id=None, coe=None, **kwargs): self, name, image_id=None, keypair_id=None, coe=None, **kwargs):
@ -280,28 +263,16 @@ class CoeCloudMixin:
:raises: ``OpenStackCloudException`` if something goes wrong during :raises: ``OpenStackCloudException`` if something goes wrong during
the OpenStack API call the OpenStack API call
""" """
error_message = ("Error creating cluster template of name" cluster_template = self.container_infrastructure_management \
" {cluster_template_name}".format( .create_cluster_template(
cluster_template_name=name)) name=name,
with _utils.shade_exceptions(error_message): image_id=image_id,
body = kwargs.copy() keypair_id=keypair_id,
body['name'] = name coe=coe,
body['image_id'] = image_id **kwargs,
body['keypair_id'] = keypair_id )
body['coe'] = coe
try: return cluster_template
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
def delete_cluster_template(self, name_or_id): def delete_cluster_template(self, name_or_id):
"""Delete a cluster template. """Delete a cluster template.
@ -322,68 +293,31 @@ class CoeCloudMixin:
exc_info=True) exc_info=True)
return False return False
with _utils.shade_exceptions("Error in deleting cluster template"): self.container_infrastructure_management.delete_cluster_template(
if getattr(self._container_infra_client, cluster_template)
'_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)
return True 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', def update_cluster_template(self, name_or_id, **kwargs):
'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):
"""Update a cluster template. """Update a cluster template.
:param name_or_id: Name or ID of the cluster template being updated. :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. :raises: OpenStackCloudException on operation error.
""" """
self.list_cluster_templates.invalidate(self)
cluster_template = self.get_cluster_template(name_or_id) cluster_template = self.get_cluster_template(name_or_id)
if not cluster_template: if not cluster_template:
raise exc.OpenStackCloudException( raise exc.OpenStackCloudException(
"Cluster template %s not found." % name_or_id) "Cluster template %s not found." % name_or_id)
if operation not in ['add', 'replace', 'remove']: cluster_template = self.container_infrastructure_management \
raise TypeError( .update_cluster_template(
"%s operation not in 'add', 'replace', 'remove'" % operation) cluster_template,
**kwargs
)
patches = _utils.generate_patches_from_kwargs(operation, **kwargs) return cluster_template
# 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
def list_magnum_services(self): def list_magnum_services(self):
"""List all Magnum services. """List all Magnum services.

View File

@ -13,6 +13,9 @@
from openstack.container_infrastructure_management.v1 import ( from openstack.container_infrastructure_management.v1 import (
cluster as _cluster cluster as _cluster
) )
from openstack.container_infrastructure_management.v1 import (
cluster_template as _cluster_template
)
from openstack import proxy from openstack import proxy
@ -20,6 +23,7 @@ class Proxy(proxy.Proxy):
_resource_registry = { _resource_registry = {
"cluster": _cluster.Cluster, "cluster": _cluster.Cluster,
"cluster_template": _cluster_template.ClusterTemplate,
} }
def create_cluster(self, **attrs): def create_cluster(self, **attrs):
@ -107,3 +111,99 @@ class Proxy(proxy.Proxy):
:class:`~openstack.container_infrastructure_management.v1.cluster.Cluster` :class:`~openstack.container_infrastructure_management.v1.cluster.Cluster`
""" """
return self._update(_cluster.Cluster, cluster, **attrs) 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
)

View File

@ -88,11 +88,10 @@ class Cluster(resource.Resource):
#: the bay/cluster. The login name is specific to the bay/cluster driver. #: 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. #: For example, with fedora-atomic image the default login name is fedora.
keypair = resource.Body('keypair') keypair = resource.Body('keypair')
#: Arbitrary labels in the form of key=value pairs. The accepted keys and #: Arbitrary labels. The accepted keys and valid values are defined in the
#: valid values are defined in the bay/cluster drivers. They are used as a #: bay/cluster drivers. They are used as a way to pass additional
#: way to pass additional parameters that are specific to a bay/cluster #: parameters that are specific to a bay/cluster driver.
#: driver. labels = resource.Body('labels', type=dict)
labels = resource.Body('labels', type=list)
#: A list of floating IPs of all master nodes. #: A list of floating IPs of all master nodes.
master_addresses = resource.Body('master_addresses', type=list) master_addresses = resource.Body('master_addresses', type=list)
#: The number of servers that will serve as master for the bay/cluster. Set #: The number of servers that will serve as master for the bay/cluster. Set

View File

@ -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
#: containers 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 userss 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')

View File

@ -90,7 +90,7 @@ class TestClusterTemplate(base.BaseFunctionalTest):
# Test we can update a field on the cluster_template and only that # Test we can update a field on the cluster_template and only that
# field is updated # field is updated
cluster_template_update = self.user_cloud.update_cluster_template( cluster_template_update = self.user_cloud.update_cluster_template(
self.ct['uuid'], 'replace', tls_disabled=True) self.ct, tls_disabled=True)
self.assertEqual( self.assertEqual(
cluster_template_update['uuid'], self.ct['uuid']) cluster_template_update['uuid'], self.ct['uuid'])
self.assertTrue(cluster_template_update['tls_disabled']) self.assertTrue(cluster_template_update['tls_disabled'])

View File

@ -10,14 +10,14 @@
# 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 munch
import testtools import testtools
from openstack.container_infrastructure_management.v1 import cluster_template
from openstack import exceptions from openstack import exceptions
from openstack.tests.unit import base from openstack.tests.unit import base
cluster_template_obj = munch.Munch( cluster_template_obj = dict(
apiserver_port=12345, apiserver_port=12345,
cluster_distro='fake-distro', cluster_distro='fake-distro',
coe='fake-coe', coe='fake-coe',
@ -50,6 +50,12 @@ cluster_template_obj = munch.Munch(
class TestClusterTemplates(base.TestCase): 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( def get_mock_url(
self, self,
service_type='container-infrastructure-management', service_type='container-infrastructure-management',
@ -64,15 +70,12 @@ class TestClusterTemplates(base.TestCase):
dict( dict(
method='GET', method='GET',
uri=self.get_mock_url(resource='clustertemplates'), uri=self.get_mock_url(resource='clustertemplates'),
status_code=404), json=dict(clustertemplates=[cluster_template_obj]))])
dict(
method='GET',
uri=self.get_mock_url(resource='baymodels/detail'),
json=dict(baymodels=[cluster_template_obj.toDict()]))])
cluster_templates_list = self.cloud.list_cluster_templates() cluster_templates_list = self.cloud.list_cluster_templates()
self.assertEqual( self._compare_clustertemplates(
cluster_template_obj,
cluster_templates_list[0], cluster_templates_list[0],
self.cloud._normalize_cluster_template(cluster_template_obj)) )
self.assert_calls() self.assert_calls()
def test_list_cluster_templates_with_detail(self): def test_list_cluster_templates_with_detail(self):
@ -80,15 +83,12 @@ class TestClusterTemplates(base.TestCase):
dict( dict(
method='GET', method='GET',
uri=self.get_mock_url(resource='clustertemplates'), uri=self.get_mock_url(resource='clustertemplates'),
status_code=404), json=dict(clustertemplates=[cluster_template_obj]))])
dict(
method='GET',
uri=self.get_mock_url(resource='baymodels/detail'),
json=dict(baymodels=[cluster_template_obj.toDict()]))])
cluster_templates_list = self.cloud.list_cluster_templates(detail=True) cluster_templates_list = self.cloud.list_cluster_templates(detail=True)
self.assertEqual( self._compare_clustertemplates(
cluster_template_obj,
cluster_templates_list[0], cluster_templates_list[0],
self.cloud._normalize_cluster_template(cluster_template_obj)) )
self.assert_calls() self.assert_calls()
def test_search_cluster_templates_by_name(self): def test_search_cluster_templates_by_name(self):
@ -96,11 +96,7 @@ class TestClusterTemplates(base.TestCase):
dict( dict(
method='GET', method='GET',
uri=self.get_mock_url(resource='clustertemplates'), uri=self.get_mock_url(resource='clustertemplates'),
status_code=404), json=dict(clustertemplates=[cluster_template_obj]))])
dict(
method='GET',
uri=self.get_mock_url(resource='baymodels/detail'),
json=dict(baymodels=[cluster_template_obj.toDict()]))])
cluster_templates = self.cloud.search_cluster_templates( cluster_templates = self.cloud.search_cluster_templates(
name_or_id='fake-cluster-template') name_or_id='fake-cluster-template')
@ -115,11 +111,7 @@ class TestClusterTemplates(base.TestCase):
dict( dict(
method='GET', method='GET',
uri=self.get_mock_url(resource='clustertemplates'), uri=self.get_mock_url(resource='clustertemplates'),
status_code=404), json=dict(clustertemplates=[cluster_template_obj]))])
dict(
method='GET',
uri=self.get_mock_url(resource='baymodels/detail'),
json=dict(baymodels=[cluster_template_obj.toDict()]))])
cluster_templates = self.cloud.search_cluster_templates( cluster_templates = self.cloud.search_cluster_templates(
name_or_id='non-existent') name_or_id='non-existent')
@ -132,16 +124,14 @@ class TestClusterTemplates(base.TestCase):
dict( dict(
method='GET', method='GET',
uri=self.get_mock_url(resource='clustertemplates'), uri=self.get_mock_url(resource='clustertemplates'),
status_code=404), json=dict(clustertemplates=[cluster_template_obj]))])
dict(
method='GET',
uri=self.get_mock_url(resource='baymodels/detail'),
json=dict(baymodels=[cluster_template_obj.toDict()]))])
r = self.cloud.get_cluster_template('fake-cluster-template') r = self.cloud.get_cluster_template('fake-cluster-template')
self.assertIsNotNone(r) self.assertIsNotNone(r)
self.assertDictEqual( self._compare_clustertemplates(
r, self.cloud._normalize_cluster_template(cluster_template_obj)) cluster_template_obj,
r,
)
self.assert_calls() self.assert_calls()
def test_get_cluster_template_not_found(self): def test_get_cluster_template_not_found(self):
@ -149,35 +139,29 @@ class TestClusterTemplates(base.TestCase):
dict( dict(
method='GET', method='GET',
uri=self.get_mock_url(resource='clustertemplates'), uri=self.get_mock_url(resource='clustertemplates'),
status_code=404), json=dict(clustertemplates=[]))])
dict(
method='GET',
uri=self.get_mock_url(resource='baymodels/detail'),
json=dict(baymodels=[]))])
r = self.cloud.get_cluster_template('doesNotExist') r = self.cloud.get_cluster_template('doesNotExist')
self.assertIsNone(r) self.assertIsNone(r)
self.assert_calls() self.assert_calls()
def test_create_cluster_template(self): def test_create_cluster_template(self):
json_response = cluster_template_obj.toDict() json_response = cluster_template_obj.copy()
kwargs = dict(name=cluster_template_obj.name, kwargs = dict(name=cluster_template_obj['name'],
image_id=cluster_template_obj.image_id, image_id=cluster_template_obj['image_id'],
keypair_id=cluster_template_obj.keypair_id, keypair_id=cluster_template_obj['keypair_id'],
coe=cluster_template_obj.coe) coe=cluster_template_obj['coe'])
self.register_uris([ self.register_uris([
dict( dict(
method='POST', method='POST',
uri=self.get_mock_url(resource='clustertemplates'), uri=self.get_mock_url(resource='clustertemplates'),
status_code=404),
dict(
method='POST',
uri=self.get_mock_url(resource='baymodels'),
json=json_response, json=json_response,
validate=dict(json=kwargs)), validate=dict(json=kwargs))])
])
expected = self.cloud._normalize_cluster_template(json_response)
response = self.cloud.create_cluster_template(**kwargs) response = self.cloud.create_cluster_template(**kwargs)
self.assertEqual(response, expected) self._compare_clustertemplates(
json_response,
response
)
self.assert_calls() self.assert_calls()
def test_create_cluster_template_exception(self): def test_create_cluster_template_exception(self):
@ -185,10 +169,6 @@ class TestClusterTemplates(base.TestCase):
dict( dict(
method='POST', method='POST',
uri=self.get_mock_url(resource='clustertemplates'), uri=self.get_mock_url(resource='clustertemplates'),
status_code=404),
dict(
method='POST',
uri=self.get_mock_url(resource='baymodels'),
status_code=403)]) status_code=403)])
# TODO(mordred) requests here doens't give us a great story # TODO(mordred) requests here doens't give us a great story
# for matching the old error message text. Investigate plumbing # for matching the old error message text. Investigate plumbing
@ -207,14 +187,10 @@ class TestClusterTemplates(base.TestCase):
dict( dict(
method='GET', method='GET',
uri=self.get_mock_url(resource='clustertemplates'), uri=self.get_mock_url(resource='clustertemplates'),
status_code=404), json=dict(clustertemplates=[cluster_template_obj])),
dict(
method='GET',
uri=self.get_mock_url(resource='baymodels/detail'),
json=dict(baymodels=[cluster_template_obj.toDict()])),
dict( dict(
method='DELETE', 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.cloud.delete_cluster_template('fake-uuid')
self.assert_calls() self.assert_calls()
@ -224,43 +200,36 @@ class TestClusterTemplates(base.TestCase):
dict( dict(
method='GET', method='GET',
uri=self.get_mock_url(resource='clustertemplates'), uri=self.get_mock_url(resource='clustertemplates'),
status_code=404), json=dict(clustertemplates=[cluster_template_obj])),
dict(
method='GET',
uri=self.get_mock_url(resource='baymodels/detail'),
json=dict(baymodels=[cluster_template_obj.toDict()])),
dict( dict(
method='PATCH', method='PATCH',
uri=self.get_mock_url(resource='baymodels/fake-uuid'), uri=self.get_mock_url(resource='clustertemplates/fake-uuid'),
status_code=200, status_code=200,
validate=dict( validate=dict(
json=[{ json=[{
u'op': u'replace', 'op': 'replace',
u'path': u'/name', 'path': '/name',
u'value': u'new-cluster-template' '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' new_name = 'new-cluster-template'
self.cloud.update_cluster_template( updated = self.cloud.update_cluster_template(
'fake-uuid', 'replace', name=new_name) 'fake-uuid', name=new_name)
self.assertEqual(new_name, updated.name)
self.assert_calls() self.assert_calls()
def test_get_coe_cluster_template(self): def test_coe_get_cluster_template(self):
self.register_uris([ self.register_uris([
dict( dict(
method='GET', method='GET',
uri=self.get_mock_url(resource='clustertemplates'), 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.assertIsNotNone(r)
self.assertDictEqual( self._compare_clustertemplates(
r, self.cloud._normalize_cluster_template(cluster_template_obj)) cluster_template_obj,
r,
)
self.assert_calls() self.assert_calls()

View File

@ -19,7 +19,7 @@ EXAMPLE = {
"discovery_url": None, "discovery_url": None,
"flavor_id": None, "flavor_id": None,
"keypair": "my_keypair", "keypair": "my_keypair",
"labels": [], "labels": {},
"master_count": 2, "master_count": 2,
"master_flavor_id": None, "master_flavor_id": None,
"name": "k8s", "name": "k8s",

View File

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

View File

@ -12,16 +12,17 @@
from openstack.container_infrastructure_management.v1 import _proxy from openstack.container_infrastructure_management.v1 import _proxy
from openstack.container_infrastructure_management.v1 import cluster 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 from openstack.tests.unit import test_proxy_base
class TestClusterProxy(test_proxy_base.TestProxyBase): class TestMagnumProxy(test_proxy_base.TestProxyBase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.proxy = _proxy.Proxy(self.session) self.proxy = _proxy.Proxy(self.session)
class TestCluster(TestClusterProxy): class TestCluster(TestMagnumProxy):
def test_cluster_get(self): def test_cluster_get(self):
self.verify_get(self.proxy.get_cluster, cluster.Cluster) self.verify_get(self.proxy.get_cluster, cluster.Cluster)
@ -49,3 +50,46 @@ class TestCluster(TestClusterProxy):
def test_cluster_delete_ignore(self): def test_cluster_delete_ignore(self):
self.verify_delete(self.proxy.delete_cluster, cluster.Cluster, True) 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,
)