From ff0fc73d64cd2467246435097bf25416e4e1cc7e Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Tue, 31 Mar 2015 16:37:07 -0400 Subject: Add OpenStack Floating IP Module Also deprecate the two old quantum floating ip modules. --- cloud/openstack/_quantum_floating_ip.py | 290 ++++++++++++++++++++++ cloud/openstack/_quantum_floating_ip_associate.py | 229 +++++++++++++++++ cloud/openstack/os_floating_ip.py | 245 ++++++++++++++++++ cloud/openstack/quantum_floating_ip.py | 289 --------------------- cloud/openstack/quantum_floating_ip_associate.py | 228 ----------------- 5 files changed, 764 insertions(+), 517 deletions(-) create mode 100644 cloud/openstack/_quantum_floating_ip.py create mode 100644 cloud/openstack/_quantum_floating_ip_associate.py create mode 100644 cloud/openstack/os_floating_ip.py delete mode 100644 cloud/openstack/quantum_floating_ip.py delete mode 100644 cloud/openstack/quantum_floating_ip_associate.py diff --git a/cloud/openstack/_quantum_floating_ip.py b/cloud/openstack/_quantum_floating_ip.py new file mode 100644 index 00000000..5220d307 --- /dev/null +++ b/cloud/openstack/_quantum_floating_ip.py @@ -0,0 +1,290 @@ +#!/usr/bin/python +#coding: utf-8 -*- + +# (c) 2013, Benno Joy +# +# This module is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This software is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this software. If not, see . + +import time + +try: + from novaclient.v1_1 import client as nova_client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client + from keystoneclient.v2_0 import client as ksclient + HAVE_DEPS = True +except ImportError: + HAVE_DEPS = False + +DOCUMENTATION = ''' +--- +module: quantum_floating_ip +version_added: "1.2" +author: + - "Benno Joy (@bennojoy)" + - "Brad P. Crochet (@bcrochet)" +deprecated: Deprecated in 2.0. Use os_floating_ip instead +short_description: Add/Remove floating IP from an instance +description: + - Add or Remove a floating IP to an instance +options: + login_username: + description: + - login username to authenticate to keystone + required: true + default: admin + login_password: + description: + - Password of login user + required: true + default: 'yes' + login_tenant_name: + description: + - The tenant name of the login user + required: true + default: 'yes' + auth_url: + description: + - The keystone url for authentication + required: false + default: 'http://127.0.0.1:35357/v2.0/' + region_name: + description: + - Name of the region + required: false + default: None + state: + description: + - Indicate desired state of the resource + choices: ['present', 'absent'] + default: present + network_name: + description: + - Name of the network from which IP has to be assigned to VM. Please make sure the network is an external network + required: true + default: None + instance_name: + description: + - The name of the instance to which the IP address should be assigned + required: true + default: None + internal_network_name: + description: + - The name of the network of the port to associate with the floating ip. Necessary when VM multiple networks. + required: false + default: None + version_added: "1.5" +requirements: + - "python >= 2.6" + - "python-novaclient" + - "python-neutronclient or python-quantumclient" + - "python-keystoneclient" +''' + +EXAMPLES = ''' +# Assign a floating ip to the instance from an external network +- quantum_floating_ip: state=present login_username=admin login_password=admin + login_tenant_name=admin network_name=external_network + instance_name=vm1 internal_network_name=internal_network +''' + +def _get_ksclient(module, kwargs): + try: + kclient = ksclient.Client(username=kwargs.get('login_username'), + password=kwargs.get('login_password'), + tenant_name=kwargs.get('login_tenant_name'), + auth_url=kwargs.get('auth_url'), + region_name=kwargs.get('region_name')) + except Exception, e: + module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message) + global _os_keystone + _os_keystone = kclient + return kclient + + +def _get_endpoint(module, ksclient): + try: + endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') + except Exception, e: + module.fail_json(msg = "Error getting network endpoint: %s" % e.message) + return endpoint + +def _get_neutron_client(module, kwargs): + _ksclient = _get_ksclient(module, kwargs) + token = _ksclient.auth_token + endpoint = _get_endpoint(module, _ksclient) + kwargs = { + 'token': token, + 'endpoint_url': endpoint + } + try: + neutron = client.Client('2.0', **kwargs) + except Exception, e: + module.fail_json(msg = "Error in connecting to neutron: %s " % e.message) + return neutron + +def _get_server_state(module, nova): + server_info = None + server = None + try: + for server in nova.servers.list(): + if server: + info = server._info + if info['name'] == module.params['instance_name']: + if info['status'] != 'ACTIVE' and module.params['state'] == 'present': + module.fail_json( msg="The VM is available but not Active. state:" + info['status']) + server_info = info + break + except Exception, e: + module.fail_json(msg = "Error in getting the server list: %s" % e.message) + return server_info, server + +def _get_port_info(neutron, module, instance_id, internal_network_name=None): + subnet_id = None + if internal_network_name: + kwargs = {'name': internal_network_name} + networks = neutron.list_networks(**kwargs) + network_id = networks['networks'][0]['id'] + kwargs = { + 'network_id': network_id, + 'ip_version': 4 + } + subnets = neutron.list_subnets(**kwargs) + subnet_id = subnets['subnets'][0]['id'] + kwargs = { + 'device_id': instance_id, + } + try: + ports = neutron.list_ports(**kwargs) + except Exception, e: + module.fail_json( msg = "Error in listing ports: %s" % e.message) + if subnet_id: + port = next(port for port in ports['ports'] if port['fixed_ips'][0]['subnet_id'] == subnet_id) + port_id = port['id'] + fixed_ip_address = port['fixed_ips'][0]['ip_address'] + else: + port_id = ports['ports'][0]['id'] + fixed_ip_address = ports['ports'][0]['fixed_ips'][0]['ip_address'] + if not ports['ports']: + return None, None + return fixed_ip_address, port_id + +def _get_floating_ip(module, neutron, fixed_ip_address, network_name): + kwargs = { + 'fixed_ip_address': fixed_ip_address + } + try: + ips = neutron.list_floatingips(**kwargs) + except Exception, e: + module.fail_json(msg = "error in fetching the floatingips's %s" % e.message) + if not ips['floatingips']: + return None, None + for address in ips['floatingips']: + if _check_ips_network(neutron, address['floating_network_id'], network_name): + return address['id'], address['floating_ip_address'] + return None, None + +def _check_ips_network(neutron, net_id, network_name): + if neutron.show_network(net_id)['network']['name'] == network_name: + return True + else: + return False + +def _create_floating_ip(neutron, module, port_id, net_id, fixed_ip): + kwargs = { + 'port_id': port_id, + 'floating_network_id': net_id, + 'fixed_ip_address': fixed_ip + } + try: + result = neutron.create_floatingip({'floatingip': kwargs}) + except Exception, e: + module.fail_json(msg="There was an error in updating the floating ip address: %s" % e.message) + module.exit_json(changed=True, result=result, public_ip=result['floatingip']['floating_ip_address']) + +def _get_net_id(neutron, module): + kwargs = { + 'name': module.params['network_name'], + } + try: + networks = neutron.list_networks(**kwargs) + except Exception, e: + module.fail_json("Error in listing neutron networks: %s" % e.message) + if not networks['networks']: + return None + return networks['networks'][0]['id'] + +def _update_floating_ip(neutron, module, port_id, floating_ip_id): + kwargs = { + 'port_id': port_id + } + try: + result = neutron.update_floatingip(floating_ip_id, {'floatingip': kwargs}) + except Exception, e: + module.fail_json(msg="There was an error in updating the floating ip address: %s" % e.message) + module.exit_json(changed=True, result=result) + + +def main(): + + argument_spec = openstack_argument_spec() + argument_spec.update(dict( + network_name = dict(required=True), + instance_name = dict(required=True), + state = dict(default='present', choices=['absent', 'present']), + internal_network_name = dict(default=None), + )) + module = AnsibleModule(argument_spec=argument_spec) + + if not HAVE_DEPS: + module.fail_json(msg='python-novaclient, python-keystoneclient, and either python-neutronclient or python-quantumclient are required') + + try: + nova = nova_client.Client(module.params['login_username'], module.params['login_password'], + module.params['login_tenant_name'], module.params['auth_url'], region_name=module.params['region_name'], service_type='compute') + neutron = _get_neutron_client(module, module.params) + except Exception, e: + module.fail_json(msg="Error in authenticating to nova: %s" % e.message) + + server_info, server_obj = _get_server_state(module, nova) + if not server_info: + module.fail_json(msg="The instance name provided cannot be found") + + fixed_ip, port_id = _get_port_info(neutron, module, server_info['id'], module.params['internal_network_name']) + if not port_id: + module.fail_json(msg="Cannot find a port for this instance, maybe fixed ip is not assigned") + + floating_id, floating_ip = _get_floating_ip(module, neutron, fixed_ip, module.params['network_name']) + + if module.params['state'] == 'present': + if floating_ip: + module.exit_json(changed = False, public_ip=floating_ip) + net_id = _get_net_id(neutron, module) + if not net_id: + module.fail_json(msg = "cannot find the network specified, please check") + _create_floating_ip(neutron, module, port_id, net_id, fixed_ip) + + if module.params['state'] == 'absent': + if floating_ip: + _update_floating_ip(neutron, module, None, floating_id) + module.exit_json(changed=False) + +# this is magic, see lib/ansible/module.params['common.py +from ansible.module_utils.basic import * +from ansible.module_utils.openstack import * +if __name__ == '__main__': + main() + diff --git a/cloud/openstack/_quantum_floating_ip_associate.py b/cloud/openstack/_quantum_floating_ip_associate.py new file mode 100644 index 00000000..8960e247 --- /dev/null +++ b/cloud/openstack/_quantum_floating_ip_associate.py @@ -0,0 +1,229 @@ +#!/usr/bin/python +#coding: utf-8 -*- + +# (c) 2013, Benno Joy +# +# This module is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This software is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this software. If not, see . + +import time +try: + from novaclient.v1_1 import client as nova_client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client + from keystoneclient.v2_0 import client as ksclient + HAVE_DEPS = True +except ImportError: + HAVE_DEPS = False + +DOCUMENTATION = ''' +--- +module: quantum_floating_ip_associate +version_added: "1.2" +author: "Benno Joy (@bennojoy)" +deprecated: Deprecated in 2.0. Use os_floating_ip instead +short_description: Associate or disassociate a particular floating IP with an instance +description: + - Associates or disassociates a specific floating IP with a particular instance +options: + login_username: + description: + - login username to authenticate to keystone + required: true + default: admin + login_password: + description: + - password of login user + required: true + default: 'yes' + login_tenant_name: + description: + - the tenant name of the login user + required: true + default: true + auth_url: + description: + - the keystone url for authentication + required: false + default: 'http://127.0.0.1:35357/v2.0/' + region_name: + description: + - name of the region + required: false + default: None + state: + description: + - indicates the desired state of the resource + choices: ['present', 'absent'] + default: present + instance_name: + description: + - name of the instance to which the public IP should be assigned + required: true + default: None + ip_address: + description: + - floating ip that should be assigned to the instance + required: true + default: None +requirements: + - "python >= 2.6" + - "python-novaclient" + - "python-neutronclient or python-quantumclient" + - "python-keystoneclient" +''' + +EXAMPLES = ''' +# Associate a specific floating IP with an Instance +- quantum_floating_ip_associate: + state=present + login_username=admin + login_password=admin + login_tenant_name=admin + ip_address=1.1.1.1 + instance_name=vm1 +''' + +def _get_ksclient(module, kwargs): + try: + kclient = ksclient.Client(username=kwargs.get('login_username'), + password=kwargs.get('login_password'), + tenant_name=kwargs.get('login_tenant_name'), + auth_url=kwargs.get('auth_url')) + except Exception, e: + module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message) + global _os_keystone + _os_keystone = kclient + return kclient + + +def _get_endpoint(module, ksclient): + try: + endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') + except Exception, e: + module.fail_json(msg = "Error getting network endpoint: %s" % e.message) + return endpoint + +def _get_neutron_client(module, kwargs): + _ksclient = _get_ksclient(module, kwargs) + token = _ksclient.auth_token + endpoint = _get_endpoint(module, _ksclient) + kwargs = { + 'token': token, + 'endpoint_url': endpoint + } + try: + neutron = client.Client('2.0', **kwargs) + except Exception, e: + module.fail_json(msg = "Error in connecting to neutron: %s " % e.message) + return neutron + +def _get_server_state(module, nova): + server_info = None + server = None + try: + for server in nova.servers.list(): + if server: + info = server._info + if info['name'] == module.params['instance_name']: + if info['status'] != 'ACTIVE' and module.params['state'] == 'present': + module.fail_json(msg="The VM is available but not Active. state:" + info['status']) + server_info = info + break + except Exception, e: + module.fail_json(msg = "Error in getting the server list: %s" % e.message) + return server_info, server + +def _get_port_id(neutron, module, instance_id): + kwargs = dict(device_id = instance_id) + try: + ports = neutron.list_ports(**kwargs) + except Exception, e: + module.fail_json( msg = "Error in listing ports: %s" % e.message) + if not ports['ports']: + return None + return ports['ports'][0]['id'] + +def _get_floating_ip_id(module, neutron): + kwargs = { + 'floating_ip_address': module.params['ip_address'] + } + try: + ips = neutron.list_floatingips(**kwargs) + except Exception, e: + module.fail_json(msg = "error in fetching the floatingips's %s" % e.message) + if not ips['floatingips']: + module.fail_json(msg = "Could find the ip specified in parameter, Please check") + ip = ips['floatingips'][0]['id'] + if not ips['floatingips'][0]['port_id']: + state = "detached" + else: + state = "attached" + return state, ip + +def _update_floating_ip(neutron, module, port_id, floating_ip_id): + kwargs = { + 'port_id': port_id + } + try: + result = neutron.update_floatingip(floating_ip_id, {'floatingip': kwargs}) + except Exception, e: + module.fail_json(msg = "There was an error in updating the floating ip address: %s" % e.message) + module.exit_json(changed = True, result = result, public_ip=module.params['ip_address']) + +def main(): + + argument_spec = openstack_argument_spec() + argument_spec.update(dict( + ip_address = dict(required=True), + instance_name = dict(required=True), + state = dict(default='present', choices=['absent', 'present']) + )) + module = AnsibleModule(argument_spec=argument_spec) + + if not HAVE_DEPS: + module.fail_json(msg='python-novaclient, python-keystoneclient, and either python-neutronclient or python-quantumclient are required') + + try: + nova = nova_client.Client(module.params['login_username'], module.params['login_password'], + module.params['login_tenant_name'], module.params['auth_url'], service_type='compute') + except Exception, e: + module.fail_json( msg = " Error in authenticating to nova: %s" % e.message) + neutron = _get_neutron_client(module, module.params) + state, floating_ip_id = _get_floating_ip_id(module, neutron) + if module.params['state'] == 'present': + if state == 'attached': + module.exit_json(changed = False, result = 'attached', public_ip=module.params['ip_address']) + server_info, server_obj = _get_server_state(module, nova) + if not server_info: + module.fail_json(msg = " The instance name provided cannot be found") + port_id = _get_port_id(neutron, module, server_info['id']) + if not port_id: + module.fail_json(msg = "Cannot find a port for this instance, maybe fixed ip is not assigned") + _update_floating_ip(neutron, module, port_id, floating_ip_id) + + if module.params['state'] == 'absent': + if state == 'detached': + module.exit_json(changed = False, result = 'detached') + if state == 'attached': + _update_floating_ip(neutron, module, None, floating_ip_id) + module.exit_json(changed = True, result = "detached") + +# this is magic, see lib/ansible/module.params['common.py +from ansible.module_utils.basic import * +from ansible.module_utils.openstack import * +if __name__ == '__main__': + main() + diff --git a/cloud/openstack/os_floating_ip.py b/cloud/openstack/os_floating_ip.py new file mode 100644 index 00000000..2d939a9b --- /dev/null +++ b/cloud/openstack/os_floating_ip.py @@ -0,0 +1,245 @@ +#!/usr/bin/python +# coding: utf-8 -*- + +# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. +# Copyright (c) 2013, Benno Joy +# +# This module is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This software is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this software. If not, see . + + +try: + import shade + HAS_SHADE = True +except ImportError: + HAS_SHADE = False + +DOCUMENTATION = ''' +--- +module: os_floating_ip +version_added: "2.0" +short_description: Add/Remove floating IP from an instance +extends_documentation_fragment: openstack +description: + - Add or Remove a floating IP to an instance +options: + server: + description: + - The name or ID of the instance to which the IP address + should be assigned. + required: true + network_name: + description: + - Name of the network from which IP has to be assigned to VM. + Please make sure the network is an external network. + - Required if ip_address is not given. + required: true + default: None + internal_network_name: + description: + - Name of the network of the port to associate with the floating ip. + Necessary when VM multiple networks. + required: false + default: None + state: + description: + - Should the resource be present or absent. + choices: [present, absent] + required: false + default: present +requirements: ["shade"] +''' + +EXAMPLES = ''' +# Assign a floating ip to the instance from an external network +- os_floating_ip: + cloud: mordred + state: present + server: vm1 + network_name: external_network + internal_network_name: internal_network +''' + + +def _get_server_state(module, cloud): + info = None + server = cloud.get_server(module.params['server']) + if server: + info = server._info + status = info['status'] + if status != 'ACTIVE' and module.params['state'] == 'present': + module.fail_json( + msg="The VM is available but not Active. State: %s" % status + ) + return info, server + + +def _get_port_info(neutron, module, instance_id, internal_network_name=None): + subnet_id = None + if internal_network_name: + kwargs = {'name': internal_network_name} + networks = neutron.list_networks(**kwargs) + network_id = networks['networks'][0]['id'] + kwargs = { + 'network_id': network_id, + 'ip_version': 4 + } + subnets = neutron.list_subnets(**kwargs) + subnet_id = subnets['subnets'][0]['id'] + + kwargs = { + 'device_id': instance_id, + } + try: + ports = neutron.list_ports(**kwargs) + except Exception, e: + module.fail_json(msg="Error in listing ports: %s" % e.message) + + if subnet_id: + port = next(port for port in ports['ports'] if port['fixed_ips'][0]['subnet_id'] == subnet_id) + port_id = port['id'] + fixed_ip_address = port['fixed_ips'][0]['ip_address'] + else: + port_id = ports['ports'][0]['id'] + fixed_ip_address = ports['ports'][0]['fixed_ips'][0]['ip_address'] + + if not ports['ports']: + return None, None + return fixed_ip_address, port_id + + +def _get_floating_ip(neutron, module, fixed_ip_address): + kwargs = { + 'fixed_ip_address': fixed_ip_address + } + try: + ips = neutron.list_floatingips(**kwargs) + except Exception, e: + module.fail_json( + msg="Error in fetching the floatingips's %s" % e.message + ) + + if not ips['floatingips']: + return None, None + + return (ips['floatingips'][0]['id'], + ips['floatingips'][0]['floating_ip_address']) + + +def _create_and_associate_floating_ip(neutron, module, port_id, + net_id, fixed_ip): + kwargs = { + 'port_id': port_id, + 'floating_network_id': net_id, + 'fixed_ip_address': fixed_ip + } + + try: + result = neutron.create_floatingip({'floatingip': kwargs}) + except Exception, e: + module.fail_json( + msg="Error in updating the floating ip address: %s" % e.message + ) + + module.exit_json( + changed=True, + result=result, + public_ip=result['floatingip']['floating_ip_address'] + ) + + +def _get_public_net_id(neutron, module): + kwargs = { + 'name': module.params['network_name'], + } + try: + networks = neutron.list_networks(**kwargs) + except Exception, e: + module.fail_json("Error in listing neutron networks: %s" % e.message) + if not networks['networks']: + return None + return networks['networks'][0]['id'] + + +def _update_floating_ip(neutron, module, port_id, floating_ip_id): + kwargs = { + 'port_id': port_id + } + try: + result = neutron.update_floatingip(floating_ip_id, + {'floatingip': kwargs}) + except Exception, e: + module.fail_json( + msg="Error in updating the floating ip address: %s" % e.message + ) + module.exit_json(changed=True, result=result) + + +def main(): + argument_spec = openstack_full_argument_spec( + server = dict(required=True), + network_name = dict(required=True), + internal_network_name = dict(default=None), + state = dict(default='present', choices=['absent', 'present']), + ) + + module_kwargs = openstack_module_kwargs() + module = AnsibleModule(argument_spec, **module_kwargs) + + if not HAS_SHADE: + module.fail_json(msg='shade is required for this module') + + state = module.params['state'] + internal_network_name = module.params['internal_network_name'] + + try: + cloud = shade.openstack_cloud(**module.params) + neutron = cloud.neutron_client + + server_info, server_obj = _get_server_state(module, cloud) + if not server_info: + module.fail_json(msg="The server provided cannot be found") + + fixed_ip, port_id = _get_port_info( + neutron, module, server_info['id'], internal_network_name) + if not port_id: + module.fail_json(msg="Cannot find a port for this instance," + " maybe fixed ip is not assigned") + + floating_id, floating_ip = _get_floating_ip(neutron, module, fixed_ip) + + if state == 'present': + if floating_ip: + # This server already has a floating IP assigned + module.exit_json(changed=False, public_ip=floating_ip) + + pub_net_id = _get_public_net_id(neutron, module) + if not pub_net_id: + module.fail_json( + msg="Cannot find the public network specified" + ) + _create_and_associate_floating_ip(neutron, module, port_id, + pub_net_id, fixed_ip) + + elif state == 'absent': + if floating_ip: + _update_floating_ip(neutron, module, None, floating_id) + module.exit_json(changed=False) + + except shade.OpenStackCloudException as e: + module.fail_json(msg=e.message) + +# this is magic, see lib/ansible/module_common.py +from ansible.module_utils.basic import * +from ansible.module_utils.openstack import * +main() diff --git a/cloud/openstack/quantum_floating_ip.py b/cloud/openstack/quantum_floating_ip.py deleted file mode 100644 index b7599da0..00000000 --- a/cloud/openstack/quantum_floating_ip.py +++ /dev/null @@ -1,289 +0,0 @@ -#!/usr/bin/python -#coding: utf-8 -*- - -# (c) 2013, Benno Joy -# -# This module is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This software is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this software. If not, see . - -import time - -try: - from novaclient.v1_1 import client as nova_client - try: - from neutronclient.neutron import client - except ImportError: - from quantumclient.quantum import client - from keystoneclient.v2_0 import client as ksclient - HAVE_DEPS = True -except ImportError: - HAVE_DEPS = False - -DOCUMENTATION = ''' ---- -module: quantum_floating_ip -version_added: "1.2" -author: - - "Benno Joy (@bennojoy)" - - "Brad P. Crochet (@bcrochet)" -short_description: Add/Remove floating IP from an instance -description: - - Add or Remove a floating IP to an instance -options: - login_username: - description: - - login username to authenticate to keystone - required: true - default: admin - login_password: - description: - - Password of login user - required: true - default: 'yes' - login_tenant_name: - description: - - The tenant name of the login user - required: true - default: 'yes' - auth_url: - description: - - The keystone url for authentication - required: false - default: 'http://127.0.0.1:35357/v2.0/' - region_name: - description: - - Name of the region - required: false - default: None - state: - description: - - Indicate desired state of the resource - choices: ['present', 'absent'] - default: present - network_name: - description: - - Name of the network from which IP has to be assigned to VM. Please make sure the network is an external network - required: true - default: None - instance_name: - description: - - The name of the instance to which the IP address should be assigned - required: true - default: None - internal_network_name: - description: - - The name of the network of the port to associate with the floating ip. Necessary when VM multiple networks. - required: false - default: None - version_added: "1.5" -requirements: - - "python >= 2.6" - - "python-novaclient" - - "python-neutronclient or python-quantumclient" - - "python-keystoneclient" -''' - -EXAMPLES = ''' -# Assign a floating ip to the instance from an external network -- quantum_floating_ip: state=present login_username=admin login_password=admin - login_tenant_name=admin network_name=external_network - instance_name=vm1 internal_network_name=internal_network -''' - -def _get_ksclient(module, kwargs): - try: - kclient = ksclient.Client(username=kwargs.get('login_username'), - password=kwargs.get('login_password'), - tenant_name=kwargs.get('login_tenant_name'), - auth_url=kwargs.get('auth_url'), - region_name=kwargs.get('region_name')) - except Exception, e: - module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message) - global _os_keystone - _os_keystone = kclient - return kclient - - -def _get_endpoint(module, ksclient): - try: - endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') - except Exception, e: - module.fail_json(msg = "Error getting network endpoint: %s" % e.message) - return endpoint - -def _get_neutron_client(module, kwargs): - _ksclient = _get_ksclient(module, kwargs) - token = _ksclient.auth_token - endpoint = _get_endpoint(module, _ksclient) - kwargs = { - 'token': token, - 'endpoint_url': endpoint - } - try: - neutron = client.Client('2.0', **kwargs) - except Exception, e: - module.fail_json(msg = "Error in connecting to neutron: %s " % e.message) - return neutron - -def _get_server_state(module, nova): - server_info = None - server = None - try: - for server in nova.servers.list(): - if server: - info = server._info - if info['name'] == module.params['instance_name']: - if info['status'] != 'ACTIVE' and module.params['state'] == 'present': - module.fail_json( msg="The VM is available but not Active. state:" + info['status']) - server_info = info - break - except Exception, e: - module.fail_json(msg = "Error in getting the server list: %s" % e.message) - return server_info, server - -def _get_port_info(neutron, module, instance_id, internal_network_name=None): - subnet_id = None - if internal_network_name: - kwargs = {'name': internal_network_name} - networks = neutron.list_networks(**kwargs) - network_id = networks['networks'][0]['id'] - kwargs = { - 'network_id': network_id, - 'ip_version': 4 - } - subnets = neutron.list_subnets(**kwargs) - subnet_id = subnets['subnets'][0]['id'] - kwargs = { - 'device_id': instance_id, - } - try: - ports = neutron.list_ports(**kwargs) - except Exception, e: - module.fail_json( msg = "Error in listing ports: %s" % e.message) - if subnet_id: - port = next(port for port in ports['ports'] if port['fixed_ips'][0]['subnet_id'] == subnet_id) - port_id = port['id'] - fixed_ip_address = port['fixed_ips'][0]['ip_address'] - else: - port_id = ports['ports'][0]['id'] - fixed_ip_address = ports['ports'][0]['fixed_ips'][0]['ip_address'] - if not ports['ports']: - return None, None - return fixed_ip_address, port_id - -def _get_floating_ip(module, neutron, fixed_ip_address, network_name): - kwargs = { - 'fixed_ip_address': fixed_ip_address - } - try: - ips = neutron.list_floatingips(**kwargs) - except Exception, e: - module.fail_json(msg = "error in fetching the floatingips's %s" % e.message) - if not ips['floatingips']: - return None, None - for address in ips['floatingips']: - if _check_ips_network(neutron, address['floating_network_id'], network_name): - return address['id'], address['floating_ip_address'] - return None, None - -def _check_ips_network(neutron, net_id, network_name): - if neutron.show_network(net_id)['network']['name'] == network_name: - return True - else: - return False - -def _create_floating_ip(neutron, module, port_id, net_id, fixed_ip): - kwargs = { - 'port_id': port_id, - 'floating_network_id': net_id, - 'fixed_ip_address': fixed_ip - } - try: - result = neutron.create_floatingip({'floatingip': kwargs}) - except Exception, e: - module.fail_json(msg="There was an error in updating the floating ip address: %s" % e.message) - module.exit_json(changed=True, result=result, public_ip=result['floatingip']['floating_ip_address']) - -def _get_net_id(neutron, module): - kwargs = { - 'name': module.params['network_name'], - } - try: - networks = neutron.list_networks(**kwargs) - except Exception, e: - module.fail_json("Error in listing neutron networks: %s" % e.message) - if not networks['networks']: - return None - return networks['networks'][0]['id'] - -def _update_floating_ip(neutron, module, port_id, floating_ip_id): - kwargs = { - 'port_id': port_id - } - try: - result = neutron.update_floatingip(floating_ip_id, {'floatingip': kwargs}) - except Exception, e: - module.fail_json(msg="There was an error in updating the floating ip address: %s" % e.message) - module.exit_json(changed=True, result=result) - - -def main(): - - argument_spec = openstack_argument_spec() - argument_spec.update(dict( - network_name = dict(required=True), - instance_name = dict(required=True), - state = dict(default='present', choices=['absent', 'present']), - internal_network_name = dict(default=None), - )) - module = AnsibleModule(argument_spec=argument_spec) - - if not HAVE_DEPS: - module.fail_json(msg='python-novaclient, python-keystoneclient, and either python-neutronclient or python-quantumclient are required') - - try: - nova = nova_client.Client(module.params['login_username'], module.params['login_password'], - module.params['login_tenant_name'], module.params['auth_url'], region_name=module.params['region_name'], service_type='compute') - neutron = _get_neutron_client(module, module.params) - except Exception, e: - module.fail_json(msg="Error in authenticating to nova: %s" % e.message) - - server_info, server_obj = _get_server_state(module, nova) - if not server_info: - module.fail_json(msg="The instance name provided cannot be found") - - fixed_ip, port_id = _get_port_info(neutron, module, server_info['id'], module.params['internal_network_name']) - if not port_id: - module.fail_json(msg="Cannot find a port for this instance, maybe fixed ip is not assigned") - - floating_id, floating_ip = _get_floating_ip(module, neutron, fixed_ip, module.params['network_name']) - - if module.params['state'] == 'present': - if floating_ip: - module.exit_json(changed = False, public_ip=floating_ip) - net_id = _get_net_id(neutron, module) - if not net_id: - module.fail_json(msg = "cannot find the network specified, please check") - _create_floating_ip(neutron, module, port_id, net_id, fixed_ip) - - if module.params['state'] == 'absent': - if floating_ip: - _update_floating_ip(neutron, module, None, floating_id) - module.exit_json(changed=False) - -# this is magic, see lib/ansible/module.params['common.py -from ansible.module_utils.basic import * -from ansible.module_utils.openstack import * -if __name__ == '__main__': - main() - diff --git a/cloud/openstack/quantum_floating_ip_associate.py b/cloud/openstack/quantum_floating_ip_associate.py deleted file mode 100644 index a5f39dec..00000000 --- a/cloud/openstack/quantum_floating_ip_associate.py +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/python -#coding: utf-8 -*- - -# (c) 2013, Benno Joy -# -# This module is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This software is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this software. If not, see . - -import time -try: - from novaclient.v1_1 import client as nova_client - try: - from neutronclient.neutron import client - except ImportError: - from quantumclient.quantum import client - from keystoneclient.v2_0 import client as ksclient - HAVE_DEPS = True -except ImportError: - HAVE_DEPS = False - -DOCUMENTATION = ''' ---- -module: quantum_floating_ip_associate -version_added: "1.2" -author: "Benno Joy (@bennojoy)" -short_description: Associate or disassociate a particular floating IP with an instance -description: - - Associates or disassociates a specific floating IP with a particular instance -options: - login_username: - description: - - login username to authenticate to keystone - required: true - default: admin - login_password: - description: - - password of login user - required: true - default: 'yes' - login_tenant_name: - description: - - the tenant name of the login user - required: true - default: true - auth_url: - description: - - the keystone url for authentication - required: false - default: 'http://127.0.0.1:35357/v2.0/' - region_name: - description: - - name of the region - required: false - default: None - state: - description: - - indicates the desired state of the resource - choices: ['present', 'absent'] - default: present - instance_name: - description: - - name of the instance to which the public IP should be assigned - required: true - default: None - ip_address: - description: - - floating ip that should be assigned to the instance - required: true - default: None -requirements: - - "python >= 2.6" - - "python-novaclient" - - "python-neutronclient or python-quantumclient" - - "python-keystoneclient" -''' - -EXAMPLES = ''' -# Associate a specific floating IP with an Instance -- quantum_floating_ip_associate: - state=present - login_username=admin - login_password=admin - login_tenant_name=admin - ip_address=1.1.1.1 - instance_name=vm1 -''' - -def _get_ksclient(module, kwargs): - try: - kclient = ksclient.Client(username=kwargs.get('login_username'), - password=kwargs.get('login_password'), - tenant_name=kwargs.get('login_tenant_name'), - auth_url=kwargs.get('auth_url')) - except Exception, e: - module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message) - global _os_keystone - _os_keystone = kclient - return kclient - - -def _get_endpoint(module, ksclient): - try: - endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') - except Exception, e: - module.fail_json(msg = "Error getting network endpoint: %s" % e.message) - return endpoint - -def _get_neutron_client(module, kwargs): - _ksclient = _get_ksclient(module, kwargs) - token = _ksclient.auth_token - endpoint = _get_endpoint(module, _ksclient) - kwargs = { - 'token': token, - 'endpoint_url': endpoint - } - try: - neutron = client.Client('2.0', **kwargs) - except Exception, e: - module.fail_json(msg = "Error in connecting to neutron: %s " % e.message) - return neutron - -def _get_server_state(module, nova): - server_info = None - server = None - try: - for server in nova.servers.list(): - if server: - info = server._info - if info['name'] == module.params['instance_name']: - if info['status'] != 'ACTIVE' and module.params['state'] == 'present': - module.fail_json(msg="The VM is available but not Active. state:" + info['status']) - server_info = info - break - except Exception, e: - module.fail_json(msg = "Error in getting the server list: %s" % e.message) - return server_info, server - -def _get_port_id(neutron, module, instance_id): - kwargs = dict(device_id = instance_id) - try: - ports = neutron.list_ports(**kwargs) - except Exception, e: - module.fail_json( msg = "Error in listing ports: %s" % e.message) - if not ports['ports']: - return None - return ports['ports'][0]['id'] - -def _get_floating_ip_id(module, neutron): - kwargs = { - 'floating_ip_address': module.params['ip_address'] - } - try: - ips = neutron.list_floatingips(**kwargs) - except Exception, e: - module.fail_json(msg = "error in fetching the floatingips's %s" % e.message) - if not ips['floatingips']: - module.fail_json(msg = "Could find the ip specified in parameter, Please check") - ip = ips['floatingips'][0]['id'] - if not ips['floatingips'][0]['port_id']: - state = "detached" - else: - state = "attached" - return state, ip - -def _update_floating_ip(neutron, module, port_id, floating_ip_id): - kwargs = { - 'port_id': port_id - } - try: - result = neutron.update_floatingip(floating_ip_id, {'floatingip': kwargs}) - except Exception, e: - module.fail_json(msg = "There was an error in updating the floating ip address: %s" % e.message) - module.exit_json(changed = True, result = result, public_ip=module.params['ip_address']) - -def main(): - - argument_spec = openstack_argument_spec() - argument_spec.update(dict( - ip_address = dict(required=True), - instance_name = dict(required=True), - state = dict(default='present', choices=['absent', 'present']) - )) - module = AnsibleModule(argument_spec=argument_spec) - - if not HAVE_DEPS: - module.fail_json(msg='python-novaclient, python-keystoneclient, and either python-neutronclient or python-quantumclient are required') - - try: - nova = nova_client.Client(module.params['login_username'], module.params['login_password'], - module.params['login_tenant_name'], module.params['auth_url'], service_type='compute') - except Exception, e: - module.fail_json( msg = " Error in authenticating to nova: %s" % e.message) - neutron = _get_neutron_client(module, module.params) - state, floating_ip_id = _get_floating_ip_id(module, neutron) - if module.params['state'] == 'present': - if state == 'attached': - module.exit_json(changed = False, result = 'attached', public_ip=module.params['ip_address']) - server_info, server_obj = _get_server_state(module, nova) - if not server_info: - module.fail_json(msg = " The instance name provided cannot be found") - port_id = _get_port_id(neutron, module, server_info['id']) - if not port_id: - module.fail_json(msg = "Cannot find a port for this instance, maybe fixed ip is not assigned") - _update_floating_ip(neutron, module, port_id, floating_ip_id) - - if module.params['state'] == 'absent': - if state == 'detached': - module.exit_json(changed = False, result = 'detached') - if state == 'attached': - _update_floating_ip(neutron, module, None, floating_ip_id) - module.exit_json(changed = True, result = "detached") - -# this is magic, see lib/ansible/module.params['common.py -from ansible.module_utils.basic import * -from ansible.module_utils.openstack import * -if __name__ == '__main__': - main() - -- cgit v1.2.1