Browse Source

Remove vendored os_router ansible module

The module was vendored for Pike because the ansible 2.3 module had
a bug which broke idempotency and would constantly attach/deattach
the router each playbook run, breaking the network.

Since OSA is updated to queens, the vendored module is no longer
needed as ansible 2.4 ships with the fixed module.

Change-Id: I8594dda75900af43842a044de2055e22ef0d8283
changes/06/643706/2
Logan V 3 months ago
parent
commit
0cec8f1551
2 changed files with 0 additions and 428 deletions
  1. 0
    4
      network_bootstrap/bootstrap-neutron.yml
  2. 0
    424
      network_bootstrap/library/os_router.py

+ 0
- 4
network_bootstrap/bootstrap-neutron.yml View File

@@ -80,10 +80,6 @@
80 80
         ipv6_address_mode: dhcpv6-stateless
81 81
         ipv6_ra_mode: dhcpv6-stateless
82 82
 
83
-    # NOTE(logan): uses vendored os_router module from stable-2.4
84
-    # since the os_router module in 2.3 (pike) has broken idempotency
85
-    # and causes needless interface attach/deattaches.
86
-    # remove vendored module in queens
87 83
     - name: Create flat uplink router
88 84
       os_router:
89 85
         cloud: default

+ 0
- 424
network_bootstrap/library/os_router.py View File

@@ -1,424 +0,0 @@
1
-#!/usr/bin/python
2
-#
3
-# This module is free software: you can redistribute it and/or modify
4
-# it under the terms of the GNU General Public License as published by
5
-# the Free Software Foundation, either version 3 of the License, or
6
-# (at your option) any later version.
7
-#
8
-# This software is distributed in the hope that it will be useful,
9
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
-# GNU General Public License for more details.
12
-#
13
-# You should have received a copy of the GNU General Public License
14
-# along with this software.  If not, see <http://www.gnu.org/licenses/>.
15
-
16
-ANSIBLE_METADATA = {'metadata_version': '1.1',
17
-                    'status': ['preview'],
18
-                    'supported_by': 'community'}
19
-
20
-
21
-DOCUMENTATION = '''
22
----
23
-module: os_router
24
-short_description: Create or delete routers from OpenStack
25
-extends_documentation_fragment: openstack
26
-version_added: "2.0"
27
-author: "David Shrewsbury (@Shrews)"
28
-description:
29
-   - Create or Delete routers from OpenStack. Although Neutron allows
30
-     routers to share the same name, this module enforces name uniqueness
31
-     to be more user friendly.
32
-options:
33
-   state:
34
-     description:
35
-        - Indicate desired state of the resource
36
-     choices: ['present', 'absent']
37
-     default: present
38
-   name:
39
-     description:
40
-        - Name to be give to the router
41
-     required: true
42
-   admin_state_up:
43
-     description:
44
-        - Desired admin state of the created or existing router.
45
-     required: false
46
-     default: true
47
-   enable_snat:
48
-     description:
49
-        - Enable Source NAT (SNAT) attribute.
50
-     required: false
51
-     default: true
52
-   network:
53
-     description:
54
-        - Unique name or ID of the external gateway network.
55
-        - required I(interfaces) or I(enable_snat) are provided.
56
-     required: false
57
-     default: None
58
-   project:
59
-     description:
60
-        - Unique name or ID of the project.
61
-     required: false
62
-     default: None
63
-     version_added: "2.2"
64
-   external_fixed_ips:
65
-     description:
66
-        - The IP address parameters for the external gateway network. Each
67
-          is a dictionary with the subnet name or ID (subnet) and the IP
68
-          address to assign on the subnet (ip). If no IP is specified,
69
-          one is automatically assigned from that subnet.
70
-     required: false
71
-     default: None
72
-   interfaces:
73
-     description:
74
-        - List of subnets to attach to the router internal interface.
75
-     required: false
76
-     default: None
77
-   availability_zone:
78
-     description:
79
-       - Ignored. Present for backwards compatibility
80
-     required: false
81
-requirements: ["shade"]
82
-'''
83
-
84
-EXAMPLES = '''
85
-# Create a simple router, not attached to a gateway or subnets.
86
-- os_router:
87
-    cloud: mycloud
88
-    state: present
89
-    name: simple_router
90
-
91
-# Create a simple router, not attached to a gateway or subnets for a given project.
92
-- os_router:
93
-    cloud: mycloud
94
-    state: present
95
-    name: simple_router
96
-    project: myproj
97
-
98
-# Creates a router attached to ext_network1 on an IPv4 subnet and one
99
-# internal subnet interface.
100
-- os_router:
101
-    cloud: mycloud
102
-    state: present
103
-    name: router1
104
-    network: ext_network1
105
-    external_fixed_ips:
106
-      - subnet: public-subnet
107
-        ip: 172.24.4.2
108
-    interfaces:
109
-      - private-subnet
110
-
111
-# Update existing router1 external gateway to include the IPv6 subnet.
112
-# Note that since 'interfaces' is not provided, any existing internal
113
-# interfaces on an existing router will be left intact.
114
-- os_router:
115
-    cloud: mycloud
116
-    state: present
117
-    name: router1
118
-    network: ext_network1
119
-    external_fixed_ips:
120
-      - subnet: public-subnet
121
-        ip: 172.24.4.2
122
-      - subnet: ipv6-public-subnet
123
-        ip: 2001:db8::3
124
-
125
-# Delete router1
126
-- os_router:
127
-    cloud: mycloud
128
-    state: absent
129
-    name: router1
130
-'''
131
-
132
-RETURN = '''
133
-router:
134
-    description: Dictionary describing the router.
135
-    returned: On success when I(state) is 'present'
136
-    type: complex
137
-    contains:
138
-        id:
139
-            description: Router ID.
140
-            type: string
141
-            sample: "474acfe5-be34-494c-b339-50f06aa143e4"
142
-        name:
143
-            description: Router name.
144
-            type: string
145
-            sample: "router1"
146
-        admin_state_up:
147
-            description: Administrative state of the router.
148
-            type: boolean
149
-            sample: true
150
-        status:
151
-            description: The router status.
152
-            type: string
153
-            sample: "ACTIVE"
154
-        tenant_id:
155
-            description: The tenant ID.
156
-            type: string
157
-            sample: "861174b82b43463c9edc5202aadc60ef"
158
-        external_gateway_info:
159
-            description: The external gateway parameters.
160
-            type: dictionary
161
-            sample: {
162
-                      "enable_snat": true,
163
-                      "external_fixed_ips": [
164
-                         {
165
-                           "ip_address": "10.6.6.99",
166
-                           "subnet_id": "4272cb52-a456-4c20-8f3c-c26024ecfa81"
167
-                         }
168
-                       ]
169
-                    }
170
-        routes:
171
-            description: The extra routes configuration for L3 router.
172
-            type: list
173
-'''
174
-
175
-try:
176
-    import shade
177
-    HAS_SHADE = True
178
-except ImportError:
179
-    HAS_SHADE = False
180
-
181
-from distutils.version import StrictVersion
182
-
183
-ROUTER_INTERFACE_OWNERS = set([
184
-    'network:router_interface',
185
-    'network:router_interface_distributed',
186
-    'network:ha_router_replicated_interface'
187
-])
188
-
189
-
190
-def _router_internal_interfaces(cloud, router):
191
-    for port in cloud.list_router_interfaces(router, 'internal'):
192
-        if port['device_owner'] in ROUTER_INTERFACE_OWNERS:
193
-            yield port
194
-
195
-
196
-def _needs_update(cloud, module, router, network, internal_subnet_ids):
197
-    """Decide if the given router needs an update.
198
-    """
199
-    if router['admin_state_up'] != module.params['admin_state_up']:
200
-        return True
201
-    if router['external_gateway_info']:
202
-        if router['external_gateway_info'].get('enable_snat', True) != module.params['enable_snat']:
203
-            return True
204
-    if network:
205
-        if not router['external_gateway_info']:
206
-            return True
207
-        elif router['external_gateway_info']['network_id'] != network['id']:
208
-            return True
209
-
210
-    # check external interfaces
211
-    if module.params['external_fixed_ips']:
212
-        for new_iface in module.params['external_fixed_ips']:
213
-            subnet = cloud.get_subnet(new_iface['subnet'])
214
-            exists = False
215
-
216
-            # compare the requested interface with existing, looking for an existing match
217
-            for existing_iface in router['external_gateway_info']['external_fixed_ips']:
218
-                if existing_iface['subnet_id'] == subnet['id']:
219
-                    if 'ip' in new_iface:
220
-                        if existing_iface['ip_address'] == new_iface['ip']:
221
-                            # both subnet id and ip address match
222
-                            exists = True
223
-                            break
224
-                    else:
225
-                        # only the subnet was given, so ip doesn't matter
226
-                        exists = True
227
-                        break
228
-
229
-            # this interface isn't present on the existing router
230
-            if not exists:
231
-                return True
232
-
233
-    # check internal interfaces
234
-    if module.params['interfaces']:
235
-        existing_subnet_ids = []
236
-        for port in _router_internal_interfaces(cloud, router):
237
-            if 'fixed_ips' in port:
238
-                for fixed_ip in port['fixed_ips']:
239
-                    existing_subnet_ids.append(fixed_ip['subnet_id'])
240
-
241
-        if set(internal_subnet_ids) != set(existing_subnet_ids):
242
-            return True
243
-
244
-    return False
245
-
246
-
247
-def _system_state_change(cloud, module, router, network, internal_ids):
248
-    """Check if the system state would be changed."""
249
-    state = module.params['state']
250
-    if state == 'absent' and router:
251
-        return True
252
-    if state == 'present':
253
-        if not router:
254
-            return True
255
-        return _needs_update(cloud, module, router, network, internal_ids)
256
-    return False
257
-
258
-
259
-def _build_kwargs(cloud, module, router, network):
260
-    kwargs = {
261
-        'admin_state_up': module.params['admin_state_up'],
262
-    }
263
-
264
-    if router:
265
-        kwargs['name_or_id'] = router['id']
266
-    else:
267
-        kwargs['name'] = module.params['name']
268
-
269
-    if network:
270
-        kwargs['ext_gateway_net_id'] = network['id']
271
-        # can't send enable_snat unless we have a network
272
-        kwargs['enable_snat'] = module.params['enable_snat']
273
-
274
-    if module.params['external_fixed_ips']:
275
-        kwargs['ext_fixed_ips'] = []
276
-        for iface in module.params['external_fixed_ips']:
277
-            subnet = cloud.get_subnet(iface['subnet'])
278
-            d = {'subnet_id': subnet['id']}
279
-            if 'ip' in iface:
280
-                d['ip_address'] = iface['ip']
281
-            kwargs['ext_fixed_ips'].append(d)
282
-
283
-    return kwargs
284
-
285
-
286
-def _validate_subnets(module, cloud):
287
-    external_subnet_ids = []
288
-    internal_subnet_ids = []
289
-    if module.params['external_fixed_ips']:
290
-        for iface in module.params['external_fixed_ips']:
291
-            subnet = cloud.get_subnet(iface['subnet'])
292
-            if not subnet:
293
-                module.fail_json(msg='subnet %s not found' % iface['subnet'])
294
-            external_subnet_ids.append(subnet['id'])
295
-
296
-    if module.params['interfaces']:
297
-        for iface in module.params['interfaces']:
298
-            subnet = cloud.get_subnet(iface)
299
-            if not subnet:
300
-                module.fail_json(msg='subnet %s not found' % iface)
301
-            internal_subnet_ids.append(subnet['id'])
302
-
303
-    return external_subnet_ids, internal_subnet_ids
304
-
305
-
306
-def main():
307
-    argument_spec = openstack_full_argument_spec(
308
-        state=dict(default='present', choices=['absent', 'present']),
309
-        name=dict(required=True),
310
-        admin_state_up=dict(type='bool', default=True),
311
-        enable_snat=dict(type='bool', default=True),
312
-        network=dict(default=None),
313
-        interfaces=dict(type='list', default=None),
314
-        external_fixed_ips=dict(type='list', default=None),
315
-        project=dict(default=None)
316
-    )
317
-
318
-    module_kwargs = openstack_module_kwargs()
319
-    module = AnsibleModule(argument_spec,
320
-                           supports_check_mode=True,
321
-                           **module_kwargs)
322
-
323
-    if not HAS_SHADE:
324
-        module.fail_json(msg='shade is required for this module')
325
-
326
-    if (module.params['project'] and
327
-            StrictVersion(shade.__version__) <= StrictVersion('1.9.0')):
328
-        module.fail_json(msg="To utilize project, the installed version of"
329
-                             "the shade library MUST be > 1.9.0")
330
-
331
-    state = module.params['state']
332
-    name = module.params['name']
333
-    network = module.params['network']
334
-    project = module.params['project']
335
-
336
-    if module.params['external_fixed_ips'] and not network:
337
-        module.fail_json(msg='network is required when supplying external_fixed_ips')
338
-
339
-    try:
340
-        cloud = shade.openstack_cloud(**module.params)
341
-        if project is not None:
342
-            proj = cloud.get_project(project)
343
-            if proj is None:
344
-                module.fail_json(msg='Project %s could not be found' % project)
345
-            project_id = proj['id']
346
-            filters = {'tenant_id': project_id}
347
-        else:
348
-            project_id = None
349
-            filters = None
350
-
351
-        router = cloud.get_router(name, filters=filters)
352
-        net = None
353
-        if network:
354
-            net = cloud.get_network(network)
355
-            if not net:
356
-                module.fail_json(msg='network %s not found' % network)
357
-
358
-        # Validate and cache the subnet IDs so we can avoid duplicate checks
359
-        # and expensive API calls.
360
-        external_ids, internal_ids = _validate_subnets(module, cloud)
361
-
362
-        if module.check_mode:
363
-            module.exit_json(
364
-                changed=_system_state_change(cloud, module, router, net, internal_ids)
365
-            )
366
-
367
-        if state == 'present':
368
-            changed = False
369
-
370
-            if not router:
371
-                kwargs = _build_kwargs(cloud, module, router, net)
372
-                if project_id:
373
-                    kwargs['project_id'] = project_id
374
-                router = cloud.create_router(**kwargs)
375
-                for internal_subnet_id in internal_ids:
376
-                    cloud.add_router_interface(router, subnet_id=internal_subnet_id)
377
-                changed = True
378
-            else:
379
-                if _needs_update(cloud, module, router, net, internal_ids):
380
-                    kwargs = _build_kwargs(cloud, module, router, net)
381
-                    updated_router = cloud.update_router(**kwargs)
382
-
383
-                    # Protect against update_router() not actually
384
-                    # updating the router.
385
-                    if not updated_router:
386
-                        changed = False
387
-
388
-                    # On a router update, if any internal interfaces were supplied,
389
-                    # just detach all existing internal interfaces and attach the new.
390
-                    elif internal_ids:
391
-                        router = updated_router
392
-                        ports = _router_internal_interfaces(cloud, router)
393
-                        for port in ports:
394
-                            cloud.remove_router_interface(router, port_id=port['id'])
395
-                        for internal_subnet_id in internal_ids:
396
-                            cloud.add_router_interface(router, subnet_id=internal_subnet_id)
397
-                        changed = True
398
-
399
-            module.exit_json(changed=changed,
400
-                             router=router,
401
-                             id=router['id'])
402
-
403
-        elif state == 'absent':
404
-            if not router:
405
-                module.exit_json(changed=False)
406
-            else:
407
-                # We need to detach all internal interfaces on a router before
408
-                # we will be allowed to delete it.
409
-                ports = _router_internal_interfaces(cloud, router)
410
-                router_id = router['id']
411
-                for port in ports:
412
-                    cloud.remove_router_interface(router, port_id=port['id'])
413
-                cloud.delete_router(router_id)
414
-                module.exit_json(changed=True)
415
-
416
-    except shade.OpenStackCloudException as e:
417
-        module.fail_json(msg=str(e))
418
-
419
-
420
-# this is magic, see lib/ansible/module_common.py
421
-from ansible.module_utils.basic import *
422
-from ansible.module_utils.openstack import *
423
-if __name__ == '__main__':
424
-    main()

Loading…
Cancel
Save