neutron floating IPs implementation

Change-Id: Ib500ce980b8ba8ff537201008fd6d607f2e492b7
diff --git a/README.rst b/README.rst
index b1a3047..efa1e7c 100644
--- a/README.rst
+++ b/README.rst
@@ -697,6 +697,29 @@
 
     TODO: implement updating existing security rules (now it adds new rule if trying to update existing one)
 
+
+Floating IP addresses
+
+.. code-block:: yaml
+
+    neutron:
+      client:
+        enabled: true
+        server:
+          identity:
+            floating_ip:
+              prx01-instance:
+                server: prx01.mk22-lab-basic.local
+                subnet: private-subnet1
+                network: public-net1
+                tenant: demo
+              gtw01-instance:
+                ...
+
+.. note:: The network must have flag router:external set to True.
+          Instance port in the stated subnet will be associated with the dynamically generated floating IP.
+
+
 Usage
 =====
 
diff --git a/neutron/_modules/neutronng.py b/neutron/_modules/neutronng.py
index 55a5acd..e0b6831 100644
--- a/neutron/_modules/neutronng.py
+++ b/neutron/_modules/neutronng.py
@@ -278,7 +278,7 @@
     response = neutron_interface.create_floatingip(
         {'floatingip': floatingip_params})
     if 'floatingip' in response and 'id' in response['floatingip']:
-        return response['floatingip']['id']
+        return response
 
 
 @_autheticate
diff --git a/neutron/_states/neutronng.py b/neutron/_states/neutronng.py
index b7c9195..df0736d 100644
--- a/neutron/_states/neutronng.py
+++ b/neutron/_states/neutronng.py
@@ -327,6 +327,62 @@
             return _update_failed(name, 'router')
     return _no_change(name, 'router')
 
+
+def floatingip_present(name=None,
+                       tenant_name=None,
+                       subnet=None,
+                       tenant=None,
+                       network=None,
+                       port_id=None,
+                       fip_exists=False,
+                       profile=None):
+    '''
+    Ensure that the floating ip address is present for an instance
+    '''
+    instance_id = __salt__['novang.server_get'](name=name, tenant_name=tenant_name, profile=profile)
+    subnet_name = subnet
+    connection_args = _auth(profile)
+    existing_subnet = _neutron_module_call(
+        'list_subnets', name=subnet_name, **connection_args)
+    subnet_id = existing_subnet[subnet_name]['id']
+
+    ret = {}
+    existing_ports = _neutron_module_call(
+        'list_ports', **connection_args)
+    existing_floatingips = _neutron_module_call(
+        'list_floatingips', **connection_args)
+
+    tenant = __salt__['keystone.tenant_get'](name=tenant_name, profile=profile, **connection_args)
+    tenant_id = tenant[tenant_name]['id']
+    existing_network = _neutron_module_call(
+            'list_networks', name=network, **connection_args)
+    floating_network_id = existing_network[network]['id']
+
+    for key, value in existing_ports.iteritems():
+        try:
+            if value['fixed_ips'][0]['subnet_id'] == subnet_id and value['device_id'] == instance_id:
+                port_id=value['id']
+        except:
+            pass
+    for key, value in existing_floatingips.iteritems():
+        try:
+            if value['floating_network_id'] == floating_network_id and value['port_id'] == port_id and value['tenant_id'] == tenant_id:
+                fip_exists = True
+                break
+        except:
+            pass
+
+    if fip_exists == False:
+        for key, value in existing_ports.iteritems():
+            try:
+                if value['fixed_ips'][0]['subnet_id'] == subnet_id and value['device_id'] == instance_id:
+                    ret = _neutron_module_call('create_floatingip', floating_network_id=floating_network_id, port_id=value['id'], tenant_id=tenant_id, **connection_args)
+            except:
+                pass
+        return _created('port', 'floatingip', ret)
+    else:
+        return _no_change('for instance {0}'.format(name), 'floatingip')
+
 def security_group_present(name=None,
                            tenant=None,
                            description=None,
diff --git a/neutron/client.sls b/neutron/client.sls
index 865186e..fda72c0 100644
--- a/neutron/client.sls
+++ b/neutron/client.sls
@@ -8,6 +8,8 @@
 
 {%- for identity_name, identity in client.server.iteritems() %}
 
+{%- if identity.network is defined %}
+
 {%- for network_name, network in identity.network.iteritems() %}
 
 neutron_openstack_network_{{ network_name }}:
@@ -35,6 +37,8 @@
     - provider_segmentation_id: {{ network.provider_segmentation_id }}
     {%- endif %}
 
+{%- if network.subnet is defined %}
+
 {%- for subnet_name, subnet in network.subnet.iteritems() %}
 neutron_openstack_subnet_{{ subnet_name }}:
   neutronng.subnet_present:
@@ -68,8 +72,15 @@
       - neutronng: neutron_openstack_network_{{ network_name }}
 
 {%- endfor %}
+
+{%- endif %}
+
 {%- endfor %}
 
+{%- endif %}
+
+{%- if identity.router is defined %}
+
 {%- for router_name, router in identity.router.iteritems() %}
 neutron_openstack_router_{{ router_name }}:
   neutronng.router_present:
@@ -81,6 +92,10 @@
     - admin_state_up: {{ router.admin_state_up }}
 {%- endfor %}
 
+{%- endif %}
+
+{%- if identity.security_group is defined %}
+
 {%- for security_group_name, security_group in identity.security_group.iteritems() %}
 openstack_security_group_{{ security_group_name }}:
   neutronng.security_group_present:
@@ -91,6 +106,23 @@
     - tenant: {{ security_group.tenant }}
 {%- endfor %}
 
+{%- endif %}
+
+{%- if identity.floating_ip is defined %}
+
+{%- for instance_name, instance in identity.floating_ip.iteritems() %}
+neutron_floating_ip_for_{{ instance_name }}:
+  neutronng.floatingip_present:
+    - subnet: {{ instance.subnet }}
+    - tenant_name: {{ instance.tenant }}
+    - name: {{ instance.server }}
+    - network:  {{ instance.network }}
+    - profile: {{ identity_name }}
+
+{%- endfor %}
+
+{%- endif %}
+
 {%- endfor %}
 
 {%- endif %}