ml2_conf.ini
[root@controller01 ~]# cat /etc/kolla/neutron-server/ml2_conf.ini
[ml2]
type_drivers = flat,vlan,vxlan
tenant_network_types = vxlan
mechanism_drivers = openvswitch,l2population
extension_drivers = port_security
[ml2_type_vlan]
network_vlan_ranges =
[ml2_type_flat]
flat_networks = physnet1
[ml2_type_vxlan]
vni_ranges = 1:1000
vxlan_group = 239.1.1.1
[securitygroup]
firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver
[agent]
tunnel_types = vxlan
l2_population = true
arp_responder = true
[ovs]
bridge_mappings = physnet1:br-ex
datapath_type = system
ovsdb_connection = tcp:127.0.0.1:6640
local_ip = 172.16.63.191
[root@controller01 ~]#
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
#>>>>>>>>>>>>> do_create()
objs = do_create(body, bulk=True, emulated=True)
return notify({self._collection: objs})
else:
#>>>>>>>>>>>>> do_create()
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 获取 plugin 和对应的 action
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)
#>>>>>从配置文件/etc/kolla/neutron-server/ml2_conf.ini 中获取extension_drivers = port_security 这个信息,并根据值,查找到这个driver类中对应的方法
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)
#>>>>>>>从配置文件/etc/kolla/neutron-server/ml2_conf.ini 中获取mechanism_drivers= xxx 这个信息,并根据值,查找到这个driver类中对应的方法
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,根据
setup.cfg
neutron.ml2.extension_drivers =
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启动时,为其提供。
————————————————
版权声明:本文为CSDN博主「张忠琳」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhonglinzhang/article/details/75559735