Kaydet (Commit) 81d8caaf authored tarafından Joffrey F's avatar Joffrey F

Merge pull request #916 from docker/container_update_feature

Support for container limits update
......@@ -398,6 +398,39 @@ class ContainerApiMixin(object):
res = self._post(url)
self._raise_for_status(res)
@utils.minimum_version('1.22')
@utils.check_resource
def update_container(
self, container, blkio_weight=None, cpu_period=None, cpu_quota=None,
cpu_shares=None, cpuset_cpus=None, cpuset_mems=None, mem_limit=None,
mem_reservation=None, memswap_limit=None, kernel_memory=None
):
url = self._url('/containers/{0}/update', container)
data = {}
if blkio_weight:
data['BlkioWeight'] = blkio_weight
if cpu_period:
data['CpuPeriod'] = cpu_period
if cpu_shares:
data['CpuShares'] = cpu_shares
if cpu_quota:
data['CpuQuota'] = cpu_quota
if cpuset_cpus:
data['CpusetCpus'] = cpuset_cpus
if cpuset_mems:
data['CpusetMems'] = cpuset_mems
if mem_limit:
data['Memory'] = utils.parse_bytes(mem_limit)
if mem_reservation:
data['MemoryReservation'] = utils.parse_bytes(mem_reservation)
if memswap_limit:
data['MemorySwap'] = utils.parse_bytes(memswap_limit)
if kernel_memory:
data['KernelMemory'] = utils.parse_bytes(kernel_memory)
res = self._post_json(url, data=data)
return self._result(res, True)
@utils.check_resource
def wait(self, container, timeout=None):
url = self._url("/containers/{0}/wait", container)
......
......@@ -518,41 +518,43 @@ def longint(n):
def parse_bytes(s):
if isinstance(s, six.integer_types + (float,)):
return s
if len(s) == 0:
s = 0
else:
if s[-2:-1].isalpha() and s[-1].isalpha():
if s[-1] == "b" or s[-1] == "B":
s = s[:-1]
units = BYTE_UNITS
suffix = s[-1].lower()
# Check if the variable is a string representation of an int
# without a units part. Assuming that the units are bytes.
if suffix.isdigit():
digits_part = s
suffix = 'b'
else:
digits_part = s[:-1]
return 0
if suffix in units.keys() or suffix.isdigit():
try:
digits = longint(digits_part)
except ValueError:
raise errors.DockerException(
'Failed converting the string value for memory ({0}) to'
' an integer.'.format(digits_part)
)
if s[-2:-1].isalpha() and s[-1].isalpha():
if s[-1] == "b" or s[-1] == "B":
s = s[:-1]
units = BYTE_UNITS
suffix = s[-1].lower()
# Check if the variable is a string representation of an int
# without a units part. Assuming that the units are bytes.
if suffix.isdigit():
digits_part = s
suffix = 'b'
else:
digits_part = s[:-1]
# Reconvert to long for the final result
s = longint(digits * units[suffix])
else:
if suffix in units.keys() or suffix.isdigit():
try:
digits = longint(digits_part)
except ValueError:
raise errors.DockerException(
'The specified value for memory ({0}) should specify the'
' units. The postfix should be one of the `b` `k` `m` `g`'
' characters'.format(s)
'Failed converting the string value for memory ({0}) to'
' an integer.'.format(digits_part)
)
# Reconvert to long for the final result
s = longint(digits * units[suffix])
else:
raise errors.DockerException(
'The specified value for memory ({0}) should specify the'
' units. The postfix should be one of the `b` `k` `m` `g`'
' characters'.format(s)
)
return s
......@@ -594,16 +596,10 @@ def create_host_config(binds=None, port_bindings=None, lxc_conf=None,
version = constants.DEFAULT_DOCKER_API_VERSION
if mem_limit is not None:
if isinstance(mem_limit, six.string_types):
mem_limit = parse_bytes(mem_limit)
host_config['Memory'] = mem_limit
host_config['Memory'] = parse_bytes(mem_limit)
if memswap_limit is not None:
if isinstance(memswap_limit, six.string_types):
memswap_limit = parse_bytes(memswap_limit)
host_config['MemorySwap'] = memswap_limit
host_config['MemorySwap'] = parse_bytes(memswap_limit)
if mem_swappiness is not None:
if version_lt(version, '1.20'):
......@@ -878,9 +874,9 @@ def create_container_config(
if isinstance(labels, list):
labels = dict((lbl, six.text_type('')) for lbl in labels)
if isinstance(mem_limit, six.string_types):
if mem_limit is not None:
mem_limit = parse_bytes(mem_limit)
if isinstance(memswap_limit, six.string_types):
if memswap_limit is not None:
memswap_limit = parse_bytes(memswap_limit)
if isinstance(ports, list):
......
......@@ -998,12 +998,32 @@ Display the running processes of a container.
## unpause
Unpauses all processes within a container.
Unpause all processes within a container.
**Params**:
* container (str): The container to unpause
## update_container
Update resource configs of one or more containers.
**Params**:
* container (str): The container to inspect
* blkio_weight (int): Block IO (relative weight), between 10 and 1000
* cpu_period (int): Limit CPU CFS (Completely Fair Scheduler) period
* cpu_quota (int): Limit CPU CFS (Completely Fair Scheduler) quota
* cpu_shares (int): CPU shares (relative weight)
* cpuset_cpus (str): CPUs in which to allow execution
* cpuset_mems (str): MEMs in which to allow execution
* mem_limit (int or str): Memory limit
* mem_reservation (int or str): Memory soft limit
* memswap_limit (int or str): Total memory (memory + swap), -1 to disable swap
* kernel_memory (int or str): Kernel memory limit
**Returns** (dict): Dictionary containing a `Warnings` key.
## version
Nearly identical to the `docker version` command.
......
......@@ -1044,3 +1044,21 @@ class GetContainerStatsTest(helpers.BaseTestCase):
for key in ['read', 'network', 'precpu_stats', 'cpu_stats',
'memory_stats', 'blkio_stats']:
self.assertIn(key, chunk)
class ContainerUpdateTest(helpers.BaseTestCase):
@requires_api_version('1.22')
def test_update_container(self):
old_mem_limit = 400 * 1024 * 1024
new_mem_limit = 300 * 1024 * 1024
container = self.client.create_container(
BUSYBOX, 'top', host_config=self.client.create_host_config(
mem_limit=old_mem_limit
), cpu_shares=102
)
self.tmp_containers.append(container)
self.client.start(container)
self.client.update_container(container, mem_limit=new_mem_limit)
inspect_data = self.client.inspect_container(container)
self.assertEqual(inspect_data['HostConfig']['Memory'], new_mem_limit)
self.assertEqual(inspect_data['HostConfig']['CpuShares'], 102)
......@@ -1452,3 +1452,21 @@ class ContainerTest(DockerClientTest):
params={'ps_args': 'waux'},
timeout=DEFAULT_TIMEOUT_SECONDS
)
@requires_api_version('1.22')
def test_container_update(self):
self.client.update_container(
fake_api.FAKE_CONTAINER_ID, mem_limit='2k', cpu_shares=124,
blkio_weight=345
)
args = fake_request.call_args
self.assertEqual(
args[0][1], url_prefix + 'containers/3cc2351ab11b/update'
)
self.assertEqual(
json.loads(args[1]['data']),
{'Memory': 2 * 1024, 'CpuShares': 124, 'BlkioWeight': 345}
)
self.assertEqual(
args[1]['headers']['Content-Type'], 'application/json'
)
......@@ -441,6 +441,11 @@ def get_fake_volume():
def fake_remove_volume():
return 204, None
def post_fake_update_container():
return 200, {'Warnings': []}
# Maps real api url to fake response callback
prefix = 'http+docker://localunixsocket'
fake_responses = {
......@@ -478,6 +483,8 @@ fake_responses = {
get_fake_diff,
'{1}/{0}/containers/3cc2351ab11b/export'.format(CURRENT_VERSION, prefix):
get_fake_export,
'{1}/{0}/containers/3cc2351ab11b/update'.format(CURRENT_VERSION, prefix):
post_fake_update_container,
'{1}/{0}/containers/3cc2351ab11b/exec'.format(CURRENT_VERSION, prefix):
post_fake_exec_create,
'{1}/{0}/exec/d5d177f121dc/start'.format(CURRENT_VERSION, prefix):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment