omni/neutron/neutron/plugins/ml2/drivers/azure/mech_azure.py

357 lines
13 KiB
Python

"""
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)
self._create_resource_group_if_not_created(conf)
return self._network_client
def _create_resource_group_if_not_created(self, conf):
is_resource_created = utils.check_resource_existence(
self.resource_client, conf.resource_group)
if not is_resource_created:
utils.create_resource_group(
self.resource_client, conf.resource_group, conf.region)
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)