summaryrefslogtreecommitdiffstats
path: root/tools/scripts/pk_to_named_url.py
blob: 9265f60c75e0d46ee23df14ec493fa39884c9ad4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/usr/bin/env python
import argparse
import urllib.parse

import requests


NAMED_URL_RES_DILIMITER = "--"
NAMED_URL_RES_INNER_DILIMITER = "-"
NAMED_URL_RES_DILIMITER_ENCODE = "%2D"
URL_PATH_RESERVED_CHARSET = {}
for c in ';/?:@=&[]':
    URL_PATH_RESERVED_CHARSET[c] = urllib.parse.quote(c, safe='')


def _get_named_url_graph(url, auth):
    """Get the graph data structure AWX used to manage all named URLs.

    Args:
        url: String representing the URL of tower configuration endpoint where
            to fetch graph information.
        auth: Tuple of username + password to authenticate connection to AWX.

    Return:
        A dict of graph nodes that in ensembly represent the graph structure. Each
        node is represented as a dict of 'fields' and 'adj_list'.

    Raises:
        N/A
    """
    r = requests.get(url, auth=auth, verify=False)
    ret = r.json()['NAMED_URL_GRAPH_NODES']
    return ret


def _encode_uri(text):
    """Properly encode input text to make it satisfy named URL convention.

    Args:
        text: the original string to be encoded.

    Return:
        The encoded string

    Raises:
        N/A
    """
    for c in URL_PATH_RESERVED_CHARSET:
        if c in text:
            text = text.replace(c, URL_PATH_RESERVED_CHARSET[c])
    text = text.replace(NAMED_URL_RES_INNER_DILIMITER, '[%s]' % NAMED_URL_RES_INNER_DILIMITER)
    return text


def _generate_identifier_component(response, fields):
    """Generate an individual component of named URL identifier.

    Args:
        response: JSON containing the details of a particular resource object.
        fields: name of resource object fields needed to generate a named URL
            identifier component.

    Return:
        A string representing generated identifier component.

    Raises:
        N/A
    """
    ret = []
    for field_name in fields:
        ret.append(_encode_uri(response[field_name]))
    return NAMED_URL_RES_INNER_DILIMITER.join(ret)


def _get_named_url_identifier(url, named_url_graph, resource, tower_host, auth, ret):
    """DFS the named URL graph structure to generate identifier for a resource object.

    Args:
        url: A string used to access a particular resource object to generate identifier
            component from.
        named_url_graph: The graph structure used to DFS against.
        resource: Key name of the current graph node.
        tower_host: String representing the host name of AWX backend.
        auth: Tuple of username + password to authenticate connection to AWX.
        ret: list of strings storing components that would later be joined into
            the final named URL identifier.

    Return:
        None. Note the actual outcome is stored in argument ret due to the recursive
        nature of this function.

    Raises:
    """
    r = requests.get(url, auth=auth, verify=False).json()
    ret.append(_generate_identifier_component(r, named_url_graph[resource]['fields']))
    for next_ in named_url_graph[resource]['adj_list']:
        next_fk, next_res = tuple(next_)
        if next_fk in r['related']:
            _get_named_url_identifier(tower_host.strip('/') + r['related'][next_fk], named_url_graph, next_res, tower_host, auth, ret)
        else:
            ret.append('')


def main(username=None, password=None, tower_host=None, resource=None, pk=None):
    """Main function for generating and printing named URL of a resource object given its pk.

    Args:
        username: String representing the username needed to authenticating AWX.
        password: String representing the password needed to authenticating AWX.
        tower_host: String representing the host name of AWX backend.
        resource: REST API name of a specific resource, e.g. name for resource inventory
            is 'inventories'.
        pk: Primary key of the resource object whose named URL will be derived.

    Returns:
        None

    Raises:
        N/A
    """
    start_url = '%s/api/v2/%s/%s/' % (tower_host.strip('/'), resource.strip('/'), pk)
    conf_url = '%s/api/v2/settings/named-url/' % tower_host.strip('/')
    auth = (username, password)
    named_url_graph = _get_named_url_graph(conf_url, auth)
    named_url_identifier = []
    _get_named_url_identifier(start_url, named_url_graph, resource, tower_host, auth, named_url_identifier)
    print('%s/api/v2/%s/%s/' % (tower_host.strip('/'), resource.strip('/'), NAMED_URL_RES_DILIMITER.join(named_url_identifier)))


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--username', type=str, required=True, help='Name of the user for making requests', dest='username', metavar='STR')
    parser.add_argument('--password', type=str, required=True, help='Password of the user for making requests', dest='password', metavar='STR')
    parser.add_argument('--tower-host', type=str, required=True, help='API host name, like "http://127.0.0.1"', dest='tower_host', metavar='STR')
    parser.add_argument('--resource', type=str, required=True, help='Name of the resource in REST endpoints', dest='resource', metavar='STR')
    parser.add_argument('--pk', type=int, required=True, help='Primary key of resource object whose named URL will be derived', dest='pk', metavar='INT')
    main(**vars(parser.parse_args()))