1#!/usr/local/bin/python3.8
2
3# Copyright: (c) 2017, VEXXHOST, Inc.
4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6DOCUMENTATION = '''
7---
8module: endpoint
9short_description: Manage OpenStack Identity service endpoints
10author: OpenStack Ansible SIG
11description:
12    - Create, update, or delete OpenStack Identity service endpoints. If a
13      service with the same combination of I(service), I(interface) and I(region)
14      exist, the I(url) and I(state) (C(present) or C(absent)) will be updated.
15options:
16   service:
17     description:
18        - Name or id of the service.
19     required: true
20     type: str
21   endpoint_interface:
22     description:
23        - Interface of the service.
24     choices: [admin, public, internal]
25     required: true
26     type: str
27   url:
28     description:
29        - URL of the service.
30     required: true
31     type: str
32   region:
33     description:
34        - Region that the service belongs to. Note that I(region_name) is used for authentication.
35     type: str
36   enabled:
37     description:
38        - Is the service enabled.
39     default: True
40     type: bool
41   state:
42     description:
43       - Should the resource be C(present) or C(absent).
44     choices: [present, absent]
45     default: present
46     type: str
47requirements:
48    - "python >= 3.6"
49    - "openstacksdk >= 0.13.0"
50
51extends_documentation_fragment:
52- openstack.cloud.openstack
53'''
54
55EXAMPLES = '''
56- name: Create a service for glance
57  openstack.cloud.endpoint:
58     cloud: mycloud
59     service: glance
60     endpoint_interface: public
61     url: http://controller:9292
62     region: RegionOne
63     state: present
64
65- name: Delete a service for nova
66  openstack.cloud.endpoint:
67     cloud: mycloud
68     service: nova
69     endpoint_interface: public
70     region: RegionOne
71     state: absent
72'''
73
74RETURN = '''
75endpoint:
76    description: Dictionary describing the endpoint.
77    returned: On success when I(state) is C(present)
78    type: complex
79    contains:
80        id:
81            description: Endpoint ID.
82            type: str
83            sample: 3292f020780b4d5baf27ff7e1d224c44
84        region:
85            description: Region Name.
86            type: str
87            sample: RegionOne
88        service_id:
89            description: Service ID.
90            type: str
91            sample: b91f1318f735494a825a55388ee118f3
92        interface:
93            description: Endpoint Interface.
94            type: str
95            sample: public
96        url:
97            description: Service URL.
98            type: str
99            sample: http://controller:9292
100        enabled:
101            description: Service status.
102            type: bool
103            sample: True
104'''
105
106from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
107
108
109class IdentityEndpointModule(OpenStackModule):
110    argument_spec = dict(
111        service=dict(type='str', required=True),
112        endpoint_interface=dict(type='str', required=True, choices=['admin', 'public', 'internal']),
113        url=dict(type='str', required=True),
114        region=dict(type='str'),
115        enabled=dict(type='bool', default=True),
116        state=dict(type='str', default='present', choices=['absent', 'present']),
117    )
118
119    module_kwargs = dict(
120        supports_check_mode=True
121    )
122
123    def _needs_update(self, endpoint):
124        if endpoint.enabled != self.params['enabled']:
125            return True
126        if endpoint.url != self.params['url']:
127            return True
128        return False
129
130    def _system_state_change(self, endpoint):
131        state = self.params['state']
132        if state == 'absent' and endpoint:
133            return True
134
135        if state == 'present':
136            if endpoint is None:
137                return True
138            return self._needs_update(endpoint)
139
140        return False
141
142    def run(self):
143        service_name_or_id = self.params['service']
144        interface = self.params['endpoint_interface']
145        url = self.params['url']
146        region = self.params['region']
147        enabled = self.params['enabled']
148        state = self.params['state']
149
150        service = self.conn.get_service(service_name_or_id)
151        if service is None and state == 'absent':
152            self.exit_json(changed=False)
153
154        elif service is None and state == 'present':
155            self.fail_json(msg='Service %s does not exist' % service_name_or_id)
156
157        filters = dict(service_id=service.id, interface=interface)
158        if region is not None:
159            filters['region'] = region
160        endpoints = self.conn.search_endpoints(filters=filters)
161
162        if len(endpoints) > 1:
163            self.fail_json(msg='Service %s, interface %s and region %s are '
164                           'not unique' %
165                           (service_name_or_id, interface, region))
166        elif len(endpoints) == 1:
167            endpoint = endpoints[0]
168        else:
169            endpoint = None
170
171        if self.ansible.check_mode:
172            self.exit_json(changed=self._system_state_change(endpoint))
173
174        if state == 'present':
175            if endpoint is None:
176                result = self.conn.create_endpoint(
177                    service_name_or_id=service, url=url, interface=interface,
178                    region=region, enabled=enabled)
179                endpoint = result[0]
180                changed = True
181            else:
182                if self._needs_update(endpoint):
183                    endpoint = self.conn.update_endpoint(
184                        endpoint.id, url=url, enabled=enabled)
185                    changed = True
186                else:
187                    changed = False
188            self.exit_json(changed=changed, endpoint=endpoint)
189
190        elif state == 'absent':
191            if endpoint is None:
192                changed = False
193            else:
194                self.conn.delete_endpoint(endpoint.id)
195                changed = True
196            self.exit_json(changed=changed)
197
198
199def main():
200    module = IdentityEndpointModule()
201    module()
202
203
204if __name__ == '__main__':
205    main()
206