今天测试部的说,平台上创建两个相同名字的子网以及相同cidr的子网,严重影响一些根据名字和cidr进行的一些常规操作,
应要求修改代码使得不能创建相同名字以及相同cidr的子网。
过程如下,大神请绕过,有问题请多多指教!
创建子网的接口,这里直接从插件的接口开始,如下:
vim .../neutron/plugins/ml2/plugin.py
@utils.transaction_guard
@db_api.retry_if_session_inactive()
def create_subnet(self, context, subnet):
result, mech_context = self._create_subnet_db(context, subnet)
kwargs = {'context': context, 'subnet': result}
registry.notify(resources.SUBNET, events.AFTER_CREATE, self, **kwargs)
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
注释:(1)result, mech_context = self._create_subnet_db(context, subnet)
这里主要进行数据库操作,保存子网信息到数据库
使用户不能创建同名字以及同cidr的子网,需要查询数据库中是否有对应的子网,如果有,则报错“已存在,不能创建”,如果没有,则创建
vim .../neutron/plugins/ml2/plugin.py
def _create_subnet_db(self, context, subnet):
session = context.session
# FIXME(kevinbenton): this is a mess because create_subnet ends up
# calling _update_router_gw_ports which ends up calling update_port
# on a router port inside this transaction. Need to find a way to
# separate router updates from the subnet update operation.
setattr(context, 'GUARD_TRANSACTION', False)
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
注释:class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
dvr_mac_db.DVRDbMixin,
external_net_db.External_net_db_mixin,
sg_db_rpc.SecurityGroupServerRpcMixin,
agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
addr_pair_db.AllowedAddressPairsMixin,
vlantransparent_db.Vlantransparent_db_mixin,
extradhcpopt_db.ExtraDhcpOptMixin,
address_scope_db.AddressScopeDbMixin,
service_type_db.SubnetServiceTypeMixin):...
with session.begin(subtransactions=True):
result = super(Ml2Plugin, self).create_subnet(context, subnet)
连接到数据库后,调用class NeutronDbPluginV2类中的create_subnet()
vim .../nuetron/db/db_base_plugin_v2.py
@db_api.retry_if_session_inactive()
def create_subnet(self, context, subnet):
s = subnet['subnet']
cidr = s.get('cidr', constants.ATTR_NOT_SPECIFIED)
prefixlen = s.get('prefixlen', constants.ATTR_NOT_SPECIFIED)
has_cidr = validators.is_attr_set(cidr)
has_prefixlen = validators.is_attr_set(prefixlen)
if has_cidr and has_prefixlen:
msg = _('cidr and prefixlen must not be supplied together')
raise 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 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 = n_const.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 exc.BadRequest(resource='subnets', msg=msg)
self._validate_subnet(context, s)
return self._create_subnet(context, subnet, subnetpool_id)
注:前面是一些参数分析,这里我们只关注self._create_subnet(context, subnet, subnetpool_id)
vim .../nuetron/db/db_base_plugin_v2.py
def _create_subnet(self, context, subnet, subnetpool_id):
s = subnet['subnet']
with context.session.begin(subtransactions=True):
network = self._get_network(context, s["network_id"])
subnet, ipam_subnet = self.ipam.allocate_subnet(context,
network,
s,
subnetpool_id)
if hasattr(network, 'external') and network.external:
self._update_router_gw_ports(context,
network,
subnet)
# If this subnet supports auto-addressing, then update any
# internal ports on the network with addresses for this subnet.
if ipv6_utils.is_auto_address_subnet(subnet):
updated_ports = self.ipam.add_auto_addrs_on_network_ports(context,
subnet, ipam_subnet)
for port_id in updated_ports:
port_info = {'port': {'id': port_id}}
self.update_port(context, port_id, port_info)
return self._make_subnet_dict(subnet, context=context)
至此完成数据库的操作。
修改代码:
vim .../nuetron/db/db_base_plugin_v2.py
def _create_subnet(self, context, subnet, subnetpool_id):
s = subnet['subnet']
#-------------------------------------------------------------------
#获取待创建的子网名以及cidr
subnet_cidr = subnet['subnet']['cidr']
subnet_name = subnet['subnet']['name']
#-------------------------------------------------------------------
with context.session.begin(subtransactions=True):
#-------------------------------------------------------------------
#通过子网名和cidr判断数据库中是否存在相同的子网
subnet_exist_by_cidr = self._get_subnet_by_cidr(context,subnet_cidr)
subnet_exist_by_name = self._get_subnet_by_name(context,subnet_name)
if subnet_exist_by_cidr or subnet_exist_by_name:
msg = _('subnet has exist,please create other cidr subnet ')
raise exc.BadRequest(resource='subnets', msg=msg)
if subnet_exist_by_name:
msg = _('same name subnet has exist,please create other name subnet ')
#-------------------------------------------------------------------
raise exc.BadRequest(resource='subnets', msg=msg)
network = self._get_network(context, s["network_id"])
subnet, ipam_subnet = self.ipam.allocate_subnet(context,
network,
s,
subnetpool_id)
if hasattr(network, 'external') and network.external:
self._update_router_gw_ports(context,
network,
subnet)
# If this subnet supports auto-addressing, then update any
# internal ports on the network with addresses for this subnet.
if ipv6_utils.is_auto_address_subnet(subnet):
updated_ports = self.ipam.add_auto_addrs_on_network_ports(context,
subnet, ipam_subnet)
for port_id in updated_ports:
port_info = {'port': {'id': port_id}}
self.update_port(context, port_id, port_info)
return self._make_subnet_dict(subnet, context=context)
#通过cidr以及name查询Subnet的数据库,如下:
vim .../neutron/db/db_base_plugin_common.py
class DbBasePluginCommon(common_db_mixin.CommonDbMixin):
#-------------------------------------------------------------------
def _get_subnet_by_cidr(self,context,cidr):
try:
subnet = self._get_by_cidr(context, models_v2.Subnet, cidr)
except exc.NoResultFound:
return None
return subnet
def _get_subnet_by_name(self,context,name):
try:
subnet = self._get_by_name(context, models_v2.Subnet, name)
except exc.NoResultFound:
return None
return subnet
#-------------------------------------------------------------------
Subnet表的属性如下:models_v2.Subnet 子网对应的表
vim .../db/models_v2.py
class Subnet(standard_attr.HasStandardAttributes, model_base.BASEV2,
model_base.HasId, model_base.HasProject):
"""Represents a neutron subnet.
When a subnet is created the first and last entries will be created. These
are used for the IP allocation.
"""
name = sa.Column(sa.String(attr.NAME_MAX_LEN))
network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id'))
# Added by the segments service plugin
segment_id = sa.Column(sa.String(36), sa.ForeignKey('networksegments.id'))
subnetpool_id = sa.Column(sa.String(36), index=True)
# NOTE: Explicitly specify join conditions for the relationship because
# subnetpool_id in subnet might be 'prefix_delegation' when the IPv6 Prefix
# Delegation is enabled
subnetpool = orm.relationship(
'SubnetPool', lazy='joined',
foreign_keys='Subnet.subnetpool_id',
primaryjoin='Subnet.subnetpool_id==SubnetPool.id')
ip_version = sa.Column(sa.Integer, nullable=False)
cidr = sa.Column(sa.String(64), nullable=False)
gateway_ip = sa.Column(sa.String(64))
revises_on_change = ('networks', )
allocation_pools = orm.relationship(IPAllocationPool,
backref='subnet',
lazy="subquery",
cascade='delete')
enable_dhcp = sa.Column(sa.Boolean())
dns_nameservers = orm.relationship(DNSNameServer,
backref='subnet',
cascade='all, delete, delete-orphan',
order_by=DNSNameServer.order,
lazy='subquery')
routes = orm.relationship(SubnetRoute,
backref='subnet',
cascade='all, delete, delete-orphan',
lazy='subquery')
ipv6_ra_mode = sa.Column(sa.Enum(constants.IPV6_SLAAC,
constants.DHCPV6_STATEFUL,
constants.DHCPV6_STATELESS,
name='ipv6_ra_modes'),
nullable=True)
ipv6_address_mode = sa.Column(sa.Enum(constants.IPV6_SLAAC,
constants.DHCPV6_STATEFUL,
constants.DHCPV6_STATELESS,
name='ipv6_address_modes'),
nullable=True)
# subnets don't have their own rbac_entries, they just inherit from
# the network rbac entries
rbac_entries = orm.relationship(
rbac_db_models.NetworkRBAC, lazy='subquery', uselist=True,
foreign_keys='Subnet.network_id',
primaryjoin='Subnet.network_id==NetworkRBAC.object_id')
api_collections = [attr.SUBNETS]
#这里我们只关注name和cidr两个属性:
name = sa.Column(sa.String(attr.NAME_MAX_LEN))
cidr = sa.Column(sa.String(64), nullable=False)
vim .../neutron/db/common_db_mixin.py
class CommonDbMixin(object):
#-------------------------------------------------------------------
def _get_by_cidr(self, context, model, cidr):
query = self._model_query(context, model)
return query.filter(model.cidr == cidr).first()
def _get_by_name(self, context, model, name):
query = self._model_query(context, model)
return query.filter(model.name == name).first()
#-------------------------------------------------------------------
#query.filter(model.cidr == cidr).first()
#query.filter(model.name == name).first() 查询并返回第一个实例Subnet
至此,修改完成,重启neutron-server服务,测试达到理想效果。
补充一下,这里修改的方法并不是唯一,只是通过和数据库已有的数据库做比较的途径来实现;
还有一种方法就是,设计子网的表时,限定name和cidr是主键,不能在表格中重复,可以达到name和cidr不能同时相同,
但可以有一个相同(算是一个不足吧),并且要求就是升级数据库版本(算是第二个不足吧)。
应要求修改代码使得不能创建相同名字以及相同cidr的子网。
过程如下,大神请绕过,有问题请多多指教!
创建子网的接口,这里直接从插件的接口开始,如下:
vim .../neutron/plugins/ml2/plugin.py
@utils.transaction_guard
@db_api.retry_if_session_inactive()
def create_subnet(self, context, subnet):
result, mech_context = self._create_subnet_db(context, subnet)
kwargs = {'context': context, 'subnet': result}
registry.notify(resources.SUBNET, events.AFTER_CREATE, self, **kwargs)
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
注释:(1)result, mech_context = self._create_subnet_db(context, subnet)
这里主要进行数据库操作,保存子网信息到数据库
使用户不能创建同名字以及同cidr的子网,需要查询数据库中是否有对应的子网,如果有,则报错“已存在,不能创建”,如果没有,则创建
vim .../neutron/plugins/ml2/plugin.py
def _create_subnet_db(self, context, subnet):
session = context.session
# FIXME(kevinbenton): this is a mess because create_subnet ends up
# calling _update_router_gw_ports which ends up calling update_port
# on a router port inside this transaction. Need to find a way to
# separate router updates from the subnet update operation.
setattr(context, 'GUARD_TRANSACTION', False)
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
注释:class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
dvr_mac_db.DVRDbMixin,
external_net_db.External_net_db_mixin,
sg_db_rpc.SecurityGroupServerRpcMixin,
agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
addr_pair_db.AllowedAddressPairsMixin,
vlantransparent_db.Vlantransparent_db_mixin,
extradhcpopt_db.ExtraDhcpOptMixin,
address_scope_db.AddressScopeDbMixin,
service_type_db.SubnetServiceTypeMixin):...
with session.begin(subtransactions=True):
result = super(Ml2Plugin, self).create_subnet(context, subnet)
连接到数据库后,调用class NeutronDbPluginV2类中的create_subnet()
vim .../nuetron/db/db_base_plugin_v2.py
@db_api.retry_if_session_inactive()
def create_subnet(self, context, subnet):
s = subnet['subnet']
cidr = s.get('cidr', constants.ATTR_NOT_SPECIFIED)
prefixlen = s.get('prefixlen', constants.ATTR_NOT_SPECIFIED)
has_cidr = validators.is_attr_set(cidr)
has_prefixlen = validators.is_attr_set(prefixlen)
if has_cidr and has_prefixlen:
msg = _('cidr and prefixlen must not be supplied together')
raise 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 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 = n_const.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 exc.BadRequest(resource='subnets', msg=msg)
self._validate_subnet(context, s)
return self._create_subnet(context, subnet, subnetpool_id)
注:前面是一些参数分析,这里我们只关注self._create_subnet(context, subnet, subnetpool_id)
vim .../nuetron/db/db_base_plugin_v2.py
def _create_subnet(self, context, subnet, subnetpool_id):
s = subnet['subnet']
with context.session.begin(subtransactions=True):
network = self._get_network(context, s["network_id"])
subnet, ipam_subnet = self.ipam.allocate_subnet(context,
network,
s,
subnetpool_id)
if hasattr(network, 'external') and network.external:
self._update_router_gw_ports(context,
network,
subnet)
# If this subnet supports auto-addressing, then update any
# internal ports on the network with addresses for this subnet.
if ipv6_utils.is_auto_address_subnet(subnet):
updated_ports = self.ipam.add_auto_addrs_on_network_ports(context,
subnet, ipam_subnet)
for port_id in updated_ports:
port_info = {'port': {'id': port_id}}
self.update_port(context, port_id, port_info)
return self._make_subnet_dict(subnet, context=context)
至此完成数据库的操作。
修改代码:
vim .../nuetron/db/db_base_plugin_v2.py
def _create_subnet(self, context, subnet, subnetpool_id):
s = subnet['subnet']
#-------------------------------------------------------------------
#获取待创建的子网名以及cidr
subnet_cidr = subnet['subnet']['cidr']
subnet_name = subnet['subnet']['name']
#-------------------------------------------------------------------
with context.session.begin(subtransactions=True):
#-------------------------------------------------------------------
#通过子网名和cidr判断数据库中是否存在相同的子网
subnet_exist_by_cidr = self._get_subnet_by_cidr(context,subnet_cidr)
subnet_exist_by_name = self._get_subnet_by_name(context,subnet_name)
if subnet_exist_by_cidr or subnet_exist_by_name:
msg = _('subnet has exist,please create other cidr subnet ')
raise exc.BadRequest(resource='subnets', msg=msg)
if subnet_exist_by_name:
msg = _('same name subnet has exist,please create other name subnet ')
#-------------------------------------------------------------------
raise exc.BadRequest(resource='subnets', msg=msg)
network = self._get_network(context, s["network_id"])
subnet, ipam_subnet = self.ipam.allocate_subnet(context,
network,
s,
subnetpool_id)
if hasattr(network, 'external') and network.external:
self._update_router_gw_ports(context,
network,
subnet)
# If this subnet supports auto-addressing, then update any
# internal ports on the network with addresses for this subnet.
if ipv6_utils.is_auto_address_subnet(subnet):
updated_ports = self.ipam.add_auto_addrs_on_network_ports(context,
subnet, ipam_subnet)
for port_id in updated_ports:
port_info = {'port': {'id': port_id}}
self.update_port(context, port_id, port_info)
return self._make_subnet_dict(subnet, context=context)
#通过cidr以及name查询Subnet的数据库,如下:
vim .../neutron/db/db_base_plugin_common.py
class DbBasePluginCommon(common_db_mixin.CommonDbMixin):
#-------------------------------------------------------------------
def _get_subnet_by_cidr(self,context,cidr):
try:
subnet = self._get_by_cidr(context, models_v2.Subnet, cidr)
except exc.NoResultFound:
return None
return subnet
def _get_subnet_by_name(self,context,name):
try:
subnet = self._get_by_name(context, models_v2.Subnet, name)
except exc.NoResultFound:
return None
return subnet
#-------------------------------------------------------------------
Subnet表的属性如下:models_v2.Subnet 子网对应的表
vim .../db/models_v2.py
class Subnet(standard_attr.HasStandardAttributes, model_base.BASEV2,
model_base.HasId, model_base.HasProject):
"""Represents a neutron subnet.
When a subnet is created the first and last entries will be created. These
are used for the IP allocation.
"""
name = sa.Column(sa.String(attr.NAME_MAX_LEN))
network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id'))
# Added by the segments service plugin
segment_id = sa.Column(sa.String(36), sa.ForeignKey('networksegments.id'))
subnetpool_id = sa.Column(sa.String(36), index=True)
# NOTE: Explicitly specify join conditions for the relationship because
# subnetpool_id in subnet might be 'prefix_delegation' when the IPv6 Prefix
# Delegation is enabled
subnetpool = orm.relationship(
'SubnetPool', lazy='joined',
foreign_keys='Subnet.subnetpool_id',
primaryjoin='Subnet.subnetpool_id==SubnetPool.id')
ip_version = sa.Column(sa.Integer, nullable=False)
cidr = sa.Column(sa.String(64), nullable=False)
gateway_ip = sa.Column(sa.String(64))
revises_on_change = ('networks', )
allocation_pools = orm.relationship(IPAllocationPool,
backref='subnet',
lazy="subquery",
cascade='delete')
enable_dhcp = sa.Column(sa.Boolean())
dns_nameservers = orm.relationship(DNSNameServer,
backref='subnet',
cascade='all, delete, delete-orphan',
order_by=DNSNameServer.order,
lazy='subquery')
routes = orm.relationship(SubnetRoute,
backref='subnet',
cascade='all, delete, delete-orphan',
lazy='subquery')
ipv6_ra_mode = sa.Column(sa.Enum(constants.IPV6_SLAAC,
constants.DHCPV6_STATEFUL,
constants.DHCPV6_STATELESS,
name='ipv6_ra_modes'),
nullable=True)
ipv6_address_mode = sa.Column(sa.Enum(constants.IPV6_SLAAC,
constants.DHCPV6_STATEFUL,
constants.DHCPV6_STATELESS,
name='ipv6_address_modes'),
nullable=True)
# subnets don't have their own rbac_entries, they just inherit from
# the network rbac entries
rbac_entries = orm.relationship(
rbac_db_models.NetworkRBAC, lazy='subquery', uselist=True,
foreign_keys='Subnet.network_id',
primaryjoin='Subnet.network_id==NetworkRBAC.object_id')
api_collections = [attr.SUBNETS]
#这里我们只关注name和cidr两个属性:
name = sa.Column(sa.String(attr.NAME_MAX_LEN))
cidr = sa.Column(sa.String(64), nullable=False)
vim .../neutron/db/common_db_mixin.py
class CommonDbMixin(object):
#-------------------------------------------------------------------
def _get_by_cidr(self, context, model, cidr):
query = self._model_query(context, model)
return query.filter(model.cidr == cidr).first()
def _get_by_name(self, context, model, name):
query = self._model_query(context, model)
return query.filter(model.name == name).first()
#-------------------------------------------------------------------
#query.filter(model.cidr == cidr).first()
#query.filter(model.name == name).first() 查询并返回第一个实例Subnet
至此,修改完成,重启neutron-server服务,测试达到理想效果。
补充一下,这里修改的方法并不是唯一,只是通过和数据库已有的数据库做比较的途径来实现;
还有一种方法就是,设计子网的表时,限定name和cidr是主键,不能在表格中重复,可以达到name和cidr不能同时相同,
但可以有一个相同(算是一个不足吧),并且要求就是升级数据库版本(算是第二个不足吧)。