diff --git a/neutron/neutron/common/azure/__init__.py b/neutron/neutron/common/azure/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron/neutron/common/azure/config.py b/neutron/neutron/common/azure/config.py new file mode 100644 index 0000000..28f7a26 --- /dev/null +++ b/neutron/neutron/common/azure/config.py @@ -0,0 +1,30 @@ +""" +Copyright 2017 Platform9 Systems Inc.(http://www.platform9.com) +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 oslo_config import cfg + +azure_group = cfg.OptGroup(name='azure', + title='Options to connect to Azure cloud') + +azure_opts = [ + cfg.StrOpt('tenant_id', help='Tenant id of Azure account'), + cfg.StrOpt('client_id', help='Azure client id'), + cfg.StrOpt('client_secret', help='Azure Client secret', secret=True), + cfg.StrOpt('subscription_id', help='Azure subscription id'), + cfg.StrOpt('region', help='Azure region'), + cfg.StrOpt('resource_group', help="Azure resource group"), +] + +cfg.CONF.register_group(azure_group) +cfg.CONF.register_opts(azure_opts, group=azure_group) + +azure_conf = cfg.CONF.azure diff --git a/neutron/neutron/common/azure/utils.py b/neutron/neutron/common/azure/utils.py new file mode 100644 index 0000000..89234a5 --- /dev/null +++ b/neutron/neutron/common/azure/utils.py @@ -0,0 +1,231 @@ +""" +Copyright (c) 2017 Platform9 Systems Inc. +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 expressed or implied. See the +License for the specific language governing permissions and limitations +under the License. +""" +from functools import partial +import uuid + +from azure.common.credentials import ServicePrincipalCredentials +from azure.mgmt.compute import ComputeManagementClient +from azure.mgmt.network import NetworkManagementClient +from msrestazure.azure_exceptions import CloudError +from oslo_log import log as logging + +from neutron.extensions import securitygroup as sg +from neutron_lib import exceptions as n_exceptions + +LOG = logging.getLogger(__name__) + + +class FloatingIPNotFound(n_exceptions.NotFound): + message = "Floating IP %(ip)s could not be found." + + +def azure_handle_exception(fn): + def wrapper(*args, **kwargs): + try: + return fn(*args, **kwargs) + except Exception as e: + LOG.exception("Exception occurred in Azure operation: %s" % + (e.message)) + + return wrapper + + +def get_credentials(tenant_id, client_id, client_secret): + credentials = ServicePrincipalCredentials( + client_id=client_id, secret=client_secret, tenant=tenant_id) + return credentials + + +def _get_client(tenant_id, client_id, client_secret, subscription_id, + cls=None): + """Returns Azure compute resource object for interacting with Azure API + + :param tenant_id: string, tenant_id from azure account + :param client_id: string, client_id (application id) + :param client_secret: string, secret key of application + :param subscription_id: string, unique identification id of account + :return: :class:`Resource ` object + """ + credentials = get_credentials(tenant_id, client_id, client_secret) + client = cls(credentials, subscription_id) + return client + + +get_compute_client = partial(_get_client, cls=ComputeManagementClient) +get_network_client = partial(_get_client, cls=NetworkManagementClient) + + +def _perform_and_wait(operation, args=(), kwargs={}, timeout=300): + operation(*args, **kwargs).wait(timeout=timeout) + + +def create_network(network, resource_group, name, info): + _perform_and_wait(network.virtual_networks.create_or_update, + (resource_group, name, info)) + + +@azure_handle_exception +def delete_network(network, resource_group, name): + _perform_and_wait(network.virtual_networks.delete, (resource_group, name)) + + +def get_network(network, resource_group, name): + try: + return network.virtual_networks.get(resource_group, name) + except CloudError: + raise n_exceptions.NetworkNotFound(net_id=name) + + +def create_subnet(network, resource_group, network_name, name, info): + _perform_and_wait(network.subnets.create_or_update, + (resource_group, network_name, name, info)) + + +@azure_handle_exception +def delete_subnet(network, resource_group, network_name, name): + _perform_and_wait(network.subnets.delete, (resource_group, network_name, + name)) + + +def get_subnet(network, resource_group, network_name, name): + try: + return network.subnets.get(resource_group, network_name, name) + except CloudError: + raise n_exceptions.SubnetNotFound(subnet_id=name) + + +def get_nic(network, resource_group, name): + try: + return network.network_interfaces.get(resource_group, name) + except CloudError: + raise n_exceptions.PortNotFound(port_id=name) + + +def create_nic(network, resource_group, name, body): + _perform_and_wait(network.network_interfaces.create_or_update, + (resource_group, name, body)) + + +@azure_handle_exception +def delete_nic(network, resource_group, name): + _perform_and_wait(network.network_interfaces.delete, (resource_group, + name)) + + +def get_sg(network, resource_group, name): + return network.network_security_groups.get(resource_group, name) + + +def create_sg(network, resource_group, name, body): + _perform_and_wait(network.network_security_groups.create_or_update, + (resource_group, name, body)) + + +@azure_handle_exception +def delete_sg(network, resource_group, name): + _perform_and_wait(network.network_security_groups.delete, (resource_group, + name)) + + +def get_sg_rule(network, resource_group, sg_name, name): + return network.network_security_rules.get(resource_group, sg_name, name) + + +def create_sg_rule(network, resource_group, sg_name, name, body): + _perform_and_wait(network.security_rules.create_or_update, + (resource_group, sg_name, name, body)) + + +@azure_handle_exception +def delete_sg_rule(network, resource_group, sg_name, name): + _perform_and_wait(network.security_rules.delete, (resource_group, sg_name, + name)) + + +# Maintaining different calls for update to simplify mocking +update_network = create_network +update_nic = create_nic +update_sg = create_sg +update_sg_rule = create_sg_rule + + +def convert_sg_rule(openstack_rule, priority=None): + directions = {'ingress': 'Inbound', 'egress': 'Outbound'} + protocols = {'tcp': 'Tcp', 'udp': 'Udp'} + + # Asterix '*' is used to match all possible values. + # E.g. In case of source_port_range it will allow all ports. + # The default security group is allow all traffic, based on + # user inputs we refine it further. + sg_rule = { + 'source_port_range': '*', + 'destination_port_range': '*', + 'source_address_prefix': '*', + 'destination_address_prefix': '*', + 'access': 'Allow', + 'priority': priority + } + sg_rule['direction'] = directions[openstack_rule['direction']] + + if openstack_rule['ethertype'] != 'IPv4': + raise sg.SecurityGroupRuleInvalidEtherType( + ethertype=openstack_rule['ethertype'], values=('IPv4', )) + + protocol = openstack_rule['protocol'] + if protocol is None: + sg_rule['protocol'] = '*' + if protocol and protocol in protocols: + sg_rule['protocol'] = protocols[protocol] + else: + raise sg.SecurityGroupRuleInvalidProtocol( + protocol=protocol, values=protocols.keys()) + + port_range_min = openstack_rule['port_range_min'] + port_range_max = openstack_rule['port_range_max'] + if port_range_min and port_range_min == port_range_max: + sg_rule['destination_port_range'] = str(port_range_min) + elif port_range_min and port_range_max: + sg_rule['destination_port_range'] = "%s-%s" % (port_range_min, + port_range_max) + + if openstack_rule['remote_ip_prefix']: + # TODO(ssudake21): Allow support for tags in source_address_prefix + sg_rule['source_address_prefix'] = openstack_rule['remote_ip_prefix'] + + return sg_rule + + +def allocate_floatingip(network, resource_group, region): + name = 'eip-' + str(uuid.uuid4()) + data = { + 'location': region, + 'public_ip_allocation_method': 'Static', + 'public_ip_address_version': 'IPv4', + 'idle_timeout_in_minutes': 4 + } + response = network.public_ip_addresses.create_or_update( + resource_group, name, data) + return response.result() + + +def get_floatingip(network, resource_group, ip): + for public_ip in network.public_ip_addresses.list(resource_group): + if public_ip.ip_address == ip: + return public_ip + raise FloatingIPNotFound(ip=ip) + + +@azure_handle_exception +def delete_floatingip(network, resource_group, public_ip_name): + _perform_and_wait(network.public_ip_addresses.delete, (resource_group, + public_ip_name)) diff --git a/neutron/neutron/plugins/ml2/drivers/azure/__init__.py b/neutron/neutron/plugins/ml2/drivers/azure/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron/neutron/plugins/ml2/drivers/azure/mech_azure.py b/neutron/neutron/plugins/ml2/drivers/azure/mech_azure.py new file mode 100644 index 0000000..dae0b58 --- /dev/null +++ b/neutron/neutron/plugins/ml2/drivers/azure/mech_azure.py @@ -0,0 +1,349 @@ +""" +Copyright 2017 Platform9 Systems Inc.(http://www.platform9.com) +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. +""" + +import random + +from oslo_log import log + +import ipaddr +from neutron.callbacks import events +from neutron.callbacks import registry +from neutron.callbacks import resources +from neutron.common.azure.config import azure_conf +from neutron.common.azure import utils +from neutron.manager import NeutronManager +from neutron.plugins.ml2 import driver_api as api +from neutron_lib import constants as n_const +from neutron_lib import exceptions as e + +try: + from neutron_lib.plugins import directory +except ImportError: + pass + +LOG = log.getLogger(__name__) + + +def log_network_hook(function): + def wraps(self, context): + LOG.debug("Called %s with context: %s" % (function.__name__, context)) + return function(self, context) + + return wraps + + +# Custom VIF type for Azure +VIF_TYPE_AZURE = "azure_nic" + + +class AzureMechanismDriver(api.MechanismDriver): + """Ml2 Mechanism driver for Azure""" + + def __init__(self): + super(AzureMechanismDriver, self).__init__() + self._network_client = None + self._sg_callback_map = {} + + def initialize(self): + LOG.info("Azure Mechanism driver init with %s project, %s region" % + (azure_conf.tenant_id, azure_conf.region)) + self._subscribe_events() + + def _subscribe_events(self): + events_info = [(resources.SECURITY_GROUP, events.BEFORE_DELETE, + self._delete_secgrp), + (resources.SECURITY_GROUP, events.BEFORE_UPDATE, + self._update_secgrp), (resources.SECURITY_GROUP, + events.BEFORE_CREATE, + self._validate_secgrp), + (resources.SECURITY_GROUP, events.AFTER_CREATE, + self._create_secgrp), (resources.SECURITY_GROUP_RULE, + events.BEFORE_DELETE, + self._delete_secrule), + (resources.SECURITY_GROUP_RULE, events.BEFORE_UPDATE, + self._update_secrule), (resources.SECURITY_GROUP_RULE, + events.BEFORE_CREATE, + self._validate_secrule), + (resources.SECURITY_GROUP_RULE, events.AFTER_CREATE, + self._create_secrule)] + for resource, event, callback in events_info: + registry.subscribe(self.secgrp_callback, resource, event) + self._sg_callback_map[(resource, event)] = callback + LOG.info("Azure mechanism driver registered security groups callbacks") + + @property + def network_client(self): + conf = azure_conf + if self._network_client is None: + args = (conf.tenant_id, conf.client_id, conf.client_secret, + conf.subscription_id) + self._network_client = utils.get_network_client(*args) + return self._network_client + + def _azure_network_name(self, context): + return 'net-' + context.current[api.ID] + + def _azure_subnet_name(self, context): + return 'subnet-' + context.current[api.ID] + + def _azure_subnet_network_name(self, context): + return 'net-' + context.current['network_id'] + + def _azure_secgrp_id(self, openstack_id): + return "secgrp-" + openstack_id + + def _azure_secrule_id(self, openstack_id): + return "secrule-" + openstack_id + + @staticmethod + def is_private_network(cidr): + return ipaddr.IPNetwork(cidr).is_private + + @log_network_hook + def create_network_precommit(self, context): + pass + + @log_network_hook + def create_network_postcommit(self, context): + pass + + @log_network_hook + def update_network_precommit(self, context): + pass + + @log_network_hook + def update_network_postcommit(self, context): + pass + + @log_network_hook + def delete_network_precommit(self, context): + pass + + @log_network_hook + def delete_network_postcommit(self, context): + name = self._azure_network_name(context) + utils.delete_network(self.network_client, azure_conf.resource_group, + name) + + @log_network_hook + def create_subnet_precommit(self, context): + net_svc = self.network_client + name = self._azure_subnet_name(context) + network_name = self._azure_subnet_network_name(context) + cidr = context.current['cidr'] + if self.is_private_network(cidr): + try: + azure_network = utils.get_network( + net_svc, azure_conf.resource_group, network_name) + address_prefixes = azure_network.address_space.address_prefixes + if cidr not in address_prefixes: + address_prefixes.append(cidr) + utils.update_network(net_svc, azure_conf.resource_group, + network_name, azure_network) + except e.NetworkNotFound: + body = { + 'location': azure_conf.region, + 'address_space': { + 'address_prefixes': [ + cidr, + ] + } + } + utils.create_network(net_svc, azure_conf.resource_group, + network_name, body) + utils.create_subnet(net_svc, azure_conf.resource_group, + network_name, name, {'address_prefix': cidr}) + + @log_network_hook + def create_subnet_postcommit(self, context): + pass + + @log_network_hook + def update_subnet_precommit(self, context): + pass + + @log_network_hook + def update_subnet_postcommit(self, context): + pass + + @log_network_hook + def delete_subnet_precommit(self, context): + cidr = context.current['cidr'] + if self.is_private_network(cidr): + name = self._azure_subnet_name(context) + network_name = self._azure_subnet_network_name(context) + utils.delete_subnet(self.network_client, azure_conf.resource_group, + network_name, name) + + @log_network_hook + def delete_subnet_postcommit(self, context): + pass + + def _check_dev_owner(self, port_context): + dev_owner = port_context.current['device_owner'] + return len(dev_owner) == 0 or dev_owner.startswith( + n_const.DEVICE_OWNER_COMPUTE_PREFIX) + + @log_network_hook + def create_port_precommit(self, context): + LOG.debug("Create_port_precommit: %s" % context.current) + if not self._check_dev_owner(context): + return + net_svc = self.network_client + resource_group = azure_conf.resource_group + region = azure_conf.region + network_name = self._azure_subnet_network_name(context) + details = context.current['fixed_ips'][0] + subnet_name = 'subnet-' + details['subnet_id'] + ip_address = details['ip_address'] + nic_name = 'nic-' + context.current['id'] + ipc_name = 'ipc-' + context.current['id'] + azure_subnet = utils.get_subnet(net_svc, resource_group, network_name, + subnet_name) + body = { + 'location': + region, + 'ip_configurations': [{ + 'name': ipc_name, + 'private_ip_address': ip_address, + 'private_ip_allocation_method': 'Static', + 'subnet': { + 'id': azure_subnet.id + }, + }] + } + security_groups = context.current['security_groups'] + if security_groups and len(security_groups) == 1: + sg_name = self._azure_secgrp_id(security_groups[0]) + sg = utils.get_sg(net_svc, resource_group, sg_name) + body['network_security_group'] = {'id': sg.id} + utils.create_nic(net_svc, resource_group, nic_name, body) + LOG.info("Created NIC %s on Azure." % nic_name) + + @log_network_hook + def create_port_postcommit(self, context): + pass + + @log_network_hook + def update_port_precommit(self, context): + pass + + @log_network_hook + def update_port_postcommit(self, context): + pass + + @log_network_hook + def delete_port_precommit(self, context): + if not self._check_dev_owner(context): + return + net_svc = self.network_client + resource_group = azure_conf.resource_group + nic_name = 'nic-' + context.current['id'] + utils.get_nic(net_svc, resource_group, nic_name) + utils.delete_nic(net_svc, resource_group, nic_name) + LOG.info("Deleted NIC %s on Azure." % nic_name) + + @log_network_hook + def delete_port_postcommit(self, context): + pass + + def get_secgrp(self, context, id): + try: + core_plugin = NeutronManager.get_plugin() + except AttributeError: + core_plugin = directory.get_plugin() + return core_plugin.get_security_group(context, id) + + def get_secgrp_rule(self, context, id): + try: + core_plugin = NeutronManager.get_plugin() + except AttributeError: + core_plugin = directory.get_plugin() + return core_plugin.get_security_group_rule(context, id) + + def _validate_secrule(self, **kwargs): + rule = kwargs['security_group_rule'] + utils.convert_sg_rule(rule) + + def _create_secrule(self, **kwargs): + net_svc = self.network_client + resource_group = azure_conf.resource_group + rule = kwargs['security_group_rule'] + azure_rule = utils.convert_sg_rule(rule) + sg_name = self._azure_secgrp_id(rule['security_group_id']) + name = self._azure_secrule_id(rule['id']) + sg = utils.get_sg(net_svc, resource_group, sg_name) + """Each Azure security rule has a priority. + The value can be between 100 and 4096. The priority number must be + unique for each rule in the collection. The lower the priority number, + the higher the priority of the rule. + """ + previous_priorities = sorted([i.priority for i in sg.security_rules]) + if previous_priorities: + priority = previous_priorities[-1] + 1 + else: + priority = 100 + azure_rule['priority'] = priority + utils.create_sg_rule(net_svc, resource_group, sg_name, name, + azure_rule) + + def _update_secrule(self, **kwargs): + pass + + def _delete_secrule(self, **kwargs): + net_svc = self.network_client + resource_group = azure_conf.resource_group + secrule_id = kwargs['security_group_rule_id'] + sec_rule = self.get_secgrp_rule(kwargs['context'], secrule_id) + sg_name = self._azure_secgrp_id(sec_rule['security_group_id']) + name = self._azure_secrule_id(secrule_id) + utils.delete_sg_rule(net_svc, resource_group, sg_name, name) + + def _validate_secgrp(self, **kwargs): + pass + + def _create_secgrp(self, **kwargs): + net_svc = self.network_client + resource_group = azure_conf.resource_group + region = azure_conf.region + name = self._azure_secgrp_id(kwargs['security_group']['id']) + body = { + 'location': region, + } + utils.create_sg(net_svc, resource_group, name, body) + + def _update_secgrp(self, **kwargs): + pass + + def _delete_secgrp(self, **kwargs): + net_svc = self.network_client + resource_group = azure_conf.resource_group + name = self._azure_secgrp_id(kwargs['security_group_id']) + utils.delete_sg(net_svc, resource_group, name) + + @log_network_hook + def bind_port(self, context): + fixed_ip_dict = dict() + if 'fixed_ips' in context.current: + if len(context.current['fixed_ips']): + fixed_ip_dict = context.current['fixed_ips'][0] + + segment_id = random.choice(context.segments_to_bind)[api.ID] + context.set_binding( + segment_id, VIF_TYPE_AZURE, fixed_ip_dict, status='ACTIVE') + return True + + def secgrp_callback(self, resource, event, trigger, **kwargs): + LOG.info("Secgrp_callback: %s %s %s" % (resource, event, kwargs)) + callback = self._sg_callback_map[(resource, event)] + callback(**kwargs) diff --git a/neutron/neutron/services/l3_router/azure_router_plugin.py b/neutron/neutron/services/l3_router/azure_router_plugin.py new file mode 100644 index 0000000..5bd38ab --- /dev/null +++ b/neutron/neutron/services/l3_router/azure_router_plugin.py @@ -0,0 +1,203 @@ +""" +Copyright 2017 Platform9 Systems Inc.(http://www.platform9.com) +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. +""" +import neutron_lib + +from distutils.version import LooseVersion +from neutron.common.azure.config import azure_conf +from neutron.common.azure import utils +from neutron.db import common_db_mixin +from neutron.db import extraroute_db +from neutron.db import l3_db +from neutron.db import l3_dvrscheduler_db +from neutron.db import l3_gwmode_db +from neutron.db import l3_hamode_db +from neutron.db import l3_hascheduler_db +from neutron.plugins.common import constants +from neutron.quota import resource_registry +from neutron.services import service_base +from neutron_lib import constants as n_const +from oslo_log import log as logging + +LOG = logging.getLogger(__name__) + +if LooseVersion(neutron_lib.__version__) < LooseVersion("1.0.0"): + router = l3_db.Router + floating_ip = l3_db.FloatingIP + plugin_type = constants.L3_ROUTER_NAT + service_plugin_class = service_base.ServicePluginBase +else: + from neutron.db.models import l3 + from neutron_lib.plugins import constants as plugin_constants + from neutron_lib.services import base + router = l3.Router + floating_ip = l3.FloatingIP + plugin_type = plugin_constants.L3 + service_plugin_class = base.ServicePluginBase + + +class AzureRouterPlugin( + service_plugin_class, common_db_mixin.CommonDbMixin, + extraroute_db.ExtraRoute_db_mixin, l3_hamode_db.L3_HA_NAT_db_mixin, + l3_gwmode_db.L3_NAT_db_mixin, l3_dvrscheduler_db.L3_DVRsch_db_mixin, + l3_hascheduler_db.L3_HA_scheduler_db_mixin): + """Implementation of the Neutron L3 Router Service Plugin. + + This class implements a L3 service plugin that provides + router and floatingip resources and manages associated + request/response. + All DB related work is implemented in classes + l3_db.L3_NAT_db_mixin, l3_hamode_db.L3_HA_NAT_db_mixin, + l3_dvr_db.L3_NAT_with_dvr_db_mixin, and extraroute_db.ExtraRoute_db_mixin. + """ + supported_extension_aliases = [ + "dvr", "router", "ext-gw-mode", "extraroute", "l3_agent_scheduler", + "l3-ha" + ] + + @resource_registry.tracked_resources(router=router, floatingip=floating_ip) + def __init__(self): + super(AzureRouterPlugin, self).__init__() + l3_db.subscribe() + self._compute_client = None + self._network_client = None + self.tenant_id = azure_conf.tenant_id + self.client_id = azure_conf.client_id + self.client_secret = azure_conf.client_secret + self.subscription_id = azure_conf.subscription_id + self.region = azure_conf.region + self.resource_group = azure_conf.resource_group + + LOG.info("Azure Router plugin init with %s project, %s region" % + (self.tenant_id, self.region)) + + @property + def compute_client(self): + if self._compute_client is None: + args = (self.tenant_id, self.client_id, self.client_secret, + self.subscription_id) + self._compute_client = utils.get_compute_client(*args) + return self._compute_client + + @property + def network_client(self): + if self._network_client is None: + args = (self.tenant_id, self.client_id, self.client_secret, + self.subscription_id) + self._network_client = utils.get_network_client(*args) + return self._network_client + + def get_plugin_type(self): + return plugin_type + + def get_plugin_description(self): + """returns string description of the plugin.""" + return ("Azure L3 Router Service Plugin for basic L3 forwarding" + " between (L2) Neutron networks and access to external" + " networks via a NAT gateway.") + + def _add_floatingip_to_port(self, port_id, public_ip): + net_svc = self.network_client + resource_group = self.resource_group + nic_name = 'nic-' + port_id + nic = utils.get_nic(net_svc, resource_group, nic_name) + nic.ip_configurations[0].public_ip_address = public_ip + utils.update_nic(net_svc, resource_group, nic_name, nic) + + def _remove_floatingip_from_port(self, port_id): + self._add_floatingip_to_port(port_id, public_ip=None) + + def create_floatingip(self, context, floatingip): + net_svc = self.network_client + resource_group = self.resource_group + public_ip = None + port_id = None + status = n_const.FLOATINGIP_STATUS_DOWN + try: + floatingip_dict = floatingip['floatingip'] + public_ip = utils.allocate_floatingip(net_svc, resource_group, + self.region) + LOG.info("Created Azure static IP %s" % public_ip.ip_address) + floatingip_dict['floating_ip_address'] = public_ip.ip_address + port_id = floatingip_dict['port_id'] + if port_id: + self._add_floatingip_to_port(port_id, public_ip) + status = n_const.FLOATINGIP_STATUS_ACTIVE + res = super(AzureRouterPlugin, self).create_floatingip( + context, floatingip, initial_status=status) + except Exception as e: + LOG.exception("Error in Creation/Allocating floating IP: %s" % e) + if status == n_const.FLOATINGIP_STATUS_ACTIVE: + self._remove_floatingip_from_port(port_id) + if public_ip: + utils.delete_floatingip(net_svc, resource_group, + public_ip.name) + raise e + return res + + def update_floatingip(self, context, id, floatingip): + net_svc = self.network_client + resource_group = self.resource_group + status = n_const.FLOATINGIP_STATUS_DOWN + floatingip_dict = floatingip['floatingip'] + orig_floatingip = super(AzureRouterPlugin, self).get_floatingip( + context, id) + ip_address = orig_floatingip['floating_ip_address'] + port_id = floatingip_dict['port_id'] + if port_id: + public_ip = utils.get_floatingip(net_svc, resource_group, + ip_address) + self._add_floatingip_to_port(port_id, public_ip) + status = n_const.FLOATINGIP_STATUS_ACTIVE + else: + self._remove_floatingip_from_port(orig_floatingip['port_id']) + floatingip_dict['status'] = status + return super(AzureRouterPlugin, self).update_floatingip( + context, id, floatingip) + + def delete_floatingip(self, context, id): + net_svc = self.network_client + resource_group = self.resource_group + floating_ip = super(AzureRouterPlugin, self).get_floatingip( + context, id) + ip_address = floating_ip['floating_ip_address'] + public_ip = utils.get_floatingip(net_svc, resource_group, ip_address) + port_id = floating_ip['port_id'] + if port_id: + self._remove_floatingip_from_port(port_id) + utils.delete_floatingip(net_svc, resource_group, public_ip.name) + return super(AzureRouterPlugin, self).delete_floatingip(context, id) + + def create_router(self, context, router): + LOG.debug("Creating router %s" % router['router']['name']) + return super(AzureRouterPlugin, self).create_router(context, router) + + def delete_router(self, context, id): + LOG.debug("Deleting router %s" % id) + return super(AzureRouterPlugin, self).delete_router(context, id) + + def update_router(self, context, id, router): + LOG.debug("Updating router %s" % id) + return super(AzureRouterPlugin, self).update_router( + context, id, router) + + def add_router_interface(self, context, router_id, interface_info): + LOG.debug("Adding interface %s to router %s" % (interface_info, + router_id)) + return super(AzureRouterPlugin, self).add_router_interface( + context, router_id, interface_info) + + def remove_router_interface(self, context, router_id, interface_info): + LOG.debug("Deleting interface %s from router %s" % (interface_info, + router_id)) + return super(AzureRouterPlugin, self).remove_router_interface( + context, router_id, interface_info)