【neutron源码分析】create_network/create_subnet/create_port源码分析

1. network源码分析
      通过执行命令创建网络上,neutron net-create xxxxx

    首先neutronclient发送HTTP请求给neutron-server,调用create函数。
def create(self, request, body=None, **kwargs):
    self._notifier.info(request.context,
                        self._resource + '.create.start',
                        body)
    return self._create(request, body, **kwargs)

def _create(self, request, body, **kwargs):
    """Creates a new instance of the requested entity."""
    parent_id = kwargs.get(self._parent_id_name)
    body = Controller.prepare_request_body(request.context,
                                           copy.deepcopy(body), True,
                                           self._resource, self._attr_info,
                                           allow_bulk=self._allow_bulk)
    action = self._plugin_handlers[self.CREATE]
    # Check authz
    if self._collection in body:
        # Have to account for bulk create
        items = body[self._collection]
    else:
        items = [body]
 
    policy.init()
    
    request_deltas = collections.defaultdict(int)
    for item in items:
        self._validate_network_tenant_ownership(request,
                                                item[self._resource])
        policy.enforce(request.context,
                       action,
                       item[self._resource],
                       pluralized=self._collection)
        if 'tenant_id' not in item[self._resource]:
            # no tenant_id - no quota check
            continue
        tenant_id = item[self._resource]['tenant_id']
        request_deltas[tenant_id] += 1
    # Quota enforcement
    reservations = []
    try:
        for (tenant, delta) in request_deltas.items():
            reservation = quota.QUOTAS.make_reservation(
                request.context,
                tenant,
                {self._resource: delta},
                self._plugin)
            reservations.append(reservation)
    except exceptions.QuotaResourceUnknown as e:
            # We don't want to quota this resource
            LOG.debug(e)

    if self._collection in body and self._native_bulk:
        # plugin does atomic bulk create operations
        objs = do_create(body, bulk=True)
        # Use first element of list to discriminate attributes which
        # should be removed because of authZ policies
        fields_to_strip = self._exclude_attributes_by_policy(
            request.context, objs[0])
        return notify({self._collection: [self._filter_attributes(
            request.context, obj, fields_to_strip=fields_to_strip)
            for obj in objs]})
    else:
        if self._collection in body:
            # Emulate atomic bulk behavior
            objs = do_create(body, bulk=True, emulated=True)
            return notify({self._collection: objs})
        else:
            obj = do_create(body)
            self._send_nova_notification(action, {},
                                         {self._resource: obj})
            return notify({self._resource: self._view(request.context,
                                                      obj)})

     body内容大概如下所示:
{
   
u'network ': {
        'router: external': False,
        'availability_zone_hints': [
            
        ],
        'description': '',
        'provider: physical_network': <objectobjectat0x7f67c71e03a0>,
        u'admin_state_up': True,
        '
tenant_id ': u'6b13cdad170c4674a061b9ff69847164',
        'segments': <objectobjectat0x7f67c71e03a0>,
        'dns_domain': '',
        'provider: network_type': <objectobjectat0x7f67c71e03a0>,
        'is_default': False,
        'qos_policy_id': None,
        'shared': False,
        'port_security_enabled': True,
        'provider: segmentation_id': <objectobjectat0x7f67c71e03a0>,
        u
'name': u'zzl-test11111 '
    }
}

    action: create_network

def do_create(body, bulk=False, emulated=False):
    kwargs = {self._parent_id_name: parent_id} if parent_id else {}
    if bulk and not emulated:
        obj_creator = getattr(self._plugin, "%s_bulk" % action)
    else:
        obj_creator = getattr(self._plugin, action)
    try:
        if emulated:
            return self._emulate_bulk_create(obj_creator, request,
                                             body, parent_id)
        else:
            if self._collection in body:
                # This is weird but fixing it requires changes to the
                # plugin interface
                kwargs.update({self._collection: body})
            else:
                kwargs.update({self._resource: body})
            return obj_creator(request.context, **kwargs)
    except Exception:
        # In case of failure the plugin will always raise an
        # exception. Cancel the reservation
        with excutils.save_and_reraise_exception():
            for reservation in reservations:
                quota.QUOTAS.cancel_reservation(
                    request.context, reservation.reservation_id)

        obj_creator函数中self._plugin为ml2,action为create_network: Ml2Plugin.create_network
              neutron.plugins.ml2.plugin.Ml2Plugin

     位置:neutron/plugins/ml2/plugin.py 
def create_network(self, context, network):
    result, mech_context = self._create_network_db(context, network)
    try:
        self.mechanism_manager.create_network_postcommit(mech_context)
    except ml2_exc.MechanismDriverError:
        with excutils.save_and_reraise_exception():
            LOG.error(_LE("mechanism_manager.create_network_postcommit "
                          "failed, deleting network '%s'"), result['id'])
            self.delete_network(context, result['id'])

    return result

    调用_create_network_db,写入数据库操作具体如下:
def _create_network_db(self, context, network):
    net_data = network[attributes.NETWORK]
    tenant_id = net_data['tenant_id']
    session = context.session
    with session.begin(subtransactions=True):
        self._ensure_default_security_group(context, tenant_id)
        net_db = self.create_network_db(context, network)
        result = self._make_network_dict(net_db, process_extensions=False,
                                         context=context)
        self.extension_manager.process_create_network(context, net_data,
                                                      result)
        self._process_l3_create(context, result, net_data)
        net_data['id'] = result['id']
        self.type_manager.create_network_segments(context, net_data,
                                                  tenant_id)
        self.type_manager.extend_network_dict_provider(context, result)
        # Update the transparent vlan if configured
        if utils.is_extension_supported(self, 'vlan-transparent'):
            vlt = vlantransparent.get_vlan_transparent(net_data)
            net_db['vlan_transparent'] = vlt
            result['vlan_transparent'] = vlt
        mech_context = driver_context.NetworkContext(self, context,
                                                     result)
        self.mechanism_manager.create_network_precommit(mech_context)

        if net_data.get(api.MTU, 0) > 0:
            net_db[api.MTU] = net_data[api.MTU]
            result[api.MTU] = net_data[api.MTU]

        if az_ext.AZ_HINTS in net_data:
            self.validate_availability_zones(context, 'network',
                                             net_data[az_ext.AZ_HINTS])
            az_hints = az_ext.convert_az_list_to_string(
                                            net_data[az_ext.AZ_HINTS])
            net_db[az_ext.AZ_HINTS] = az_hints
            result[az_ext.AZ_HINTS] = az_hints
    net_db数据大致如下:
{
    tenant_id=u'6b13cdad170c4674a061b9ff69847164',
    id='01978067-94f2-4758-b124-b33413a9d9f2',
    name=u'xxxxx',
    status='ACTIVE',
    admin_state_up=True,
    mtu=1500,
    vlan_transparent=None,
    availability_zone_hints=None,
    standard_attr_id=1097
}

    create_network_db是真正的写入数据库操作
  data 大致如下:
{
    'router: external': False,
    'availability_zone_hints': [
        
    ],
    'description': '',
    'provider: physical_network': <objectobjectat0x7fa333ea23a0>,
    u'admin_state_up': True,
    'tenant_id': u'6b13cdad170c4674a061b9ff69847164',
    'segments': <objectobjectat0x7fa333ea23a0>,
    'dns_domain': '',
    'provider: network_type': <objectobjectat0x7fa333ea23a0>,
    'is_default': False,
    'qos_policy_id': None,
    'shared': False,
    'port_security_enabled': True,
    'provider: segmentation_id': <objectobjectat0x7fa333ea23a0>,
    u'name': u'xxxxx'
}

    extension_manager.process_create_network:创建ml2_conf.ini配置文件选项为:extension_drivers = port_security, qos,根据
port_security = neutron.plugins.ml2.extensions.port_security:PortSecurityExtensionDriver
 
qos = neutron.plugins.ml2.extensions.qos:QosExtensionDriver
class QosExtensionDriver(api.ExtensionDriver):
    def process_create_network(self, context, data, result):
        self.core_ext_handler.process_fields(
            context, base_core.NETWORK, data, result)
      如果请求含有qos_policy_id,则调用update_network_policy
class PortSecurityExtensionDriver(api.ExtensionDriver,
                                  ps_db_common.PortSecurityDbCommon,
                                  common_db_mixin.CommonDbMixin):
    _supported_extension_alias = 'port-security'

    def process_create_network(self, context, data, result):
        # Create the network extension attributes.
        if psec.PORTSECURITY not in data:
            data[psec.PORTSECURITY] = psec.DEFAULT_PORT_SECURITY
        self._process_network_port_security_create(context, data, result)
    将写入数据库中

    mechanism_manager.create_network_precommit
def create_network_precommit(self, context):
    """Notify all mechanism drivers during network creation.

    :raises: DB retriable error if create_network_precommit raises them
    See neutron.db.api.is_retriable for what db exception is retriable
    or neutron.plugins.ml2.common.MechanismDriverError
    if any mechanism driver create_network_precommit call fails.

    Called within the database transaction. If a mechanism driver
    raises an exception, then a MechanismDriverError is propogated
    to the caller, triggering a rollback. There is no guarantee
    that all mechanism drivers are called in this case.
    """
    self._check_vlan_transparency(context)
    self._call_on_drivers("create_network_precommit", context,
                          raise_db_retriable=True)
def _call_on_drivers(self, method_name, context,
                     continue_on_failure=False, raise_db_retriable=False):
 
    error = False
    for driver in self.ordered_mech_drivers:
        try:
            getattr(driver.obj, method_name)(context)

    根据配置文件ml2.conf.ini中选项:mechanism_drivers = networking-plato,
  • 调用该模块:create_network_precommit
  • 然后调用create_network_postcommit


2. subnet源码分析

      通过执行命令创建网络上,neutron subnet-create xxxxx 10.10.10.0/24 --name subnet-xxxxx --gateway 10.10.10.1 --enable_dhcp true

    
       同样经过neutron.api.v2.base.py中_create函数,body数据为:
{
    u'subnet': {
        'host_routes': <objectobjectat0x7f24c067a3a0>,
        'prefixlen': <objectobjectat0x7f24c067a3a0>,
        u'name': u'subnet-xxxxx',
        u'enable_dhcp': True,
        u'network_id': u'01978067-94f2-4758-b124-b33413a9d9f2',
        'tenant_id': u'6b13cdad170c4674a061b9ff69847164',
        'description': '',
        'dns_nameservers': <objectobjectat0x7f24c067a3a0>,
        'ipv6_ra_mode': <objectobjectat0x7f24c067a3a0>,
        'allocation_pools': <objectobjectat0x7f24c067a3a0>,
        u'gateway_ip': u'10.10.10.1',
        u'ip_version': 4,
        'ipv6_address_mode': <objectobjectat0x7f24c067a3a0>,
        u'cidr': u'10.10.10.0/24',
        'subnetpool_id': <objectobjectat0x7f24c067a3a0>,
        'use_default_subnetpool': False
    }
}
       
    action为: create_subnet
    同样需要经过Ml2Plugin.create_subnet方法

def create_subnet(self, context, subnet):
    result, mech_context = self._create_subnet_db(context, subnet)
    try:
        self.mechanism_manager.create_subnet_postcommit(mech_context)
    except ml2_exc.MechanismDriverError:
        with excutils.save_and_reraise_exception():
            LOG.error(_LE("mechanism_manager.create_subnet_postcommit "
                          "failed, deleting subnet '%s'"), result['id'])
            self.delete_subnet(context, result['id'])
    return result

def _create_subnet_db(self, context, subnet):
    session = context.session
    with session.begin(subtransactions=True):
        result = super(Ml2Plugin, self).create_subnet(context, subnet)
        self.extension_manager.process_create_subnet(
            context, subnet[attributes.SUBNET], result)
        network = self.get_network(context, result['network_id'])
        mech_context = driver_context.SubnetContext(self, context,
                                                    result, network)
        self.mechanism_manager.create_subnet_precommit(mech_context)

    return result, mech_context
      
      调用db/db_base_plugin_v2.py中create_subnet函数:
def create_subnet(self, context, subnet):

    s = subnet['subnet']
    cidr = s.get('cidr', attributes.ATTR_NOT_SPECIFIED)
    prefixlen = s.get('prefixlen', attributes.ATTR_NOT_SPECIFIED)
    has_cidr = attributes.is_attr_set(cidr)
    has_prefixlen = attributes.is_attr_set(prefixlen)

    if has_cidr and has_prefixlen:
        msg = _('cidr and prefixlen must not be supplied together')
        raise n_exc.BadRequest(resource='subnets', msg=msg)

    if has_cidr:
        # turn the CIDR into a proper subnet
        net = netaddr.IPNetwork(s['cidr'])
        subnet['subnet']['cidr'] = '%s/%s' % (net.network, net.prefixlen)

    subnetpool_id = self._get_subnetpool_id(context, s)
    if not subnetpool_id and not has_cidr:
        msg = _('a subnetpool must be specified in the absence of a cidr')
        raise n_exc.BadRequest(resource='subnets', msg=msg)

    if subnetpool_id:
        self.ipam.validate_pools_with_subnetpool(s)
        if subnetpool_id == constants.IPV6_PD_POOL_ID:
            if has_cidr:
                # We do not currently support requesting a specific
                # cidr with IPv6 prefix delegation. Set the subnetpool_id
                # to None and allow the request to continue as normal.
                subnetpool_id = None
                self._validate_subnet(context, s)
            else:
                prefix = constants.PROVISIONAL_IPV6_PD_PREFIX
                subnet['subnet']['cidr'] = prefix
                self._validate_subnet_for_pd(s)
    else:
        if not has_cidr:
            msg = _('A cidr must be specified in the absence of a '
                    'subnet pool')
            raise n_exc.BadRequest(resource='subnets', msg=msg)
        self._validate_subnet(context, s)

    return self._create_subnet(context, subnet, subnetpool_id)

        分配子网,并存入数据库


3. port源码分析

      创建一个特定ip地址的Openstack网络端口, 通过执行命令: 
neutron port-create --fixed-ip subnet_id=90cf42a5-61e5-4684-a9a3-ed241f8a00fb,ip_address=10.10.10.10 01978067-94f2-4758-b124-b33413a9d9f2


   neutron.api.v2.base中create函数中body大致如下:
{
    u'port': {
        'binding: host_id': <objectobjectat0x7f88073b63a0>,
        'description': '',
        'allowed_address_pairs': <objectobjectat0x7f88073b63a0>,
        u'admin_state_up': True,
        u'network_id': u'01978067-94f2-4758-b124-b33413a9d9f2',
        'tenant_id': u'6b13cdad170c4674a061b9ff69847164',
        'extra_dhcp_opts': None,
        'mac_address': <objectobjectat0x7f88073b63a0>,
        'name': '',
        'device_owner': '',
        'device_id': '',
        'dns_name': '',
        'binding: profile': <objectobjectat0x7f88073b63a0>,
        'port_security_enabled': <objectobjectat0x7f88073b63a0>,
        'binding: vnic_type': 'normal',
       
u'fixed_ips': [
            {
                u'subnet_id': u'90cf42a5-61e5-4684-a9a3-ed241f8a00fb',
                u'ip_address': u'10.10.10.10'
            }
        ],
        'security_groups': <objectobjectat0x7f88073b63a0>,
        'qos_policy_id': None
    }
}

def create_port(self, context, port):
    # TODO(kevinbenton): remove when bug/1543094 is fixed.
    with lockutils.lock(port['port']['network_id'],
                        lock_file_prefix='neutron-create-port',
                        external=True):
        result, mech_context = self._create_port_db(context, port)
    # notify any plugin that is interested in port create events
    kwargs = {'context': context, 'port': result}
    registry.notify(resources.PORT, events.AFTER_CREATE, self, **kwargs)

    try:
        self.mechanism_manager.create_port_postcommit(mech_context)

    self.notify_security_groups_member_updated(context, result)

    try:
        bound_context = self._bind_port_if_needed(mech_context)

    return bound_context.current

def _create_port_db(self, context, port):
    attrs = port[attributes.PORT]
    if not attrs.get('status'):
        attrs['status'] = const.PORT_STATUS_DOWN

    session = context.session
    with db_api.exc_to_retry(os_db_exception.DBDuplicateEntry),\
            session.begin(subtransactions=True):
        dhcp_opts = attrs.get(edo_ext.EXTRADHCPOPTS, [])
        port_db = self.create_port_db(context, port)
        result = self._make_port_dict(port_db, process_extensions=False)
        self.extension_manager.process_create_port(context, attrs, result)
        self._portsec_ext_port_create_processing(context, result, port)

        # sgids must be got after portsec checked with security group
        sgids = self._get_security_groups_on_port(context, port)
        self._process_port_create_security_group(context, result, sgids)
        network = self.get_network(context, result['network_id'])
        binding = db.add_port_binding(session, result['id'])
        mech_context = driver_context.PortContext(self, context, result,
                                                  network, binding, None)
        self._process_port_binding(mech_context, attrs)

        result[addr_pair.ADDRESS_PAIRS] = (
            self._process_create_allowed_address_pairs(
                context, result,
                attrs.get(addr_pair.ADDRESS_PAIRS)))
        self._process_port_create_extra_dhcp_opts(context, result,
                                                  dhcp_opts)
        self.mechanism_manager.create_port_precommit(mech_context)

    self._apply_dict_extend_functions('ports', result, port_db)
    return result, mech_context

      该代码主要调用Ml2Plugin的create_port创建port以及为port分配MAC和IP,同时更新neutron中相关的数据库信息

def add_port_binding(session, port_id):
    with session.begin(subtransactions=True):
        record = models.PortBinding(
            port_id=port_id,
            vif_type=portbindings.VIF_TYPE_UNBOUND)
        session.add(record)
        return record
      add_port_binding函数写入数据库ml2_port_bindings中vif_type为unbound


3.1 IP分配


def _allocate_ips_for_port(self, context, port):
    """Allocate IP addresses for the port.

    If port['fixed_ips'] is set to 'ATTR_NOT_SPECIFIED', allocate IP
    addresses for the port. If port['fixed_ips'] contains an IP address or
    a subnet_id then allocate an IP address accordingly.
    """
    p = port['port']
    ips = []
    v6_stateless = []
    net_id_filter = {'network_id': [p['network_id']]}
    subnets = self._get_subnets(context, filters=net_id_filter)
    is_router_port = (
        p['device_owner'] in constants.ROUTER_INTERFACE_OWNERS_SNAT)

    fixed_configured = p['fixed_ips'] is not attributes.ATTR_NOT_SPECIFIED
    if fixed_configured:
        configured_ips = self._test_fixed_ips_for_port(context,
                                                       p["network_id"],
                                                       p['fixed_ips'],
                                                       p['device_owner'])
        ips = self._allocate_fixed_ips(context,
                                       configured_ips,
                                       p['mac_address'])

        # For ports that are not router ports, implicitly include all
        # auto-address subnets for address association.
        if not is_router_port:
            v6_stateless += [subnet for subnet in subnets
                             if ipv6_utils.is_auto_address_subnet(subnet)]


def _allocate_fixed_ips(self, context, fixed_ips, mac_address):
    """Allocate IP addresses according to the configured fixed_ips."""
    ips = []

    # we need to start with entries that asked for a specific IP in case
    # those IPs happen to be next in the line for allocation for ones that
    # didn't ask for a specific IP
    fixed_ips.sort(key=lambda x: 'ip_address' not in x)
    for fixed in fixed_ips:
        subnet = self._get_subnet(context, fixed['subnet_id'])
        is_auto_addr = ipv6_utils.is_auto_address_subnet(subnet)
        if 'ip_address' in fixed:
            if not is_auto_addr:
                # Remove the IP address from the allocation pool
                IpamNonPluggableBackend._allocate_specific_ip(
                    context, fixed['subnet_id'], fixed['ip_address'])
            ips.append({'ip_address': fixed['ip_address'],
                        'subnet_id': fixed['subnet_id']})
        # Only subnet ID is specified => need to generate IP
        # from subnet
        else:
            if is_auto_addr:
                ip_address = self._calculate_ipv6_eui64_addr(context,
                                                             subnet,
                                                             mac_address)
                ips.append({'ip_address': ip_address.format(),
                            'subnet_id': subnet['id']})
            else:
                subnets = [subnet]
                # IP address allocation
                result = self._generate_ip(context, subnets)
                ips.append({'ip_address': result['ip_address'],
                            'subnet_id': result['subnet_id']})
    return ips
       根据network_id找到subnets(可能一个network有多个subnet),然后判断fixed ip。如果指定了fixed ip,则验证fixed ip在该network下是否合法,否则直接利用subnet下的可用ip分配。

  • 指定了subnet和fixed IP,执行NeutronDbPluginV2._allocate_specific_ip
  • 指定了subnet,未指定fixed IP,执行self._generate_ip代码

       主要是查询neutron数据库中的ipavailabilityranges表获取IP的范围,然后分配IP后,更新ipavailabilityranges表。

[neutron]> SELECT  * FROM ipavailabilityranges;
+--------------------------------------+----------------+----------------+
| allocation_pool_id                   | first_ip       | last_ip        |
+--------------------------------------+----------------+----------------+
| a5818bca-e7b5-4399-a405-a6a095800281 | 10.10.10.11    | 10.10.10.254   |
| a5818bca-e7b5-4399-a405-a6a095800281 | 10.10.10.2      | 10.10.10.9     |

        IP分配完毕后,则将被分配了的IP更到neutron数据库的ipallocations表。

[neutron]> SELECT * FROM ipallocations;
+--------------------------------------+----------------+--------------------------------------+--------------------------------------+
| port_id                                    | ip_address     | subnet_id                            | network_id                           |
+--------------------------------------+----------------+--------------------------------------+--------------------------------------+
| 07130e1c-a6d4-4f28-843c-33b1e07b223e | 10.248.144.102 | 5eb54ff1-25a7-4ffd-87c8-99e492701f15 | c4206574-8125-41e6-be09-5a624dadb570 |
| dba288c4-3c63-4af3-87fc-929166d072e6 | 10.10.10.10    | 90cf42a5-61e5-4684-a9a3-ed241f8a00fb | 01978067-94f2-4758-b124-b33413a9d9f2 |
+--------------------------------------+----------------+--------------------------------------+--------------------------------------+




创建VM时,从neutron-dhcp-agent获取ip和mac

1. 创建VM时,nova-compute与neutron的plugin交互,在数据库中创建VM的port。

2. neutron数据库中的port创建后,通知neutron-dhcp-agent执行port_create_end。将数据库中port的ip和mac写入到dnsmasq配置文件中(包括host和addn_hosts)。

3. VM启动时,广播dhcp discover请求,当dnsmasq进程的监听接口接收这种请求时,dnsmasq进程将根据配置文件(host和leases文件)中的内容去判定是否有未分配的ip和mac为其提供。

4. VM便获取到与保存在数据库中的ip和mac信息。neutron-dhcp-agent只是将所创建VM的ip和mac信息从数据库中获取到自己的配置文件中,然后等到VM启动时,为其提供。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值