summaryrefslogtreecommitdiff
path: root/lib/ansible/module_utils/k8s/inventory.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ansible/module_utils/k8s/inventory.py')
-rw-r--r--lib/ansible/module_utils/k8s/inventory.py330
1 files changed, 330 insertions, 0 deletions
diff --git a/lib/ansible/module_utils/k8s/inventory.py b/lib/ansible/module_utils/k8s/inventory.py
new file mode 100644
index 0000000000..9cc4301758
--- /dev/null
+++ b/lib/ansible/module_utils/k8s/inventory.py
@@ -0,0 +1,330 @@
+#
+# Copyright 2018 Red Hat | Ansible
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, division, print_function
+
+from ansible.module_utils.six import iteritems
+
+try:
+ from openshift.helper.kubernetes import KubernetesObjectHelper
+ from openshift.helper.openshift import OpenShiftObjectHelper
+ from openshift.helper.exceptions import KubernetesException
+ HAS_K8S_MODULE_HELPER = True
+except ImportError as exc:
+ HAS_K8S_MODULE_HELPER = False
+
+
+class K8sInventoryException(Exception):
+ pass
+
+
+class K8sInventoryHelper(object):
+ helper = None
+ transport = 'kubectl'
+
+ def setup(self, config_data, cache, cache_key):
+ connections = config_data.get('connections')
+
+ if not HAS_K8S_MODULE_HELPER:
+ raise K8sInventoryException(
+ "This module requires the OpenShift Python client. Try `pip install openshift`"
+ )
+
+ source_data = None
+ if cache and cache_key in self._cache:
+ try:
+ source_data = self._cache[cache_key]
+ except KeyError:
+ pass
+
+ if not source_data:
+ self.fetch_objects(connections)
+
+ def fetch_objects(self, connections):
+ self.helper = self.get_helper('v1', 'namespace_list')
+
+ if connections:
+ if not isinstance(connections, list):
+ raise K8sInventoryException("Expecting connections to be a list.")
+
+ for connection in connections:
+ if not isinstance(connection, dict):
+ raise K8sInventoryException("Expecting connection to be a dictionary.")
+ self.authenticate(connection)
+ name = connection.get('name', self.get_default_host_name(self.helper.api_client.host))
+ if connection.get('namespaces'):
+ namespaces = connections['namespaces']
+ else:
+ namespaces = self.get_available_namespaces()
+ for namespace in namespaces:
+ self.get_pods_for_namespace(name, namespace)
+ self.get_services_for_namespace(name, namespace)
+ else:
+ name = self.get_default_host_name(self.helper.api_client.host)
+ namespaces = self.get_available_namespaces()
+ for namespace in namespaces:
+ self.get_pods_for_namespace(name, namespace)
+ self.get_services_for_namespace(name, namespace)
+
+ def authenticate(self, connection=None):
+ auth_options = {}
+ if connection:
+ auth_args = ('host', 'api_key', 'kubeconfig', 'context', 'username', 'password',
+ 'cert_file', 'key_file', 'ssl_ca_cert', 'verify_ssl')
+ for key, value in iteritems(connection):
+ if key in auth_args and value is not None:
+ auth_options[key] = value
+ try:
+ self.helper.set_client_config(**auth_options)
+ except KubernetesException as exc:
+ raise K8sInventoryException('Error connecting to the API: {0}'.format(exc.message))
+
+ @staticmethod
+ def get_default_host_name(host):
+ return host.replace('https://', '').replace('http://', '').replace('.', '-').replace(':', '_')
+
+ def get_helper(self, api_version, kind):
+ try:
+ helper = KubernetesObjectHelper(api_version=api_version, kind=kind, debug=False)
+ helper.get_model(api_version, kind)
+ return helper
+ except KubernetesException as exc:
+ raise K8sInventoryException('Error initializing object helper: {0}'.format(exc.message))
+
+ def get_available_namespaces(self):
+ try:
+ obj = self.helper.get_object()
+ except KubernetesObjectHelper as exc:
+ raise K8sInventoryException('Error fetching Namespace list: {0}'.format(exc.message))
+ return [namespace.metadata.name for namespace in obj.items]
+
+ def get_pods_for_namespace(self, name, namespace):
+ self.helper.set_model('v1', 'pod_list')
+ try:
+ obj = self.helper.get_object(namespace=namespace)
+ except KubernetesException as exc:
+ raise K8sInventoryException('Error fetching Pod list: {0}'.format(exc.message))
+
+ namespace_pod_group = '{0}_pods'.format(namespace)
+
+ self.inventory.add_group(name)
+ self.inventory.add_group(namespace)
+ self.inventory.add_child(name, namespace)
+ self.inventory.add_group(namespace_pod_group)
+ self.inventory.add_child(namespace, namespace_pod_group)
+ for pod in obj.items:
+ pod_name = pod.metadata.name
+ pod_groups = []
+ pod_labels = {} if not pod.metadata.labels else pod.metadata.labels
+ pod_annotations = {} if not pod.metadata.annotations else pod.metadata.annotations
+
+ if pod.metadata.labels:
+ pod_labels = pod.metadata.labels
+ # create a group for each label_value
+ for key, value in iteritems(pod.metadata.labels):
+ group_name = '{0}_{1}'.format(key, value)
+ if group_name not in pod_groups:
+ pod_groups.append(group_name)
+ self.inventory.add_group(group_name)
+
+ for container in pod.status.container_statuses:
+ # add each pod_container to the namespace group, and to each label_value group
+ container_name = '{0}_{1}'.format(pod.metadata.name, container.name)
+ self.inventory.add_host(container_name)
+ self.inventory.add_child(namespace_pod_group, container_name)
+ if pod_groups:
+ for group in pod_groups:
+ self.inventory.add_child(group, container_name)
+
+ # Add hostvars
+ self.inventory.set_variable(container_name, 'object_type', 'pod')
+ self.inventory.set_variable(container_name, 'labels', pod_labels)
+ self.inventory.set_variable(container_name, 'annotations', pod_annotations)
+ self.inventory.set_variable(container_name, 'cluster_name', pod.metadata.cluster_name)
+ self.inventory.set_variable(container_name, 'pod_node_name', pod.spec.node_name)
+ self.inventory.set_variable(container_name, 'pod_name', pod.spec.node_name)
+ self.inventory.set_variable(container_name, 'pod_host_ip', pod.status.host_ip)
+ self.inventory.set_variable(container_name, 'pod_phase', pod.status.phase)
+ self.inventory.set_variable(container_name, 'pod_ip', pod.status.pod_ip)
+ self.inventory.set_variable(container_name, 'pod_self_link', pod.metadata.self_link)
+ self.inventory.set_variable(container_name, 'pod_resource_version', pod.metadata.resource_version)
+ self.inventory.set_variable(container_name, 'pod_uid', pod.metadata.uid)
+ self.inventory.set_variable(container_name, 'container_name', container.image)
+ self.inventory.set_variable(container_name, 'container_image', container.image)
+ if container.state.running:
+ self.inventory.set_variable(container_name, 'container_state', 'Running')
+ if container.state.terminated:
+ self.inventory.set_variable(container_name, 'container_state', 'Terminated')
+ if container.state.waiting:
+ self.inventory.set_variable(container_name, 'container_state', 'Waiting')
+ self.inventory.set_variable(container_name, 'container_ready', container.ready)
+ self.inventory.set_variable(container_name, 'ansible_connection', self.transport)
+ self.inventory.set_variable(container_name, 'ansible_{0}_pod'.format(self.transport),
+ pod_name)
+ self.inventory.set_variable(container_name, 'ansible_{0}_container'.format(self.transport),
+ container.name)
+
+ def get_services_for_namespace(self, name, namespace):
+ self.helper.set_model('v1', 'service_list')
+ try:
+ obj = self.helper.get_object(namespace=namespace)
+ except KubernetesException as exc:
+ raise K8sInventoryException('Error fetching Service list: {0}'.format(exc.message))
+
+ namespace_service_group = '{0}_services'.format(namespace)
+
+ self.inventory.add_group(name)
+ self.inventory.add_group(namespace)
+ self.inventory.add_child(name, namespace)
+ self.inventory.add_group(namespace_service_group)
+ self.inventory.add_child(namespace, namespace_service_group)
+ for service in obj.items:
+ service_name = service.metadata.name
+ service_labels = {} if not service.metadata.labels else service.metadata.labels
+ service_annotations = {} if not service.metadata.annotations else service.metadata.annotations
+
+ self.inventory.add_host(service_name)
+
+ if service.metadata.labels:
+ # create a group for each label_value
+ for key, value in iteritems(service.metadata.labels):
+ group_name = '{0}_{1}'.format(key, value)
+ self.inventory.add_group(group_name)
+ self.inventory.add_child(group_name, service_name)
+
+ self.inventory.add_child(namespace_service_group, service_name)
+
+ ports = [{'name': port.name,
+ 'port': port.port,
+ 'protocol': port.protocol,
+ 'targetPort': port.target_port} for port in service.spec.ports]
+
+ # add hostvars
+ self.inventory.set_variable(service_name, 'object_type', 'service')
+ self.inventory.set_variable(service_name, 'labels', service_labels)
+ self.inventory.set_variable(service_name, 'annotations', service_annotations)
+ self.inventory.set_variable(service_name, 'cluster_name', service.metadata.cluster_name)
+ self.inventory.set_variable(service_name, 'ports', ports)
+ self.inventory.set_variable(service_name, 'type', service.spec.type)
+ self.inventory.set_variable(service_name, 'self_link', service.metadata.self_link)
+ self.inventory.set_variable(service_name, 'resource_version', service.metadata.resource_version)
+ self.inventory.set_variable(service_name, 'uid', service.metadata.uid)
+
+ if service.spec.external_traffic_policy:
+ self.inventory.set_variable(service_name, 'external_traffic_policy',
+ service.spec.external_traffic_policy)
+ if hasattr(service.spec, 'external_ips') and service.spec.external_ips:
+ self.inventory.set_variable(service_name, 'external_ips', service.spec.external_ips)
+
+ if service.spec.external_name:
+ self.inventory.set_variable(service_name, 'external_name', service.spec.external_name)
+
+ if service.spec.health_check_node_port:
+ self.inventory.set_variable(service_name, 'health_check_node_port',
+ service.spec.health_check_node_port)
+ if service.spec.load_balancer_ip:
+ self.inventory.set_variable(service_name, 'load_balancer_ip',
+ service.spec.load_balancer_ip)
+ if service.spec.selector:
+ self.inventory.set_variable(service_name, 'selector', service.spec.selector)
+
+ if hasattr(service.status.load_balancer, 'ingress') and service.status.load_balancer.ingress:
+ load_balancer = [{'hostname': ingress.hostname,
+ 'ip': ingress.ip} for ingress in service.status.load_balancer.ingress]
+ self.inventory.set_variable(service_name, 'load_balancer', load_balancer)
+
+
+class OpenShiftInventoryHelper(K8sInventoryHelper):
+ helper = None
+ transport = 'oc'
+
+ def fetch_objects(self, connections):
+ super(OpenShiftInventoryHelper, self).fetch_objects(connections)
+ self.helper = self.get_helper('v1', 'namespace_list')
+
+ if connections:
+ for connection in connections:
+ self.authenticate(connection)
+ name = connection.get('name', self.get_default_host_name(self.helper.api_client.host))
+ if connection.get('namespaces'):
+ namespaces = connection['namespaces']
+ else:
+ namespaces = self.get_available_namespaces()
+ for namespace in namespaces:
+ self.get_routes_for_namespace(name, namespace)
+ else:
+ name = self.get_default_host_name(self.helper.api_client.host)
+ namespaces = self.get_available_namespaces()
+ for namespace in namespaces:
+ self.get_routes_for_namespace(name, namespace)
+
+ def get_helper(self, api_version, kind):
+ try:
+ helper = OpenShiftObjectHelper(api_version=api_version, kind=kind, debug=False)
+ helper.get_model(api_version, kind)
+ return helper
+ except KubernetesException as exc:
+ raise K8sInventoryException('Error initializing object helper: {0}'.format(exc.message))
+
+ def get_routes_for_namespace(self, name, namespace):
+ self.helper.set_model('v1', 'route_list')
+ try:
+ obj = self.helper.get_object(namespace=namespace)
+ except KubernetesException as exc:
+ raise K8sInventoryException('Error fetching Routes list: {0}'.format(exc.message))
+
+ namespace_routes_group = '{0}_routes'.format(namespace)
+
+ self.inventory.add_group(name)
+ self.inventory.add_group(namespace)
+ self.inventory.add_child(name, namespace)
+ self.inventory.add_group(namespace_routes_group)
+ self.inventory.add_child(namespace, namespace_routes_group)
+ for route in obj.items:
+ route_name = route.metadata.name
+ route_labels = {} if not route.metadata.labels else route.metadata.labels
+ route_annotations = {} if not route.metadata.annotations else route.metadata.annotations
+
+ self.inventory.add_host(route_name)
+
+ if route.metadata.labels:
+ # create a group for each label_value
+ for key, value in iteritems(route.metadata.labels):
+ group_name = '{0}_{1}'.format(key, value)
+ self.inventory.add_group(group_name)
+ self.inventory.add_child(group_name, route_name)
+
+ self.inventory.add_child(namespace_routes_group, route_name)
+
+ # add hostvars
+ self.inventory.set_variable(route_name, 'labels', route_labels)
+ self.inventory.set_variable(route_name, 'annotations', route_annotations)
+ self.inventory.set_variable(route_name, 'cluster_name', route.metadata.cluster_name)
+ self.inventory.set_variable(route_name, 'object_type', 'route')
+ self.inventory.set_variable(route_name, 'self_link', route.metadata.self_link)
+ self.inventory.set_variable(route_name, 'resource_version', route.metadata.resource_version)
+ self.inventory.set_variable(route_name, 'uid', route.metadata.uid)
+
+ if route.spec.host:
+ self.inventory.set_variable(route_name, 'host', route.spec.host)
+
+ if route.spec.path:
+ self.inventory.set_variable(route_name, 'path', route.spec.path)
+
+ if hasattr(route.spec.port, 'target_port') and route.spec.port.target_port:
+ self.inventory.set_variable(route_name, 'port', route.spec.port)