天天看點

使neutron不能建立相同名字以及相同cidr的子網

今天測試部的說,平台上建立兩個相同名字的子網以及相同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不能同時相同,

但可以有一個相同(算是一個不足吧),并且要求就是更新資料庫版本(算是第二個不足吧)。