40a384214c
SDK does return all keys and values of the template labels as strings. At the same time user can define some labels as integers or booleans, which will break comparison of labels and lead to module failure on consecutive runs. Change-Id: I7ab624428c8bb06030a2b28888f5cb89bb249f08
536 lines
19 KiB
Python
536 lines
19 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright (c) 2018 Catalyst IT Ltd.
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
DOCUMENTATION = r'''
|
|
---
|
|
module: coe_cluster_template
|
|
short_description: Manage COE cluster template in OpenStack Cloud
|
|
author: OpenStack Ansible SIG
|
|
description:
|
|
- Add or remove a COE (Container Orchestration Engine) cluster template
|
|
via OpenStack's Magnum aka Container Infrastructure Management API.
|
|
options:
|
|
coe:
|
|
description:
|
|
- The Container Orchestration Engine for this cluster template
|
|
- Required if I(state) is C(present).
|
|
choices: [kubernetes, swarm, mesos]
|
|
type: str
|
|
dns_nameserver:
|
|
description:
|
|
- The DNS nameserver address.
|
|
- Magnum's default value for I(dns_nameserver) is C(8.8.8.8).
|
|
type: str
|
|
docker_storage_driver:
|
|
description:
|
|
- Docker storage driver.
|
|
choices: [devicemapper, overlay, overlay2]
|
|
type: str
|
|
docker_volume_size:
|
|
description:
|
|
- The size in GB of the docker volume.
|
|
type: int
|
|
external_network_id:
|
|
description:
|
|
- The external network to attach to the cluster.
|
|
- When I(is_floating_ip_enabled) is set to C(true), then
|
|
I(external_network_id) must be defined.
|
|
type: str
|
|
fixed_network:
|
|
description:
|
|
- The fixed network name or id to attach to the cluster.
|
|
type: str
|
|
fixed_subnet:
|
|
description:
|
|
- The fixed subnet name or id to attach to the cluster.
|
|
type: str
|
|
flavor_id:
|
|
description:
|
|
- The flavor of the minion node for this cluster template.
|
|
type: str
|
|
is_floating_ip_enabled:
|
|
description:
|
|
- Indicates whether created clusters should have a floating ip or not.
|
|
- When I(is_floating_ip_enabled) is set to C(true), then
|
|
I(external_network_id) must be defined.
|
|
type: bool
|
|
default: true
|
|
aliases: ['floating_ip_enabled']
|
|
is_master_lb_enabled:
|
|
description:
|
|
- Indicates whether created clusters should have a load balancer
|
|
for master nodes or not.
|
|
- Magnum's default value for I(is_master_lb_enabled) is C(true),
|
|
ours is C(false).
|
|
type: bool
|
|
default: false
|
|
aliases: ['master_lb_enabled']
|
|
is_public:
|
|
description:
|
|
- Indicates whether the cluster template is public or not.
|
|
- Magnum's default value for I(is_public) is C(false).
|
|
type: bool
|
|
aliases: ['public']
|
|
is_registry_enabled:
|
|
description:
|
|
- Indicates whether the docker registry is enabled.
|
|
- Magnum's default value for I(is_registry_enabled) is C(false).
|
|
type: bool
|
|
aliases: ['registry_enabled']
|
|
insecure_registry:
|
|
description:
|
|
- The URL pointing to users own private insecure docker registry.
|
|
type: str
|
|
is_tls_disabled:
|
|
description:
|
|
- Indicates whether the TLS should be disabled.
|
|
- Magnum's default value for I(is_tls_disabled) is C(false).
|
|
type: bool
|
|
aliases: ['tls_disabled']
|
|
keypair_id:
|
|
description:
|
|
- Name or ID of the keypair to use.
|
|
type: str
|
|
image_id:
|
|
description:
|
|
- Image id the cluster will be based on.
|
|
- Required if I(state) is C(present).
|
|
type: str
|
|
labels:
|
|
description:
|
|
- One or more key/value pairs.
|
|
type: raw
|
|
http_proxy:
|
|
description:
|
|
- Address of a proxy that will receive all HTTP requests and relay them.
|
|
- The format is a URL including a port number.
|
|
type: str
|
|
https_proxy:
|
|
description:
|
|
- Address of a proxy that will receive all HTTPS requests and relay them.
|
|
- The format is a URL including a port number.
|
|
type: str
|
|
master_flavor_id:
|
|
description:
|
|
- The flavor of the master node for this cluster template.
|
|
type: str
|
|
name:
|
|
description:
|
|
- Name that has to be given to the cluster template.
|
|
required: true
|
|
type: str
|
|
network_driver:
|
|
description:
|
|
- The name of the driver used for instantiating container networks.
|
|
choices: [flannel, calico, docker]
|
|
type: str
|
|
no_proxy:
|
|
description:
|
|
- A comma separated list of IPs for which proxies should not be
|
|
used in the cluster.
|
|
type: str
|
|
server_type:
|
|
description:
|
|
- Server type for this cluster template.
|
|
- Magnum's default value for I(server_type) is C(vm).
|
|
choices: [vm, bm]
|
|
type: str
|
|
state:
|
|
description:
|
|
- Indicate desired state of the resource.
|
|
choices: [present, absent]
|
|
default: present
|
|
type: str
|
|
volume_driver:
|
|
description:
|
|
- The name of the driver used for instantiating container volumes.
|
|
choices: [cinder, rexray]
|
|
type: str
|
|
extends_documentation_fragment:
|
|
- openstack.cloud.openstack
|
|
'''
|
|
|
|
RETURN = r'''
|
|
cluster_template:
|
|
description: Dictionary describing the template.
|
|
returned: On success when I(state) is C(present).
|
|
type: dict
|
|
contains:
|
|
apiserver_port:
|
|
description: The exposed port of COE API server.
|
|
type: int
|
|
cluster_distro:
|
|
description: Display the attribute os_distro defined as appropriate
|
|
metadata in image for the bay/cluster driver.
|
|
type: str
|
|
coe:
|
|
description: The Container Orchestration Engine for this cluster
|
|
template. Supported COEs include kubernetes, swarm, mesos.
|
|
type: str
|
|
sample: kubernetes
|
|
created_at:
|
|
description: The date and time when the resource was created.
|
|
type: str
|
|
dns_nameserver:
|
|
description: The DNS nameserver for the servers and containers in the
|
|
bay/cluster to use.
|
|
type: str
|
|
sample: '8.8.8.8'
|
|
docker_storage_driver:
|
|
description: "The name of a driver to manage the storage for the images
|
|
and the container's writable layer."
|
|
type: str
|
|
docker_volume_size:
|
|
description: The size in GB for the local storage on each server for the
|
|
Docker daemon to cache the images and host the containers.
|
|
type: int
|
|
sample: 5
|
|
external_network_id:
|
|
description: The name or network ID of a Neutron network to provide
|
|
connectivity to the external internet for the bay/cluster.
|
|
type: str
|
|
sample: public
|
|
fixed_network:
|
|
description: The fixed network name to attach to the cluster.
|
|
type: str
|
|
sample: 07767ec6-85f5-44cb-bd63-242a8e7f0d9d
|
|
fixed_subnet:
|
|
description: The fixed subnet name to attach to the cluster.
|
|
type: str
|
|
sample: 05567ec6-85f5-44cb-bd63-242a8e7f0d9d
|
|
flavor_id:
|
|
description: The nova flavor ID or name for booting the node servers.
|
|
type: str
|
|
sample: c1.c1r1
|
|
http_proxy:
|
|
description: Address of a proxy that will receive all HTTP requests
|
|
and relay them. The format is a URL including a port
|
|
number.
|
|
type: str
|
|
sample: http://10.0.0.11:9090
|
|
https_proxy:
|
|
description: Address of a proxy that will receive all HTTPS requests
|
|
and relay them. The format is a URL including a port
|
|
number.
|
|
type: str
|
|
sample: https://10.0.0.10:8443
|
|
id:
|
|
description: The UUID of the cluster template.
|
|
type: str
|
|
image_id:
|
|
description: The name or UUID of the base image in Glance to boot the
|
|
servers for the bay/cluster.
|
|
type: str
|
|
sample: 05567ec6-85f5-44cb-bd63-242a8e7f0e9d
|
|
insecure_registry:
|
|
description: "The URL pointing to users's own private insecure docker
|
|
registry to deploy and run docker containers."
|
|
type: str
|
|
is_floating_ip_enabled:
|
|
description: Indicates whether created clusters should have a
|
|
floating ip or not.
|
|
type: bool
|
|
sample: true
|
|
is_hidden:
|
|
description: Indicates whether the cluster template is hidden or not.
|
|
type: bool
|
|
sample: false
|
|
is_master_lb_enabled:
|
|
description: Indicates whether created clusters should have a load
|
|
balancer for master nodes or not.
|
|
type: bool
|
|
sample: true
|
|
is_public:
|
|
description: Access to a baymodel/cluster template is normally limited to
|
|
the admin, owner or users within the same tenant as the
|
|
owners. Setting this flag makes the baymodel/cluster
|
|
template public and accessible by other users. The default
|
|
is not public.
|
|
type: bool
|
|
sample: false
|
|
is_registry_enabled:
|
|
description: "Docker images by default are pulled from the public Docker
|
|
registry, but in some cases, users may want to use a
|
|
private registry. This option provides an alternative
|
|
registry based on the Registry V2: Magnum will create a
|
|
local registry in the bay/cluster backed by swift to host
|
|
the images. The default is to use the public registry."
|
|
type: bool
|
|
sample: false
|
|
is_tls_disabled:
|
|
description: Transport Layer Security (TLS) is normally enabled to secure
|
|
the bay/cluster. In some cases, users may want to disable
|
|
TLS in the bay/cluster, for instance during development or
|
|
to troubleshoot certain problems. Specifying this parameter
|
|
will disable TLS so that users can access the COE endpoints
|
|
without a certificate. The default is TLS enabled.
|
|
type: bool
|
|
sample: false
|
|
keypair_id:
|
|
description: Name of the SSH keypair to configure in the bay/cluster
|
|
servers for ssh access.
|
|
type: str
|
|
sample: mykey
|
|
labels:
|
|
description: One or more key/value pairs.
|
|
type: dict
|
|
sample: {'key1': 'value1', 'key2': 'value2'}
|
|
master_flavor_id:
|
|
description: The flavor of the master node for this cluster template.
|
|
type: str
|
|
sample: c1.c1r1
|
|
name:
|
|
description: Name that has to be given to the cluster template.
|
|
type: str
|
|
sample: k8scluster
|
|
network_driver:
|
|
description: The name of a network driver for providing the networks for
|
|
the containers
|
|
type: str
|
|
sample: calico
|
|
no_proxy:
|
|
description: A comma separated list of IPs for which proxies should
|
|
not be used in the cluster.
|
|
type: str
|
|
sample: 10.0.0.4,10.0.0.5
|
|
server_type:
|
|
description: The servers in the bay/cluster can be vm or baremetal.
|
|
type: str
|
|
sample: vm
|
|
updated_at:
|
|
description: The date and time when the resource was updated.
|
|
type: str
|
|
uuid:
|
|
description: The UUID of the cluster template.
|
|
type: str
|
|
volume_driver:
|
|
description: The name of a volume driver for managing the persistent
|
|
storage for the containers.
|
|
type: str
|
|
sample: cinder
|
|
'''
|
|
|
|
EXAMPLES = r'''
|
|
- name: Create a new Kubernetes cluster template
|
|
openstack.cloud.coe_cluster_template:
|
|
cloud: devstack
|
|
coe: kubernetes
|
|
image_id: 2a8c9888-9054-4b06-a1ca-2bb61f9adb72
|
|
keypair_id: mykey
|
|
name: k8s
|
|
is_public: false
|
|
'''
|
|
|
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
|
|
|
|
|
class COEClusterTemplateModule(OpenStackModule):
|
|
argument_spec = dict(
|
|
coe=dict(choices=['kubernetes', 'swarm', 'mesos']),
|
|
dns_nameserver=dict(),
|
|
docker_storage_driver=dict(choices=['devicemapper', 'overlay',
|
|
'overlay2']),
|
|
docker_volume_size=dict(type='int'),
|
|
external_network_id=dict(),
|
|
fixed_network=dict(),
|
|
fixed_subnet=dict(),
|
|
flavor_id=dict(),
|
|
http_proxy=dict(),
|
|
https_proxy=dict(),
|
|
image_id=dict(),
|
|
is_floating_ip_enabled=dict(type='bool', default=True,
|
|
aliases=['floating_ip_enabled']),
|
|
keypair_id=dict(),
|
|
labels=dict(type='raw'),
|
|
master_flavor_id=dict(),
|
|
insecure_registry=dict(),
|
|
is_master_lb_enabled=dict(type='bool', default=False,
|
|
aliases=['master_lb_enabled']),
|
|
is_public=dict(type='bool', aliases=['public']),
|
|
is_registry_enabled=dict(type='bool', aliases=['registry_enabled']),
|
|
is_tls_disabled=dict(type='bool', aliases=['tls_disabled']),
|
|
name=dict(required=True),
|
|
network_driver=dict(choices=['flannel', 'calico', 'docker']),
|
|
no_proxy=dict(),
|
|
server_type=dict(choices=['vm', 'bm']),
|
|
state=dict(default='present', choices=['absent', 'present']),
|
|
volume_driver=dict(choices=['cinder', 'rexray']),
|
|
)
|
|
module_kwargs = dict(
|
|
required_if=[
|
|
('state', 'present', ('coe', 'image_id')),
|
|
],
|
|
supports_check_mode=True,
|
|
)
|
|
|
|
def run(self):
|
|
state = self.params['state']
|
|
|
|
cluster_template = self._find()
|
|
|
|
if self.ansible.check_mode:
|
|
self.exit_json(changed=self._will_change(state, cluster_template))
|
|
|
|
if state == 'present' and not cluster_template:
|
|
# Create cluster_template
|
|
cluster_template = self._create()
|
|
self.exit_json(
|
|
changed=True,
|
|
cluster_template=cluster_template.to_dict(computed=False))
|
|
|
|
elif state == 'present' and cluster_template:
|
|
# Update cluster_template
|
|
update = self._build_update(cluster_template)
|
|
if update:
|
|
cluster_template = self._update(cluster_template, update)
|
|
|
|
self.exit_json(
|
|
changed=bool(update),
|
|
cluster_template=cluster_template.to_dict(computed=False))
|
|
|
|
elif state == 'absent' and cluster_template:
|
|
# Delete cluster_template
|
|
self._delete(cluster_template)
|
|
self.exit_json(changed=True)
|
|
|
|
elif state == 'absent' and not cluster_template:
|
|
# Do nothing
|
|
self.exit_json(changed=False)
|
|
|
|
def _build_update(self, cluster_template):
|
|
update = {}
|
|
|
|
if self.params['is_floating_ip_enabled'] \
|
|
and self.params['external_network_id'] is None:
|
|
raise ValueError('is_floating_ip_enabled is True'
|
|
' but external_network_id is missing')
|
|
|
|
# TODO: Implement support for updates.
|
|
non_updateable_keys = [k for k in ['coe', 'dns_nameserver',
|
|
'docker_storage_driver',
|
|
'docker_volume_size',
|
|
'external_network_id',
|
|
'fixed_network',
|
|
'fixed_subnet', 'flavor_id',
|
|
'http_proxy', 'https_proxy',
|
|
'image_id',
|
|
'insecure_registry',
|
|
'is_floating_ip_enabled',
|
|
'is_master_lb_enabled',
|
|
'is_public', 'is_registry_enabled',
|
|
'is_tls_disabled', 'keypair_id',
|
|
'master_flavor_id', 'name',
|
|
'network_driver', 'no_proxy',
|
|
'server_type', 'volume_driver']
|
|
if self.params[k] is not None
|
|
and self.params[k] != cluster_template[k]]
|
|
|
|
labels = self.params['labels']
|
|
if labels is not None:
|
|
if isinstance(labels, str):
|
|
labels = dict([tuple(kv.split(":"))
|
|
for kv in labels.split(",")])
|
|
elif isinstance(labels, dict):
|
|
labels = dict({str(k): str(v)
|
|
for k, v in labels.items()})
|
|
if labels != cluster_template['labels']:
|
|
non_updateable_keys.append('labels')
|
|
|
|
if non_updateable_keys:
|
|
self.fail_json(msg='Cannot update parameters {0}'
|
|
.format(non_updateable_keys))
|
|
|
|
attributes = dict((k, self.params[k])
|
|
for k in []
|
|
if self.params[k] is not None
|
|
and self.params[k] != cluster_template[k])
|
|
|
|
if attributes:
|
|
update['attributes'] = attributes
|
|
|
|
return update
|
|
|
|
def _create(self):
|
|
if self.params['is_floating_ip_enabled'] \
|
|
and self.params['external_network_id'] is None:
|
|
raise ValueError('is_floating_ip_enabled is True'
|
|
' but external_network_id is missing')
|
|
|
|
# TODO: Complement *_id parameters with find_* functions to allow
|
|
# specifying names in addition to IDs.
|
|
kwargs = dict((k, self.params[k])
|
|
for k in ['coe', 'dns_nameserver',
|
|
'docker_storage_driver', 'docker_volume_size',
|
|
'external_network_id', 'fixed_network',
|
|
'fixed_subnet', 'flavor_id', 'http_proxy',
|
|
'https_proxy', 'image_id',
|
|
'insecure_registry', 'is_floating_ip_enabled',
|
|
'is_master_lb_enabled', 'is_public',
|
|
'is_registry_enabled', 'is_tls_disabled',
|
|
'keypair_id', 'master_flavor_id', 'name',
|
|
'network_driver', 'no_proxy', 'server_type',
|
|
'volume_driver']
|
|
if self.params[k] is not None)
|
|
|
|
labels = self.params['labels']
|
|
if labels is not None:
|
|
if isinstance(labels, str):
|
|
labels = dict([tuple(kv.split(":"))
|
|
for kv in labels.split(",")])
|
|
kwargs['labels'] = labels
|
|
|
|
return self.conn.container_infrastructure_management.\
|
|
create_cluster_template(**kwargs)
|
|
|
|
def _delete(self, cluster_template):
|
|
self.conn.container_infrastructure_management.\
|
|
delete_cluster_template(cluster_template['id'])
|
|
|
|
def _find(self):
|
|
name = self.params['name']
|
|
filters = {}
|
|
|
|
image_id = self.params['image_id']
|
|
if image_id is not None:
|
|
filters['image_id'] = image_id
|
|
|
|
coe = self.params['coe']
|
|
if coe is not None:
|
|
filters['coe'] = coe
|
|
|
|
return self.conn.get_cluster_template(name_or_id=name,
|
|
filters=filters)
|
|
|
|
def _update(self, cluster_template, update):
|
|
attributes = update.get('attributes')
|
|
if attributes:
|
|
# TODO: Implement support for updates.
|
|
# cluster_template = self.conn.\
|
|
# container_infrastructure_management.update_cluster_template(...)
|
|
pass
|
|
|
|
return cluster_template
|
|
|
|
def _will_change(self, state, cluster_template):
|
|
if state == 'present' and not cluster_template:
|
|
return True
|
|
elif state == 'present' and cluster_template:
|
|
return bool(self._build_update(cluster_template))
|
|
elif state == 'absent' and cluster_template:
|
|
return True
|
|
else:
|
|
# state == 'absent' and not cluster_template:
|
|
return False
|
|
|
|
|
|
def main():
|
|
module = COEClusterTemplateModule()
|
|
module()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|