Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lookup plugin for the OpenShift Container Platform #31525

Merged
merged 1 commit into from Nov 7, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
248 changes: 248 additions & 0 deletions lib/ansible/plugins/lookup/openshift.py
@@ -0,0 +1,248 @@
# -*- coding: utf-8 -*-
# (c) 2017, Kenneth D. Evensen <kevensen@redhat.com>

# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

DOCUMENTATION = """
lookup: openshift
version_added: "2.5"
short_description: Returns the JSON definition of an object in OpenShift
description:
- This lookup plugin provides the ability to query an OpenShift Container
- platform cluster for information about objects. This plugin requires
- a valid user or service account token.
options:
kind:
description:
- The kind of OpenShift resource to read (e.g. Project, Service, Pod)
required: True
host:
description:
- The IP address of the host serving the OpenShift API
required: False
default: 127.0.0.1
port:
description:
- The port on which to access the OpenShift API
required: False
default: 8443
token:
description:
- The token to use for authentication against the OpenShift API.
- This can be a user or ServiceAccount token.
required: True
validate_certs:
description:
- Whether or not to validate the TLS certificate of the API.
required: False
default: True
namespace:
description:
- The namespace/project where the object resides.
required: False
resource_name:
description:
- The name of the object to query.
required: False
pretty:
description:
- Whether or not to prettify the output. This is useful for debugging.
required: False
default: False
labelSelector:
description:
- Additional labels to include in the query.
required: False
fieldSelector:
description:
- Specific fields on which to query.
required: False
resourceVersion:
description:
- Query for a specific resource version.
required: False
"""

EXAMPLES = """
- name: Get Project {{ project_name }}
set_fact:
project_fact: "{{ lookup('openshift',
kind='Project',
host=inventory_host,
token=hostvars[inventory_host]['ansible_sa_token'],
resource_name=project_name,
validate_certs=validate_certs) }}"
- name: Get All Service Accounts in a Project
set_fact:
service_fact: "{{ lookup('openshift',
kind='ServiceAccount',
host=inventory_host,
token=hostvars[inventory_host]['ansible_sa_token'],
namespace=project_name,
validate_certs=validate_certs) }}"
"""

RETURN = """
_list:
description:
- An object definition or list of objects definitions returned from OpenShift.
type: dict
"""

import json
from ansible.errors import AnsibleError
from ansible.plugins.lookup import LookupBase
from ansible.module_utils import urls
from ansible.module_utils.six.moves import urllib
from ansible.module_utils.six.moves import urllib_error
from ansible.module_utils.six.moves.urllib.parse import urlencode
from ansible.module_utils._text import to_text
from ansible.module_utils._text import to_native


class OcpQuery(object):
def __init__(self, host, port, token, validate_certs):
self.apis = ['api', 'oapi']
self.token = token
self.validate_certs = validate_certs
self.host = host
self.port = port
self.kinds = {}
bearer = "Bearer " + self.token
self.headers = {"Authorization": bearer}
self.build_facts()

def build_facts(self):

for api in self.apis:
url = "https://{0}:{1}/{2}/v1".format(self.host, self.port, api)
try:
response = urls.open_url(url=url,
headers=self.headers,
validate_certs=self.validate_certs,
method='get')
except urllib_error.HTTPError as error:
try:
body = to_native(error.read())
except AttributeError:
body = ''
raise AnsibleError("OC Query raised exception with code {0} and message {1} against url {2}".format(error.code, body, url))

for resource in json.loads(to_text(response.read(), errors='surrogate_or_strict'))['resources']:
if 'generated' not in resource['name']:
self.kinds[resource['kind']] = \
{'kind': resource['kind'],
'name': resource['name'].split('/')[0],
'namespaced': resource['namespaced'],
'api': api,
'version': 'v1',
'baseurl': url
}

def url(self, kind=None, namespace=None, resource_name=None, pretty=False, labelSelector=None, fieldSelector=None, resourceVersion=None):
first_param = True

url = [self.kinds[kind]['baseurl']]
if self.kinds[kind]['namespaced'] is True:
url.append('/namespaces/')
if namespace is None:
raise AnsibleError('Kind %s requires a namespace.'
' None provided' % kind)
url.append(namespace)

url.append('/' + self.kinds[kind]['name'])

if resource_name is not None:
url.append('/' + resource_name)

if pretty:
url.append('?pretty')
first_param = False

if labelSelector is not None:
if first_param:
url.append('?')
else:
url.append('&')

url.append(urlencode({'labelSelector': labelSelector}))
first_param = False

if fieldSelector is not None:
if first_param:
url.append('?')
else:
url.append('&')

url.append(urlencode({'fieldSelector': fieldSelector}))
first_param = False

if resourceVersion is not None:
if first_param:
url.append('?')
else:
url.append('&')

url.append(urlencode({'resourceVersion': resourceVersion}))
first_param = False

return "".join(url)

def query(self, kind=None, namespace=None, resource_name=None, pretty=False, labelSelector=None, fieldSelector=None, resourceVersion=None):
url = self.url(kind=kind,
namespace=namespace,
resource_name=resource_name,
pretty=pretty,
labelSelector=labelSelector,
fieldSelector=fieldSelector,
resourceVersion=resourceVersion)

try:
response = urls.open_url(url=url,
headers=self.headers,
validate_certs=self.validate_certs,
method='get')
except urllib_error.HTTPError as error:
try:
body = to_native(error.read())
except AttributeError:
body = ''
raise AnsibleError("OC Query raised exception with code {0} and message {1} against url {2}".format(error.code, body, url))

return json.loads(to_text(response.read(), errors='surrogate_or_strict'))


class LookupModule(LookupBase):
def run(self, terms, variables=None, **kwargs):

host = kwargs.get('host', '127.0.0.1')
port = kwargs.get('port', '8443')
validate_certs = kwargs.get('validate_certs', True)
token = kwargs.get('token', None)

namespace = kwargs.get('namespace', None)
resource_name = kwargs.get('resource_name', None)
pretty = kwargs.get('pretty', False)
label_selector = kwargs.get('labelSelector', None)
field_selector = kwargs.get('fieldSelector', None)
resource_version = kwargs.get('resourceVersion', None)
resource_kind = kwargs.get('kind', None)

ocp = OcpQuery(host, port, token, validate_certs)

search_response = ocp.query(kind=resource_kind,
namespace=namespace,
resource_name=resource_name,
pretty=pretty,
labelSelector=label_selector,
fieldSelector=field_selector,
resourceVersion=resource_version)
if search_response is not None and "items" in search_response:
search_response['item_list'] = search_response.pop('items')

values = [search_response]

return values