Add OpenStack volume quota
This adds support for staying within OpenStack volume quota limits on instances that utilize boot-from-volume. Change-Id: I1b7bc177581d23cecd9443a392fb058176409c46
This commit is contained in:
parent
f399292401
commit
de02ac5a20
@ -588,4 +588,18 @@ Options
|
|||||||
:default: infinity
|
:default: infinity
|
||||||
:type: int
|
:type: int
|
||||||
|
|
||||||
The maximum number of main memory (RAM) a tenant can allocate.
|
The maximum amount of main memory (RAM) a tenant can allocate.
|
||||||
|
|
||||||
|
.. attr:: max-volumes
|
||||||
|
:default: infinity
|
||||||
|
:type: int
|
||||||
|
|
||||||
|
The maximum number of volumes a tenant can allocate. Currently
|
||||||
|
only used by the OpenStack driver.
|
||||||
|
|
||||||
|
.. attr:: max-volume-gb
|
||||||
|
:default: infinity
|
||||||
|
:type: int
|
||||||
|
|
||||||
|
The maximum total size in gigabytes of volumes a tenant can
|
||||||
|
allocate. Currently only used by the OpenStack driver.
|
||||||
|
@ -460,6 +460,22 @@ Selecting the OpenStack driver adds the following options to the
|
|||||||
the amount of ram allocated by nodepool. If not defined
|
the amount of ram allocated by nodepool. If not defined
|
||||||
nodepool can use as much ram as the tenant allows.
|
nodepool can use as much ram as the tenant allows.
|
||||||
|
|
||||||
|
.. attr:: max-volumes
|
||||||
|
:type: int
|
||||||
|
|
||||||
|
Maximum number of volumes usable from this pool. This can be
|
||||||
|
used to limit the number of volumes allocated by nodepool. If
|
||||||
|
not defined, nodepool can use as many volumes as the tenant
|
||||||
|
allows.
|
||||||
|
|
||||||
|
.. attr:: max-volume-gb
|
||||||
|
:type: int
|
||||||
|
|
||||||
|
Maximum total size in gigabytes of volumes usable from this
|
||||||
|
pool. This can be used to limit the volume storage allocated
|
||||||
|
by nodepool. If not defined, nodepool can use as much space
|
||||||
|
as the tenant allows.
|
||||||
|
|
||||||
.. attr:: ignore-provider-quota
|
.. attr:: ignore-provider-quota
|
||||||
:type: bool
|
:type: bool
|
||||||
:default: False
|
:default: False
|
||||||
|
@ -261,6 +261,11 @@ class Config(ConfigValue):
|
|||||||
limits['cores'] = resource_limit.pop('max-cores', math.inf)
|
limits['cores'] = resource_limit.pop('max-cores', math.inf)
|
||||||
limits['instances'] = resource_limit.pop('max-servers', math.inf)
|
limits['instances'] = resource_limit.pop('max-servers', math.inf)
|
||||||
limits['ram'] = resource_limit.pop('max-ram', math.inf)
|
limits['ram'] = resource_limit.pop('max-ram', math.inf)
|
||||||
|
if 'max-volumes' in resource_limit:
|
||||||
|
limits['volumes'] = resource_limit.pop('max-volumes', math.inf)
|
||||||
|
if 'max-volume-gb' in resource_limit:
|
||||||
|
limits['volume-gb'] = resource_limit.pop(
|
||||||
|
'max-volume-gb', math.inf)
|
||||||
for k, v in resource_limit.items():
|
for k, v in resource_limit.items():
|
||||||
limits[k] = v
|
limits[k] = v
|
||||||
self.tenant_resource_limits[tenant_name] = limits
|
self.tenant_resource_limits[tenant_name] = limits
|
||||||
|
@ -77,6 +77,10 @@ class FakeOpenStackCloud(object):
|
|||||||
def _get_quota():
|
def _get_quota():
|
||||||
return 100, 20, 1000000
|
return 100, 20, 1000000
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_volume_quota():
|
||||||
|
return 100, 1000000
|
||||||
|
|
||||||
def __init__(self, images=None, networks=None):
|
def __init__(self, images=None, networks=None):
|
||||||
self.pause_creates = False
|
self.pause_creates = False
|
||||||
self._image_list = images
|
self._image_list = images
|
||||||
@ -117,10 +121,13 @@ class FakeOpenStackCloud(object):
|
|||||||
device_owner=None),
|
device_owner=None),
|
||||||
]
|
]
|
||||||
self._floating_ip_list = []
|
self._floating_ip_list = []
|
||||||
|
self._volume_list = []
|
||||||
|
|
||||||
def _update_quota(self):
|
def _update_quota(self):
|
||||||
self.max_cores, self.max_instances, self.max_ram = FakeOpenStackCloud.\
|
self.max_cores, self.max_instances, self.max_ram = FakeOpenStackCloud.\
|
||||||
_get_quota()
|
_get_quota()
|
||||||
|
self.max_volumes, self.max_volume_gb = FakeOpenStackCloud.\
|
||||||
|
_get_volume_quota()
|
||||||
|
|
||||||
def _get(self, name_or_id, instance_list):
|
def _get(self, name_or_id, instance_list):
|
||||||
self.log.debug("Get %s in %s" % (name_or_id, repr(instance_list)))
|
self.log.debug("Get %s in %s" % (name_or_id, repr(instance_list)))
|
||||||
@ -211,6 +218,7 @@ class FakeOpenStackCloud(object):
|
|||||||
over_quota=over_quota,
|
over_quota=over_quota,
|
||||||
flavor=kw.get('flavor'),
|
flavor=kw.get('flavor'),
|
||||||
event=threading.Event(),
|
event=threading.Event(),
|
||||||
|
volumes=[],
|
||||||
_kw=kw)
|
_kw=kw)
|
||||||
instance_list.append(s)
|
instance_list.append(s)
|
||||||
t = threading.Thread(target=self._finish,
|
t = threading.Thread(target=self._finish,
|
||||||
@ -325,6 +333,18 @@ class FakeOpenStackCloud(object):
|
|||||||
total_ram_used=8192 * len(self._server_list)
|
total_ram_used=8192 * len(self._server_list)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_volume_limits(self):
|
||||||
|
self._update_quota()
|
||||||
|
return Dummy(
|
||||||
|
'limits',
|
||||||
|
absolute={
|
||||||
|
'maxTotalVolumes': self.max_volumes,
|
||||||
|
'maxTotalVolumeGigabytes': self.max_volume_gb,
|
||||||
|
})
|
||||||
|
|
||||||
|
def list_volumes(self):
|
||||||
|
return self._volume_list
|
||||||
|
|
||||||
def list_ports(self, filters=None):
|
def list_ports(self, filters=None):
|
||||||
if filters and filters.get('status') == 'DOWN':
|
if filters and filters.get('status') == 'DOWN':
|
||||||
return self._down_ports
|
return self._down_ports
|
||||||
|
@ -19,11 +19,13 @@
|
|||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
|
import math
|
||||||
import time
|
import time
|
||||||
import operator
|
import operator
|
||||||
|
|
||||||
import cachetools.func
|
import cachetools.func
|
||||||
import openstack
|
import openstack
|
||||||
|
from keystoneauth1.exceptions.catalog import EndpointNotFound
|
||||||
|
|
||||||
from nodepool.driver.utils import QuotaInformation
|
from nodepool.driver.utils import QuotaInformation
|
||||||
from nodepool.driver import statemachine
|
from nodepool.driver import statemachine
|
||||||
@ -34,6 +36,36 @@ from nodepool import version
|
|||||||
CACHE_TTL = 10
|
CACHE_TTL = 10
|
||||||
|
|
||||||
|
|
||||||
|
def quota_from_flavor(flavor, label=None, volumes=None):
|
||||||
|
args = dict(instances=1,
|
||||||
|
cores=flavor.vcpus,
|
||||||
|
ram=flavor.ram)
|
||||||
|
if label and label.boot_from_volume:
|
||||||
|
args['volumes'] = 1
|
||||||
|
args['volume-gb'] = label.volume_size
|
||||||
|
elif volumes:
|
||||||
|
args['volumes'] = len(volumes)
|
||||||
|
args['volume-gb'] = sum([v.size for v in volumes])
|
||||||
|
return QuotaInformation(**args)
|
||||||
|
|
||||||
|
|
||||||
|
def quota_from_limits(compute, volume):
|
||||||
|
def bound_value(value):
|
||||||
|
if value == -1:
|
||||||
|
return math.inf
|
||||||
|
return value
|
||||||
|
|
||||||
|
args = dict(
|
||||||
|
instances=bound_value(compute.max_total_instances),
|
||||||
|
cores=bound_value(compute.max_total_cores),
|
||||||
|
ram=bound_value(compute.max_total_ram_size))
|
||||||
|
if volume is not None:
|
||||||
|
args['volumes'] = bound_value(volume['absolute']['maxTotalVolumes'])
|
||||||
|
args['volume-gb'] = bound_value(
|
||||||
|
volume['absolute']['maxTotalVolumeGigabytes'])
|
||||||
|
return QuotaInformation(**args)
|
||||||
|
|
||||||
|
|
||||||
class OpenStackInstance(statemachine.Instance):
|
class OpenStackInstance(statemachine.Instance):
|
||||||
def __init__(self, provider, server, quota):
|
def __init__(self, provider, server, quota):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -192,7 +224,7 @@ class OpenStackCreateStateMachine(statemachine.StateMachine):
|
|||||||
self.flavor = self.adapter._findFlavor(
|
self.flavor = self.adapter._findFlavor(
|
||||||
flavor_name=self.label.flavor_name,
|
flavor_name=self.label.flavor_name,
|
||||||
min_ram=self.label.min_ram)
|
min_ram=self.label.min_ram)
|
||||||
self.quota = QuotaInformation.construct_from_flavor(self.flavor)
|
self.quota = quota_from_flavor(self.flavor, label=self.label)
|
||||||
self.external_id = None
|
self.external_id = None
|
||||||
|
|
||||||
def _handleServerFault(self):
|
def _handleServerFault(self):
|
||||||
@ -406,20 +438,32 @@ class OpenStackAdapter(statemachine.Adapter):
|
|||||||
self._deleteServer(resource.id)
|
self._deleteServer(resource.id)
|
||||||
|
|
||||||
def listInstances(self):
|
def listInstances(self):
|
||||||
|
volumes = {}
|
||||||
|
for volume in self._listVolumes():
|
||||||
|
volumes[volume.id] = volume
|
||||||
for server in self._listServers():
|
for server in self._listServers():
|
||||||
if server.status.lower() == 'deleted':
|
if server.status.lower() == 'deleted':
|
||||||
continue
|
continue
|
||||||
flavor = self._getFlavorFromServer(server)
|
flavor = self._getFlavorFromServer(server)
|
||||||
quota = QuotaInformation.construct_from_flavor(flavor)
|
server_volumes = []
|
||||||
|
for vattach in server.volumes:
|
||||||
|
volume = volumes.get(vattach.id)
|
||||||
|
if volume:
|
||||||
|
server_volumes.append(volume)
|
||||||
|
quota = quota_from_flavor(flavor, volumes=server_volumes)
|
||||||
yield OpenStackInstance(self.provider, server, quota)
|
yield OpenStackInstance(self.provider, server, quota)
|
||||||
|
|
||||||
def getQuotaLimits(self):
|
def getQuotaLimits(self):
|
||||||
limits = self._client.get_compute_limits()
|
compute = self._client.get_compute_limits()
|
||||||
return QuotaInformation.construct_from_limits(limits)
|
try:
|
||||||
|
volume = self._client.get_volume_limits()
|
||||||
|
except EndpointNotFound:
|
||||||
|
volume = None
|
||||||
|
return quota_from_limits(compute, volume)
|
||||||
|
|
||||||
def getQuotaForLabel(self, label):
|
def getQuotaForLabel(self, label):
|
||||||
flavor = self._findFlavor(label.flavor_name, label.min_ram)
|
flavor = self._findFlavor(label.flavor_name, label.min_ram)
|
||||||
return QuotaInformation.construct_from_flavor(flavor)
|
return quota_from_flavor(flavor, label=label)
|
||||||
|
|
||||||
def getAZs(self):
|
def getAZs(self):
|
||||||
azs = self._listAZs()
|
azs = self._listAZs()
|
||||||
@ -629,6 +673,13 @@ class OpenStackAdapter(statemachine.Adapter):
|
|||||||
def _listServers(self):
|
def _listServers(self):
|
||||||
return self._client.list_servers(bare=True)
|
return self._client.list_servers(bare=True)
|
||||||
|
|
||||||
|
@cachetools.func.ttl_cache(maxsize=1, ttl=CACHE_TTL)
|
||||||
|
def _listVolumes(self):
|
||||||
|
try:
|
||||||
|
return self._client.list_volumes()
|
||||||
|
except EndpointNotFound:
|
||||||
|
return []
|
||||||
|
|
||||||
@cachetools.func.ttl_cache(maxsize=1, ttl=CACHE_TTL)
|
@cachetools.func.ttl_cache(maxsize=1, ttl=CACHE_TTL)
|
||||||
def _listFloatingIps(self):
|
def _listFloatingIps(self):
|
||||||
return self._client.list_floating_ips()
|
return self._client.list_floating_ips()
|
||||||
|
@ -110,6 +110,8 @@ class ProviderPool(ConfigPool):
|
|||||||
self.name = pool_config['name']
|
self.name = pool_config['name']
|
||||||
self.max_cores = pool_config.get('max-cores', math.inf)
|
self.max_cores = pool_config.get('max-cores', math.inf)
|
||||||
self.max_ram = pool_config.get('max-ram', math.inf)
|
self.max_ram = pool_config.get('max-ram', math.inf)
|
||||||
|
self.max_volumes = pool_config.get('max-volumes', math.inf)
|
||||||
|
self.max_volume_gb = pool_config.get('max-volume-gb', math.inf)
|
||||||
self.ignore_provider_quota = pool_config.get('ignore-provider-quota',
|
self.ignore_provider_quota = pool_config.get('ignore-provider-quota',
|
||||||
False)
|
False)
|
||||||
self.azs = pool_config.get('availability-zones')
|
self.azs = pool_config.get('availability-zones')
|
||||||
@ -326,6 +328,8 @@ class OpenStackProviderConfig(ProviderConfig):
|
|||||||
'ignore-provider-quota': bool,
|
'ignore-provider-quota': bool,
|
||||||
'max-cores': int,
|
'max-cores': int,
|
||||||
'max-ram': int,
|
'max-ram': int,
|
||||||
|
'max-volumes': int,
|
||||||
|
'max-volume-gb': int,
|
||||||
'labels': [pool_label],
|
'labels': [pool_label],
|
||||||
'availability-zones': [str],
|
'availability-zones': [str],
|
||||||
'security-groups': [str]
|
'security-groups': [str]
|
||||||
|
@ -408,6 +408,10 @@ class StateMachineHandler(NodeRequestHandler):
|
|||||||
ram=getattr(self.pool, 'max_ram', None),
|
ram=getattr(self.pool, 'max_ram', None),
|
||||||
default=math.inf,
|
default=math.inf,
|
||||||
)
|
)
|
||||||
|
if getattr(self.pool, 'max_volumes', None):
|
||||||
|
args['volumes'] = self.pool.max_volumes
|
||||||
|
if getattr(self.pool, 'max_volume_gb', None):
|
||||||
|
args['volume_gb'] = self.pool.max_volume_gb
|
||||||
args.update(getattr(self.pool, 'max_resources', {}))
|
args.update(getattr(self.pool, 'max_resources', {}))
|
||||||
pool_quota = QuotaInformation(**args)
|
pool_quota = QuotaInformation(**args)
|
||||||
pool_quota.subtract(needed_quota)
|
pool_quota.subtract(needed_quota)
|
||||||
@ -445,6 +449,10 @@ class StateMachineHandler(NodeRequestHandler):
|
|||||||
ram=getattr(self.pool, 'max_ram', None),
|
ram=getattr(self.pool, 'max_ram', None),
|
||||||
default=math.inf,
|
default=math.inf,
|
||||||
)
|
)
|
||||||
|
if getattr(self.pool, 'max_volumes', None):
|
||||||
|
args['volumes'] = self.pool.max_volumes
|
||||||
|
if getattr(self.pool, 'max_volume_gb', None):
|
||||||
|
args['volume-gb'] = self.pool.max_volume_gb
|
||||||
args.update(getattr(self.pool, 'max_resources', {}))
|
args.update(getattr(self.pool, 'max_resources', {}))
|
||||||
pool_quota = QuotaInformation(**args)
|
pool_quota = QuotaInformation(**args)
|
||||||
pool_quota.subtract(
|
pool_quota.subtract(
|
||||||
|
@ -207,24 +207,6 @@ class QuotaInformation:
|
|||||||
self.quota['compute'][k] = v
|
self.quota['compute'][k] = v
|
||||||
self.default = default
|
self.default = default
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def construct_from_flavor(flavor):
|
|
||||||
return QuotaInformation(instances=1,
|
|
||||||
cores=flavor.vcpus,
|
|
||||||
ram=flavor.ram)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def construct_from_limits(limits):
|
|
||||||
def bound_value(value):
|
|
||||||
if value == -1:
|
|
||||||
return math.inf
|
|
||||||
return value
|
|
||||||
|
|
||||||
return QuotaInformation(
|
|
||||||
instances=bound_value(limits.max_total_instances),
|
|
||||||
cores=bound_value(limits.max_total_cores),
|
|
||||||
ram=bound_value(limits.max_total_ram_size))
|
|
||||||
|
|
||||||
def _get_default(self, value, default):
|
def _get_default(self, value, default):
|
||||||
return value if value is not None else default
|
return value if value is not None else default
|
||||||
|
|
||||||
|
48
nodepool/tests/fixtures/node_quota_cloud_volumes.yaml
vendored
Normal file
48
nodepool/tests/fixtures/node_quota_cloud_volumes.yaml
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
elements-dir: .
|
||||||
|
images-dir: '{images_dir}'
|
||||||
|
build-log-dir: '{build_log_dir}'
|
||||||
|
|
||||||
|
zookeeper-servers:
|
||||||
|
- host: {zookeeper_host}
|
||||||
|
port: {zookeeper_port}
|
||||||
|
chroot: {zookeeper_chroot}
|
||||||
|
|
||||||
|
zookeeper-tls:
|
||||||
|
ca: {zookeeper_ca}
|
||||||
|
cert: {zookeeper_cert}
|
||||||
|
key: {zookeeper_key}
|
||||||
|
|
||||||
|
labels:
|
||||||
|
- name: fake-label
|
||||||
|
min-ready: 0
|
||||||
|
|
||||||
|
providers:
|
||||||
|
- name: fake-provider
|
||||||
|
cloud: fake
|
||||||
|
driver: fake
|
||||||
|
region-name: fake-region
|
||||||
|
rate: 0.0001
|
||||||
|
diskimages:
|
||||||
|
- name: fake-image
|
||||||
|
pools:
|
||||||
|
- name: main
|
||||||
|
max-servers: 20
|
||||||
|
labels:
|
||||||
|
- name: fake-label
|
||||||
|
diskimage: fake-image
|
||||||
|
min-ram: 8192
|
||||||
|
boot-from-volume: true
|
||||||
|
volume-size: 10
|
||||||
|
|
||||||
|
diskimages:
|
||||||
|
- name: fake-image
|
||||||
|
elements:
|
||||||
|
- fedora
|
||||||
|
- vm
|
||||||
|
release: 21
|
||||||
|
dib-cmd: nodepool/tests/fake-image-create
|
||||||
|
env-vars:
|
||||||
|
TMPDIR: /opt/dib_tmp
|
||||||
|
DIB_IMAGE_CACHE: /opt/dib_cache
|
||||||
|
DIB_CLOUD_IMAGES: http://download.fedoraproject.org/pub/fedora/linux/releases/test/21-Beta/Cloud/Images/x86_64/
|
||||||
|
BASE_IMAGE_FILE: Fedora-Cloud-Base-20141029-21_Beta.x86_64.qcow2
|
48
nodepool/tests/fixtures/node_quota_pool_volume_gb.yaml
vendored
Normal file
48
nodepool/tests/fixtures/node_quota_pool_volume_gb.yaml
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
elements-dir: .
|
||||||
|
images-dir: '{images_dir}'
|
||||||
|
build-log-dir: '{build_log_dir}'
|
||||||
|
|
||||||
|
zookeeper-servers:
|
||||||
|
- host: {zookeeper_host}
|
||||||
|
port: {zookeeper_port}
|
||||||
|
chroot: {zookeeper_chroot}
|
||||||
|
|
||||||
|
zookeeper-tls:
|
||||||
|
ca: {zookeeper_ca}
|
||||||
|
cert: {zookeeper_cert}
|
||||||
|
key: {zookeeper_key}
|
||||||
|
|
||||||
|
labels:
|
||||||
|
- name: fake-label
|
||||||
|
min-ready: 0
|
||||||
|
|
||||||
|
providers:
|
||||||
|
- name: fake-provider
|
||||||
|
cloud: fake
|
||||||
|
driver: fake
|
||||||
|
region-name: fake-region
|
||||||
|
rate: 0.0001
|
||||||
|
diskimages:
|
||||||
|
- name: fake-image
|
||||||
|
pools:
|
||||||
|
- name: main
|
||||||
|
max-volume-gb: 20
|
||||||
|
labels:
|
||||||
|
- name: fake-label
|
||||||
|
diskimage: fake-image
|
||||||
|
min-ram: 8192
|
||||||
|
boot-from-volume: true
|
||||||
|
volume-size: 10
|
||||||
|
|
||||||
|
diskimages:
|
||||||
|
- name: fake-image
|
||||||
|
elements:
|
||||||
|
- fedora
|
||||||
|
- vm
|
||||||
|
release: 21
|
||||||
|
dib-cmd: nodepool/tests/fake-image-create
|
||||||
|
env-vars:
|
||||||
|
TMPDIR: /opt/dib_tmp
|
||||||
|
DIB_IMAGE_CACHE: /opt/dib_cache
|
||||||
|
DIB_CLOUD_IMAGES: http://download.fedoraproject.org/pub/fedora/linux/releases/test/21-Beta/Cloud/Images/x86_64/
|
||||||
|
BASE_IMAGE_FILE: Fedora-Cloud-Base-20141029-21_Beta.x86_64.qcow2
|
48
nodepool/tests/fixtures/node_quota_pool_volumes.yaml
vendored
Normal file
48
nodepool/tests/fixtures/node_quota_pool_volumes.yaml
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
elements-dir: .
|
||||||
|
images-dir: '{images_dir}'
|
||||||
|
build-log-dir: '{build_log_dir}'
|
||||||
|
|
||||||
|
zookeeper-servers:
|
||||||
|
- host: {zookeeper_host}
|
||||||
|
port: {zookeeper_port}
|
||||||
|
chroot: {zookeeper_chroot}
|
||||||
|
|
||||||
|
zookeeper-tls:
|
||||||
|
ca: {zookeeper_ca}
|
||||||
|
cert: {zookeeper_cert}
|
||||||
|
key: {zookeeper_key}
|
||||||
|
|
||||||
|
labels:
|
||||||
|
- name: fake-label
|
||||||
|
min-ready: 0
|
||||||
|
|
||||||
|
providers:
|
||||||
|
- name: fake-provider
|
||||||
|
cloud: fake
|
||||||
|
driver: fake
|
||||||
|
region-name: fake-region
|
||||||
|
rate: 0.0001
|
||||||
|
diskimages:
|
||||||
|
- name: fake-image
|
||||||
|
pools:
|
||||||
|
- name: main
|
||||||
|
max-volumes: 2
|
||||||
|
labels:
|
||||||
|
- name: fake-label
|
||||||
|
diskimage: fake-image
|
||||||
|
min-ram: 8192
|
||||||
|
boot-from-volume: true
|
||||||
|
volume-size: 10
|
||||||
|
|
||||||
|
diskimages:
|
||||||
|
- name: fake-image
|
||||||
|
elements:
|
||||||
|
- fedora
|
||||||
|
- vm
|
||||||
|
release: 21
|
||||||
|
dib-cmd: nodepool/tests/fake-image-create
|
||||||
|
env-vars:
|
||||||
|
TMPDIR: /opt/dib_tmp
|
||||||
|
DIB_IMAGE_CACHE: /opt/dib_cache
|
||||||
|
DIB_CLOUD_IMAGES: http://download.fedoraproject.org/pub/fedora/linux/releases/test/21-Beta/Cloud/Images/x86_64/
|
||||||
|
BASE_IMAGE_FILE: Fedora-Cloud-Base-20141029-21_Beta.x86_64.qcow2
|
52
nodepool/tests/fixtures/node_quota_tenant_volume_gb.yaml
vendored
Normal file
52
nodepool/tests/fixtures/node_quota_tenant_volume_gb.yaml
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
elements-dir: .
|
||||||
|
images-dir: '{images_dir}'
|
||||||
|
build-log-dir: '{build_log_dir}'
|
||||||
|
|
||||||
|
zookeeper-servers:
|
||||||
|
- host: {zookeeper_host}
|
||||||
|
port: {zookeeper_port}
|
||||||
|
chroot: {zookeeper_chroot}
|
||||||
|
|
||||||
|
zookeeper-tls:
|
||||||
|
ca: {zookeeper_ca}
|
||||||
|
cert: {zookeeper_cert}
|
||||||
|
key: {zookeeper_key}
|
||||||
|
|
||||||
|
|
||||||
|
tenant-resource-limits:
|
||||||
|
- tenant-name: tenant-1
|
||||||
|
max-volume-gb: 20
|
||||||
|
|
||||||
|
labels:
|
||||||
|
- name: fake-label
|
||||||
|
min-ready: 0
|
||||||
|
|
||||||
|
providers:
|
||||||
|
- name: fake-provider
|
||||||
|
cloud: fake
|
||||||
|
driver: fake
|
||||||
|
region-name: fake-region
|
||||||
|
rate: 0.0001
|
||||||
|
diskimages:
|
||||||
|
- name: fake-image
|
||||||
|
pools:
|
||||||
|
- name: main
|
||||||
|
labels:
|
||||||
|
- name: fake-label
|
||||||
|
diskimage: fake-image
|
||||||
|
min-ram: 8192
|
||||||
|
boot-from-volume: true
|
||||||
|
volume-size: 10
|
||||||
|
|
||||||
|
diskimages:
|
||||||
|
- name: fake-image
|
||||||
|
elements:
|
||||||
|
- fedora
|
||||||
|
- vm
|
||||||
|
release: 21
|
||||||
|
dib-cmd: nodepool/tests/fake-image-create
|
||||||
|
env-vars:
|
||||||
|
TMPDIR: /opt/dib_tmp
|
||||||
|
DIB_IMAGE_CACHE: /opt/dib_cache
|
||||||
|
DIB_CLOUD_IMAGES: http://download.fedoraproject.org/pub/fedora/linux/releases/test/21-Beta/Cloud/Images/x86_64/
|
||||||
|
BASE_IMAGE_FILE: Fedora-Cloud-Base-20141029-21_Beta.x86_64.qcow2
|
52
nodepool/tests/fixtures/node_quota_tenant_volumes.yaml
vendored
Normal file
52
nodepool/tests/fixtures/node_quota_tenant_volumes.yaml
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
elements-dir: .
|
||||||
|
images-dir: '{images_dir}'
|
||||||
|
build-log-dir: '{build_log_dir}'
|
||||||
|
|
||||||
|
zookeeper-servers:
|
||||||
|
- host: {zookeeper_host}
|
||||||
|
port: {zookeeper_port}
|
||||||
|
chroot: {zookeeper_chroot}
|
||||||
|
|
||||||
|
zookeeper-tls:
|
||||||
|
ca: {zookeeper_ca}
|
||||||
|
cert: {zookeeper_cert}
|
||||||
|
key: {zookeeper_key}
|
||||||
|
|
||||||
|
|
||||||
|
tenant-resource-limits:
|
||||||
|
- tenant-name: tenant-1
|
||||||
|
max-volumes: 2
|
||||||
|
|
||||||
|
labels:
|
||||||
|
- name: fake-label
|
||||||
|
min-ready: 0
|
||||||
|
|
||||||
|
providers:
|
||||||
|
- name: fake-provider
|
||||||
|
cloud: fake
|
||||||
|
driver: fake
|
||||||
|
region-name: fake-region
|
||||||
|
rate: 0.0001
|
||||||
|
diskimages:
|
||||||
|
- name: fake-image
|
||||||
|
pools:
|
||||||
|
- name: main
|
||||||
|
labels:
|
||||||
|
- name: fake-label
|
||||||
|
diskimage: fake-image
|
||||||
|
min-ram: 8192
|
||||||
|
boot-from-volume: true
|
||||||
|
volume-size: 10
|
||||||
|
|
||||||
|
diskimages:
|
||||||
|
- name: fake-image
|
||||||
|
elements:
|
||||||
|
- fedora
|
||||||
|
- vm
|
||||||
|
release: 21
|
||||||
|
dib-cmd: nodepool/tests/fake-image-create
|
||||||
|
env-vars:
|
||||||
|
TMPDIR: /opt/dib_tmp
|
||||||
|
DIB_IMAGE_CACHE: /opt/dib_cache
|
||||||
|
DIB_CLOUD_IMAGES: http://download.fedoraproject.org/pub/fedora/linux/releases/test/21-Beta/Cloud/Images/x86_64/
|
||||||
|
BASE_IMAGE_FILE: Fedora-Cloud-Base-20141029-21_Beta.x86_64.qcow2
|
@ -179,7 +179,9 @@ class TestLauncher(tests.DBTestCase):
|
|||||||
config,
|
config,
|
||||||
max_cores=100,
|
max_cores=100,
|
||||||
max_instances=20,
|
max_instances=20,
|
||||||
max_ram=1000000):
|
max_ram=1000000,
|
||||||
|
max_volumes=100,
|
||||||
|
max_volume_gb=1000000):
|
||||||
'''
|
'''
|
||||||
Successful node launch should have unlocked nodes in READY state
|
Successful node launch should have unlocked nodes in READY state
|
||||||
and assigned to the request. This should be run with a quota that
|
and assigned to the request. This should be run with a quota that
|
||||||
@ -189,10 +191,18 @@ class TestLauncher(tests.DBTestCase):
|
|||||||
# patch the cloud with requested quota
|
# patch the cloud with requested quota
|
||||||
def fake_get_quota():
|
def fake_get_quota():
|
||||||
return (max_cores, max_instances, max_ram)
|
return (max_cores, max_instances, max_ram)
|
||||||
|
|
||||||
|
def fake_get_volume_quota():
|
||||||
|
return (max_volumes, max_volume_gb)
|
||||||
|
|
||||||
self.useFixture(fixtures.MockPatchObject(
|
self.useFixture(fixtures.MockPatchObject(
|
||||||
fakeadapter.FakeAdapter.fake_cloud, '_get_quota',
|
fakeadapter.FakeAdapter.fake_cloud, '_get_quota',
|
||||||
fake_get_quota
|
fake_get_quota
|
||||||
))
|
))
|
||||||
|
self.useFixture(fixtures.MockPatchObject(
|
||||||
|
fakeadapter.FakeAdapter.fake_cloud, '_get_volume_quota',
|
||||||
|
fake_get_volume_quota
|
||||||
|
))
|
||||||
|
|
||||||
configfile = self.setup_config(config)
|
configfile = self.setup_config(config)
|
||||||
self.useBuilder(configfile)
|
self.useBuilder(configfile)
|
||||||
@ -290,6 +300,14 @@ class TestLauncher(tests.DBTestCase):
|
|||||||
self._test_node_assignment_at_quota(
|
self._test_node_assignment_at_quota(
|
||||||
config='node_quota_pool_ram.yaml')
|
config='node_quota_pool_ram.yaml')
|
||||||
|
|
||||||
|
def test_node_assignment_at_pool_quota_volumes(self):
|
||||||
|
self._test_node_assignment_at_quota(
|
||||||
|
config='node_quota_pool_volumes.yaml')
|
||||||
|
|
||||||
|
def test_node_assignment_at_pool_quota_volume_gb(self):
|
||||||
|
self._test_node_assignment_at_quota(
|
||||||
|
config='node_quota_pool_volume_gb.yaml')
|
||||||
|
|
||||||
def _test_node_assignment_at_tenant_quota(self, config):
|
def _test_node_assignment_at_tenant_quota(self, config):
|
||||||
configfile = self.setup_config(config)
|
configfile = self.setup_config(config)
|
||||||
self.useBuilder(configfile)
|
self.useBuilder(configfile)
|
||||||
@ -386,6 +404,18 @@ class TestLauncher(tests.DBTestCase):
|
|||||||
self.assertReportedStat('nodepool.tenant_limits.tenant-1.ram',
|
self.assertReportedStat('nodepool.tenant_limits.tenant-1.ram',
|
||||||
value='16384', kind='g')
|
value='16384', kind='g')
|
||||||
|
|
||||||
|
def test_node_assignment_at_tenant_quota_volumes(self):
|
||||||
|
self._test_node_assignment_at_tenant_quota(
|
||||||
|
'node_quota_tenant_volumes.yaml')
|
||||||
|
self.assertReportedStat('nodepool.tenant_limits.tenant-1.volumes',
|
||||||
|
value='2', kind='g')
|
||||||
|
|
||||||
|
def test_node_assignment_at_tenant_quota_volume_gb(self):
|
||||||
|
self._test_node_assignment_at_tenant_quota(
|
||||||
|
'node_quota_tenant_volume_gb.yaml')
|
||||||
|
self.assertReportedStat('nodepool.tenant_limits.tenant-1.volume-gb',
|
||||||
|
value='20', kind='g')
|
||||||
|
|
||||||
def test_node_assignment_at_tenant_quota_min_ready(self):
|
def test_node_assignment_at_tenant_quota_min_ready(self):
|
||||||
self._test_node_assignment_at_tenant_quota(
|
self._test_node_assignment_at_tenant_quota(
|
||||||
'node_quota_tenant_min_ready.yaml')
|
'node_quota_tenant_min_ready.yaml')
|
||||||
@ -412,6 +442,16 @@ class TestLauncher(tests.DBTestCase):
|
|||||||
max_instances=math.inf,
|
max_instances=math.inf,
|
||||||
max_ram=2 * 8192)
|
max_ram=2 * 8192)
|
||||||
|
|
||||||
|
def test_node_assignment_at_cloud_volumes_quota(self):
|
||||||
|
self._test_node_assignment_at_quota(
|
||||||
|
config='node_quota_cloud_volumes.yaml',
|
||||||
|
max_volumes=2)
|
||||||
|
|
||||||
|
def test_node_assignment_at_cloud_volume_gb_quota(self):
|
||||||
|
self._test_node_assignment_at_quota(
|
||||||
|
config='node_quota_cloud_volumes.yaml',
|
||||||
|
max_volume_gb=20)
|
||||||
|
|
||||||
def test_decline_at_quota(self):
|
def test_decline_at_quota(self):
|
||||||
'''test that a provider at quota continues to decline requests'''
|
'''test that a provider at quota continues to decline requests'''
|
||||||
|
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
The OpenStack driver now supports volume quota. It will
|
||||||
|
automatically register the limits from the cloud and ensure that
|
||||||
|
labels that utilize boot-from-volume stay under the limit. Limits
|
||||||
|
can also be specified at the pool and tenant level in Nodepool's
|
||||||
|
configuration.
|
Loading…
x
Reference in New Issue
Block a user