aboutsummaryrefslogtreecommitdiffstats
path: root/aria/multivim-plugin/neutron_plugin/port.py
diff options
context:
space:
mode:
Diffstat (limited to 'aria/multivim-plugin/neutron_plugin/port.py')
-rw-r--r--aria/multivim-plugin/neutron_plugin/port.py222
1 files changed, 222 insertions, 0 deletions
diff --git a/aria/multivim-plugin/neutron_plugin/port.py b/aria/multivim-plugin/neutron_plugin/port.py
new file mode 100644
index 0000000000..4db4c442c5
--- /dev/null
+++ b/aria/multivim-plugin/neutron_plugin/port.py
@@ -0,0 +1,222 @@
+#########
+# Copyright (c) 2014 GigaSpaces Technologies Ltd. All rights reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+
+from cloudify import ctx
+from cloudify.decorators import operation
+from cloudify.exceptions import NonRecoverableError
+
+import neutronclient.common.exceptions as neutron_exceptions
+
+from openstack_plugin_common import (
+ transform_resource_name,
+ with_neutron_client,
+ with_nova_client,
+ get_resource_id,
+ get_openstack_id_of_single_connected_node_by_openstack_type,
+ delete_resource_and_runtime_properties,
+ delete_runtime_properties,
+ use_external_resource,
+ is_external_relationship,
+ validate_resource,
+ OPENSTACK_ID_PROPERTY,
+ OPENSTACK_TYPE_PROPERTY,
+ OPENSTACK_NAME_PROPERTY,
+ COMMON_RUNTIME_PROPERTIES_KEYS,
+ is_external_relationship_not_conditionally_created)
+
+from neutron_plugin.network import NETWORK_OPENSTACK_TYPE
+from neutron_plugin.subnet import SUBNET_OPENSTACK_TYPE
+from openstack_plugin_common.floatingip import get_server_floating_ip
+
+PORT_OPENSTACK_TYPE = 'port'
+
+# Runtime properties
+FIXED_IP_ADDRESS_PROPERTY = 'fixed_ip_address' # the fixed ip address
+MAC_ADDRESS_PROPERTY = 'mac_address' # the mac address
+RUNTIME_PROPERTIES_KEYS = \
+ COMMON_RUNTIME_PROPERTIES_KEYS + [FIXED_IP_ADDRESS_PROPERTY,
+ MAC_ADDRESS_PROPERTY]
+
+NO_SG_PORT_CONNECTION_RETRY_INTERVAL = 3
+
+
+@operation
+@with_neutron_client
+def create(neutron_client, args, **kwargs):
+
+ ext_port = use_external_resource(ctx, neutron_client, PORT_OPENSTACK_TYPE)
+ if ext_port:
+ try:
+ net_id = \
+ get_openstack_id_of_single_connected_node_by_openstack_type(
+ ctx, NETWORK_OPENSTACK_TYPE, True)
+
+ if net_id:
+ port_id = ctx.instance.runtime_properties[
+ OPENSTACK_ID_PROPERTY]
+
+ if neutron_client.show_port(
+ port_id)['port']['network_id'] != net_id:
+ raise NonRecoverableError(
+ 'Expected external resources port {0} and network {1} '
+ 'to be connected'.format(port_id, net_id))
+
+ ctx.instance.runtime_properties[FIXED_IP_ADDRESS_PROPERTY] = \
+ _get_fixed_ip(ext_port)
+ ctx.instance.runtime_properties[MAC_ADDRESS_PROPERTY] = \
+ ext_port['mac_address']
+ return
+ except Exception:
+ delete_runtime_properties(ctx, RUNTIME_PROPERTIES_KEYS)
+ raise
+
+ net_id = get_openstack_id_of_single_connected_node_by_openstack_type(
+ ctx, NETWORK_OPENSTACK_TYPE)
+
+ port = {
+ 'name': get_resource_id(ctx, PORT_OPENSTACK_TYPE),
+ 'network_id': net_id,
+ 'security_groups': [],
+ }
+
+ _handle_fixed_ips(port)
+ port.update(ctx.node.properties['port'], **args)
+ transform_resource_name(ctx, port)
+
+ p = neutron_client.create_port({'port': port})['port']
+ ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY] = p['id']
+ ctx.instance.runtime_properties[OPENSTACK_TYPE_PROPERTY] =\
+ PORT_OPENSTACK_TYPE
+ ctx.instance.runtime_properties[OPENSTACK_NAME_PROPERTY] = p['name']
+ ctx.instance.runtime_properties[FIXED_IP_ADDRESS_PROPERTY] = \
+ _get_fixed_ip(p)
+ ctx.instance.runtime_properties[MAC_ADDRESS_PROPERTY] = p['mac_address']
+
+
+@operation
+@with_neutron_client
+def delete(neutron_client, **kwargs):
+ try:
+ delete_resource_and_runtime_properties(ctx, neutron_client,
+ RUNTIME_PROPERTIES_KEYS)
+ except neutron_exceptions.NeutronClientException, e:
+ if e.status_code == 404:
+ # port was probably deleted when an attached device was deleted
+ delete_runtime_properties(ctx, RUNTIME_PROPERTIES_KEYS)
+ else:
+ raise
+
+
+@operation
+@with_nova_client
+@with_neutron_client
+def detach(nova_client, neutron_client, **kwargs):
+
+ if is_external_relationship(ctx):
+ ctx.logger.info('Not detaching port from server since '
+ 'external port and server are being used')
+ return
+
+ port_id = ctx.target.instance.runtime_properties[OPENSTACK_ID_PROPERTY]
+ server_id = ctx.source.instance.runtime_properties[OPENSTACK_ID_PROPERTY]
+
+ server_floating_ip = get_server_floating_ip(neutron_client, server_id)
+ if server_floating_ip:
+ ctx.logger.info('We have floating ip {0} attached to server'
+ .format(server_floating_ip['floating_ip_address']))
+ server = nova_client.servers.get(server_id)
+ server.remove_floating_ip(server_floating_ip['floating_ip_address'])
+ return ctx.operation.retry(
+ message='Waiting for the floating ip {0} to '
+ 'detach from server {1}..'
+ .format(server_floating_ip['floating_ip_address'],
+ server_id),
+ retry_after=10)
+ change = {
+ 'port': {
+ 'device_id': '',
+ 'device_owner': ''
+ }
+ }
+ ctx.logger.info('Detaching port {0}...'.format(port_id))
+ neutron_client.update_port(port_id, change)
+ ctx.logger.info('Successfully detached port {0}'.format(port_id))
+
+
+@operation
+@with_neutron_client
+def connect_security_group(neutron_client, **kwargs):
+ port_id = ctx.source.instance.runtime_properties[OPENSTACK_ID_PROPERTY]
+ security_group_id = ctx.target.instance.runtime_properties[
+ OPENSTACK_ID_PROPERTY]
+
+ if is_external_relationship_not_conditionally_created(ctx):
+ ctx.logger.info('Validating external port and security-group are '
+ 'connected')
+ if any(sg for sg in neutron_client.show_port(port_id)['port'].get(
+ 'security_groups', []) if sg == security_group_id):
+ return
+ raise NonRecoverableError(
+ 'Expected external resources port {0} and security-group {1} to '
+ 'be connected'.format(port_id, security_group_id))
+
+ # WARNING: non-atomic operation
+ port = neutron_client.cosmo_get('port', id=port_id)
+ ctx.logger.info(
+ "connect_security_group(): source_id={0} target={1}".format(
+ port_id, ctx.target.instance.runtime_properties))
+ sgs = port['security_groups'] + [security_group_id]
+ neutron_client.update_port(port_id, {'port': {'security_groups': sgs}})
+
+ # Double check if SG has been actually updated (a race-condition
+ # in OpenStack):
+ port_info = neutron_client.show_port(port_id)['port']
+ port_security_groups = port_info.get('security_groups', [])
+ if security_group_id not in port_security_groups:
+ return ctx.operation.retry(
+ message='Security group connection (`{0}\' -> `{1}\')'
+ ' has not been established!'.format(port_id,
+ security_group_id),
+ retry_after=NO_SG_PORT_CONNECTION_RETRY_INTERVAL
+ )
+
+
+@operation
+@with_neutron_client
+def creation_validation(neutron_client, **kwargs):
+ validate_resource(ctx, neutron_client, PORT_OPENSTACK_TYPE)
+
+
+def _get_fixed_ip(port):
+ # a port may have no fixed IP if it's set on a network without subnets
+ return port['fixed_ips'][0]['ip_address'] if port['fixed_ips'] else None
+
+
+def _handle_fixed_ips(port):
+ fixed_ips_element = {}
+
+ # checking for fixed ip property
+ if ctx.node.properties['fixed_ip']:
+ fixed_ips_element['ip_address'] = ctx.node.properties['fixed_ip']
+
+ # checking for a connected subnet
+ subnet_id = get_openstack_id_of_single_connected_node_by_openstack_type(
+ ctx, SUBNET_OPENSTACK_TYPE, if_exists=True)
+ if subnet_id:
+ fixed_ips_element['subnet_id'] = subnet_id
+
+ # applying fixed ip parameter, if available
+ if fixed_ips_element:
+ port['fixed_ips'] = [fixed_ips_element]