diff --git a/lib/ansible/modules/cloud/openstack/os_router.py b/lib/ansible/modules/cloud/openstack/os_router.py index b7583388d43..760ea3133ef 100644 --- a/lib/ansible/modules/cloud/openstack/os_router.py +++ b/lib/ansible/modules/cloud/openstack/os_router.py @@ -65,7 +65,17 @@ options: default: None interfaces: description: - - List of subnets to attach to the router internal interface. + - List of subnets to attach to the router internal interface. Default + gateway associated with the subnet will be automatically attached + with the router's internal interface. + In order to provide an ip address different from the default + gateway,parameters are passed as dictionary with keys as network + name or ID(net), subnet name or ID (subnet) and the IP of + port (portip) from the network. + User defined portip is often required when a multiple router need + to be connected to a single subnet for which the default gateway has + been already used. + required: false default: None availability_zone: @@ -102,6 +112,45 @@ EXAMPLES = ''' interfaces: - private-subnet +# Create another router with two internal subnet interfaces.One with user defined port +# ip and another with default gateway. +- os_router: + cloud: mycloud + state: present + name: router2 + network: ext_network1 + interfaces: + - net: private-net + subnet: private-subnet + portip: 10.1.1.10 + - project-subnet + +# Create another router with two internal subnet interface.One with user defined port +# ip and and another with default gateway. +- os_router: + cloud: mycloud + state: present + name: router2 + network: ext_network1 + interfaces: + - net: private-net + subnet: private-subnet + portip: 10.1.1.10 + - project-subnet + +# Create another router with two internal subnet interface. one with user defined port +# ip and and another with default gateway. +- os_router: + cloud: mycloud + state: present + name: router2 + network: ext_network1 + interfaces: + - net: private-net + subnet: private-subnet + portip: 10.1.1.10 + - project-subnet + # Update existing router1 external gateway to include the IPv6 subnet. # Note that since 'interfaces' is not provided, any existing internal # interfaces on an existing router will be left intact. @@ -191,7 +240,7 @@ def _router_internal_interfaces(cloud, router): yield port -def _needs_update(cloud, module, router, network, internal_subnet_ids): +def _needs_update(cloud, module, router, network, internal_subnet_ids, internal_port_ids): """Decide if the given router needs an update. """ if router['admin_state_up'] != module.params['admin_state_up']: @@ -236,13 +285,22 @@ def _needs_update(cloud, module, router, network, internal_subnet_ids): for fixed_ip in port['fixed_ips']: existing_subnet_ids.append(fixed_ip['subnet_id']) + for iface in module.params['interfaces']: + if isinstance(iface, dict): + for p_id in internal_port_ids: + p = cloud.get_port(name_or_id=p_id) + if 'fixed_ips' in p: + for fip in p['fixed_ips']: + internal_subnet_ids.append(fip['subnet_id']) + if set(internal_subnet_ids) != set(existing_subnet_ids): + internal_subnet_ids = [] return True return False -def _system_state_change(cloud, module, router, network, internal_ids): +def _system_state_change(cloud, module, router, network, internal_ids, internal_portids): """Check if the system state would be changed.""" state = module.params['state'] if state == 'absent' and router: @@ -250,7 +308,7 @@ def _system_state_change(cloud, module, router, network, internal_ids): if state == 'present': if not router: return True - return _needs_update(cloud, module, router, network, internal_ids) + return _needs_update(cloud, module, router, network, internal_ids, internal_portids) return False @@ -284,6 +342,9 @@ def _build_kwargs(cloud, module, router, network): def _validate_subnets(module, cloud): external_subnet_ids = [] internal_subnet_ids = [] + internal_port_ids = [] + existing_port_ips = [] + existing_port_ids = [] if module.params['external_fixed_ips']: for iface in module.params['external_fixed_ips']: subnet = cloud.get_subnet(iface['subnet']) @@ -293,12 +354,34 @@ def _validate_subnets(module, cloud): if module.params['interfaces']: for iface in module.params['interfaces']: - subnet = cloud.get_subnet(iface) - if not subnet: - module.fail_json(msg='subnet %s not found' % iface) - internal_subnet_ids.append(subnet['id']) - - return external_subnet_ids, internal_subnet_ids + if isinstance(iface, str): + subnet = cloud.get_subnet(iface) + if not subnet: + module.fail_json(msg='subnet %s not found' % iface) + internal_subnet_ids.append(subnet['id']) + elif isinstance(iface, dict): + subnet = cloud.get_subnet(iface['subnet']) + if not subnet: + module.fail_json(msg='subnet %s not found' % iface['subnet']) + net = cloud.get_network(iface['net']) + if not net: + module.fail_json(msg='net %s not found' % iface['net']) + if "portip" not in iface: + internal_subnet_ids.append(subnet['id']) + elif not iface['portip']: + module.fail_json(msg='put an ip in portip or remove it from list to assign default port to router') + else: + for existing_port in cloud.list_ports(filters={'network_id': net.id}): + for fixed_ip in existing_port['fixed_ips']: + if iface['portip'] == fixed_ip['ip_address']: + internal_port_ids.append(existing_port.id) + existing_port_ips.append(fixed_ip['ip_address']) + if iface['portip'] not in existing_port_ips: + p = cloud.create_port(network_id=net.id, fixed_ips=[{'ip_address': iface['portip'], 'subnet_id': subnet.id}]) + if p: + internal_port_ids.append(p.id) + + return external_subnet_ids, internal_subnet_ids, internal_port_ids def main(): @@ -355,11 +438,10 @@ def main(): # Validate and cache the subnet IDs so we can avoid duplicate checks # and expensive API calls. - external_ids, internal_ids = _validate_subnets(module, cloud) - + external_ids, subnet_internal_ids, internal_portids = _validate_subnets(module, cloud) if module.check_mode: module.exit_json( - changed=_system_state_change(cloud, module, router, net, internal_ids) + changed=_system_state_change(cloud, module, router, net, subnet_internal_ids, internal_portids) ) if state == 'present': @@ -370,11 +452,15 @@ def main(): if project_id: kwargs['project_id'] = project_id router = cloud.create_router(**kwargs) - for internal_subnet_id in internal_ids: - cloud.add_router_interface(router, subnet_id=internal_subnet_id) + for int_s_id in subnet_internal_ids: + cloud.add_router_interface(router, subnet_id=int_s_id) + changed = True + # add interface by port id as well + for int_p_id in internal_portids: + cloud.add_router_interface(router, port_id=int_p_id) changed = True else: - if _needs_update(cloud, module, router, net, internal_ids): + if _needs_update(cloud, module, router, net, subnet_internal_ids, internal_portids): kwargs = _build_kwargs(cloud, module, router, net) updated_router = cloud.update_router(**kwargs) @@ -385,13 +471,19 @@ def main(): # On a router update, if any internal interfaces were supplied, # just detach all existing internal interfaces and attach the new. - elif internal_ids: + if internal_portids or subnet_internal_ids: router = updated_router ports = _router_internal_interfaces(cloud, router) for port in ports: cloud.remove_router_interface(router, port_id=port['id']) - for internal_subnet_id in internal_ids: - cloud.add_router_interface(router, subnet_id=internal_subnet_id) + if internal_portids: + external_ids, subnet_internal_ids, internal_portids = _validate_subnets(module, cloud) + for int_p_id in internal_portids: + cloud.add_router_interface(router, port_id=int_p_id) + changed = True + if subnet_internal_ids: + for s_id in subnet_internal_ids: + cloud.add_router_interface(router, subnet_id=s_id) changed = True module.exit_json(changed=changed,