共计 9159 个字符,预计需要花费 23 分钟才能阅读完成。
1. 前言 neutron 在安装配置完成之后,openstack 为了实现对所有 tenant 对网络资源的使用,针对 neutron 设置有专门的配额,以防止租户使用过多的资源,而对其他的 tenant 造成影响。和 nova 的 quota 相类似,neutron 也使用单独的一个驱动来实现网络 neutron 的配额控制。2. neutron 默认的配额 neutron 默认的配额针对 network,port,router,subnet,floatingip 做了配额方面的限定,参考 neutron 的配置文件,获取 quota 的配额内容为:
[root@controller ~]# vim /etc/neutron/neutron.conf
[quotas]
quota_driver = neutron.db.quota_db.DbQuotaDriver 配额驱动
quota_items = network,subnet,port quota 限定的范畴
default_quota = -1 默认的 quota,- 1 表示没有限制 (未启用)
quota_network = 10 建立的 network 个数
quota_subnet = 10 建立的 subnet 个数
quota_port = 50 允许的 port 个数
quota_security_group = 10 安全组的个数
quota_security_group_rule = 100 安全组规规则条数
quota_vip = 10 vip 个数,以下的 quota_member 和 quota_health_monitors 都用于 LBaaS 场景
quota_pool = 10 pool 个数
quota_member = -1 member 个数
quota_health_monitors = -1 monitor 个数
quota_router = 10 router 的个数
quota_floatingip = 50 floating-ip 个数
3. 修改 neutron 的配额查看 neutron 默认的配额
[root@controller ~]# keystone tenant-list
+———————————-+———-+———+
| id | name | enabled |
+———————————-+———-+———+
| 842ab3268a2c47e6a4b0d8774de805ae | admin | True |
| 7ff1dfb5a6f349958c3a949248e56236 | companyA | True | #得到 tenant 的 uuid 号
| 10d1465c00d049fab88dec1af0f56b1b | demo | True |
| 3b57a14f7c354a979c9f62b60f31a331 | service | True |
+———————————-+———-+———+
[root@controller ~]# neutron quota-show –tenant-id 7ff1dfb5a6f349958c3a949248e56236
+———————+——-+
| Field | Value |
+———————+——-+
| floatingip | 50 |
| health_monitor | -1 |
| member | -1 |
| network | 10 |
| pool | 10 |
| port | 50 | #port,每台虚拟机都需要一个 ip,即一个 port,很容易就超过配额
| router | 10 |
| security_group | 10 |
| security_group_rule | 100 |
| subnet | 10 |
| vip | 10 |
+———————+——-+
修改 neutron 配额
[root@controller ~]# neutron quota-update –network 20 –subnet 20 –port 100 –router 5 –floatingip 100 –security-group 10 –security-group-rule 100 –tenant-id 7ff1dfb5a6f349958c3a949248e56236
+———————+——-+
| Field | Value |
+———————+——-+
| floatingip | 100 |
| health_monitor | -1 |
| member | -1 |
| network | 20 |
| pool | 10 |
| port | 100 |
| router | 5 |
| security_group | 10 |
| security_group_rule | 100 |
| subnet | 20 |
| vip | 10 |
+———————+——-+
校验 neutron 的 quota 配置
[root@controller ~]# neutron quota-show –tenant-id 7ff1dfb5a6f349958c3a949248e56236
+———————+——-+
| Field | Value |
+———————+——-+
| floatingip | 100 |
| health_monitor | -1 |
| member | -1 |
| network | 20 |
| pool | 10 |
| port | 100 |
| router | 5 |
| security_group | 10 |
| security_group_rule | 100 |
| subnet | 20 |
| vip | 10 |
+———————+——-+
4. 统计 port 的个数
[root@controller ~]# neutron port-list
+————————————–+——+——————-+—————————————————————————————+
| id | name | mac_address | fixed_ips |
+————————————–+——+——————-+—————————————————————————————+
| 0060ec4a-957d-4571-b730-6b4a9bb3baf8 | | fa:16:3e:48:42:3d | {“subnet_id”: “9654a807-d4fa-49f1-abb6-2e45d776c69f”, “ip_address”: “10.16.4.19”} |
| 00942be0-a3a9-471d-a4ba-336db0ee1539 | | fa:16:3e:73:75:03 | {“subnet_id”: “ad4a5ffc-3ccc-42c4-89a1-61e7b18632a3”, “ip_address”: “10.16.6.96”} |
| 0119045c-8219-4744-bd58-a7e77294832c | | fa:16:3e:10:ed:7f | {“subnet_id”: “9654a807-d4fa-49f1-abb6-2e45d776c69f”, “ip_address”: “10.16.4.71”} |
| 04f7d8ea-1849-4938-9ef7-e8114893132f | | fa:16:3e:50:86:1b | {“subnet_id”: “ad4a5ffc-3ccc-42c4-89a1-61e7b18632a3”, “ip_address”: “10.16.6.27”} |
[root@controller ~]# neutron port-list |wc -l #超过配额时,需要修改
194
5. 总结随着时间的推移,当越来越多的 instance 加入到 openstack 中,port 也会相应增加,一个 ip 对应一个 port,所以当 port 达到配额时,openstack 会组织用户继续分配虚拟机,此时,就需要修改 neutron 的配额了,关于 neutron 配额的报错,可以参考 neutron 的日志 /var/log/neutron/neutron-server.log,可以根据日志的信息,定位到报错的原因,具体不赘述。6. 附录 neutron 实现 quota 的代码解读
[root@controller ~]# vim /usr/lib/python2.6/site-packages/neutron/db/quota_db.py
import sqlalchemy as sa
from neutron.common import exceptions
from neutron.db import model_base
from neutron.db import models_v2
”’
quota 数据库表的表结构,tenant 默认集成的配额从这里获取
mysql> desc quotas;
+———–+————–+——+—–+———+——-+
| Field | Type | Null | Key | Default | Extra |
+———–+————–+——+—–+———+——-+
| id | varchar(36) | NO | PRI | NULL | |
| tenant_id | varchar(255) | YES | MUL | NULL | |
| resource | varchar(255) | YES | | NULL | |
| limit | int(11) | YES | | NULL | |
+———–+————–+——+—–+———+——-+
”’
class Quota(model_base.BASEV2, models_v2.HasId):
“””Represent a single quota override for a tenant.
If there is no row for a given tenant id and resource, then the
default for the quota class is used.
“””
tenant_id = sa.Column(sa.String(255), index=True)
resource = sa.Column(sa.String(255))
limit = sa.Column(sa.Integer)
”’
quota 配额的具体实现,根据数据库的配置内容,实现 quota 的控制,即 quota 的增删改查方法
”’
class DbQuotaDriver(object):
“””Driver to perform necessary checks to enforce quotas and obtain quota
information.
The default driver utilizes the local database.
“””
”’
得到租户 tenant 的 quota,执行 neutron quota-show –tenant-id uuid 时调用的方法
”’
@staticmethod
def get_tenant_quotas(context, resources, tenant_id):
“””Given a list of resources, retrieve the quotas for the given
tenant.
:param context: The request context, for access checks.
:param resources: A dictionary of the registered resource keys.
:param tenant_id: The ID of the tenant to return quotas for.
:return dict: from resource name to dict of name and limit
“””
# init with defaults 得到 quota 默认的配额项 item,即所谓的 network,subnet,port 和 router 等,以及对应的值
tenant_quota = dict((key, resource.default)
for key, resource in resources.items())
# update with tenant specific limits 从数据库中获取最新的 quota 配置信息,并更新
q_qry = context.session.query(Quota).filter_by(tenant_id=tenant_id)
tenant_quota.update((q[‘resource’], q[‘limit’]) for q in q_qry)
return tenant_quota
”’
quota 的删除,即执行 neutron quota-delete 的方法,删除之后,tenant 将会集成默认的的 quota 配置
”’
@staticmethod
def delete_tenant_quota(context, tenant_id):
“””Delete the quota entries for a given tenant_id.
Atfer deletion, this tenant will use default quota values in conf.
“””
#从 neutron。quotas 数据库中查询到所有的 quota 配置之后,过略某个具体的 tenant 的 quota,之后执行 delete() 方法将其删除
with context.session.begin():
tenant_quotas = context.session.query(Quota)
tenant_quotas = tenant_quotas.filter_by(tenant_id=tenant_id)
tenant_quotas.delete()
”’
得到所有租户 tenant 的配额资源,即执行 neutron quota-list 所查看的内容
”’
@staticmethod
def get_all_quotas(context, resources):
“””Given a list of resources, retrieve the quotas for the all tenants.
:param context: The request context, for access checks.
:param resources: A dictionary of the registered resource keys.
:return quotas: list of dict of tenant_id:, resourcekey1:
resourcekey2: …
“””
tenant_default = dict((key, resource.default)
for key, resource in resources.items())
all_tenant_quotas = {}
for quota in context.session.query(Quota):
tenant_id = quota[‘tenant_id’]
# avoid setdefault() because only want to copy when actually req’d
#如果 quotas 表中,没有找到配置选项,说明使用默认的 quota 配置,直接用默认的 copy 过来即可,有配置则继承 quotas 表中的配置
tenant_quota = all_tenant_quotas.get(tenant_id)
if tenant_quota is None:
tenant_quota = tenant_default.copy()
tenant_quota[‘tenant_id’] = tenant_id
all_tenant_quotas[tenant_id] = tenant_quota
tenant_quota[quota[‘resource’]] = quota[‘limit’]
return all_tenant_quotas.values()
”’
更新 quota 的配置,即执行 neutron quota-update 命令的具体实现
”’
@staticmethod
def update_quota_limit(context, tenant_id, resource, limit):
with context.session.begin():
tenant_quota = context.session.query(Quota).filter_by(
tenant_id=tenant_id, resource=resource).first()
#有配置内容,则更新,没有则根据资源的配置内容,在数据库中添加对应的条目
if tenant_quota:
tenant_quota.update({‘limit’: limit})
else:
tenant_quota = Quota(tenant_id=tenant_id,
resource=resource,
limit=limit)
context.session.add(tenant_quota)
def _get_quotas(self, context, tenant_id, resources, keys):
“””Retrieves the quotas for specific resources.
A helper method which retrieves the quotas for the specific
resources identified by keys, and which apply to the current
context.
:param context: The request context, for access checks.
:param tenant_id: the tenant_id to check quota.
:param resources: A dictionary of the registered resources.
:param keys: A list of the desired quotas to retrieve.
“””
desired = set(keys)
sub_resources = dict((k, v) for k, v in resources.items()
if k in desired)
# Make sure we accounted for all of them…
if len(keys) != len(sub_resources):
unknown = desired – set(sub_resources.keys())
raise exceptions.QuotaResourceUnknown(unknown=sorted(unknown))
# Grab and return the quotas (without usages)
quotas = DbQuotaDriver.get_tenant_quotas(
context, sub_resources, tenant_id)
return dict((k, v) for k, v in quotas.items())
”’
neutron quota 的校验,即在执行过程中,调用该方法,确认 tenant 的 quota 是否在合理的范围内
”’
def limit_check(self, context, tenant_id, resources, values):
“””Check simple quota limits.
For limits–those quotas for which there is no usage
synchronization function–this method checks that a set of
proposed values are permitted by the limit restriction.
This method will raise a QuotaResourceUnknown exception if a
given resource is unknown or if it is not a simple limit
resource.
If any of the proposed values is over the defined quota, an
OverQuota exception will be raised with the sorted list of the
resources which are too high. Otherwise, the method returns
nothing.
:param context: The request context, for access checks.
:param tenant_id: The tenant_id to check the quota.
:param resources: A dictionary of the registered resources.
:param values: A dictionary of the values to check against the
quota.
“””
# Ensure no value is less than zero quota 的配置值不能为负数
unders = [key for key, val in values.items() if val < 0]
if unders:
raise exceptions.InvalidQuotaValue(unders=sorted(unders))
# Get the applicable quotas
quotas = self._get_quotas(context, tenant_id, resources, values.keys())
# Check the quotas and construct a list of the resources that
# would be put over limit by the desired values
overs = [key for key, val in values.items()
if quotas[key] >= 0 and quotas[key] < val]
if overs:
raise exceptions.OverQuota(overs=sorted(overs))