from flask_restful import Resource
from flask import request
import json
+from copy import deepcopy
logging.basicConfig(level=logging.INFO)
global dcs
def put(self, dc_label, compute_name, resource=None, value=None):
- # check if resource update
- if resource and value:
- c = self._update_resource(dc_label, compute_name, resource, value)
- return c.getStatus(), 200
# deploy new container
# check if json data is a dict
logging.exception("API error.")
return ex.message, 500, CORS_HEADER
- def _update_resource(self, dc_label, compute_name, resource, value):
- #check if container exists
- d = dcs.get(dc_label).net.getNodeByName(compute_name)
- if resource == 'cpu':
- cpu_period = int(dcs.get(dc_label).net.cpu_period)
- cpu_quota = int(cpu_period * float(value))
- #put default values back
- if float(value) <= 0:
- cpu_period = 100000
- cpu_quota = -1
- d.updateCpuLimit(cpu_period=cpu_period, cpu_quota=cpu_quota)
- return d
-
-
def get(self, dc_label, compute_name):
logging.debug("API CALL: compute status")
logging.exception("API error.")
return ex.message, 500, CORS_HEADER
+class ComputeResources(Resource):
+ """
+ Update the container's resources using the docker.update function
+ re-using the same parameters:
+ url params:
+ blkio_weight
+ cpu_period, cpu_quota, cpu_shares
+ cpuset_cpus
+ cpuset_mems
+ mem_limit
+ mem_reservation
+ memswap_limit
+ kernel_memory
+ restart_policy
+ see https://docs.docker.com/engine/reference/commandline/update/
+ or API docs: https://docker-py.readthedocs.io/en/stable/api.html#module-docker.api.container
+ :param dc_label: name of the DC
+ :param compute_name: compute container name
+
+ :return: docker inspect dict of deployed docker
+ """
+ global dcs
+
+ def put(self, dc_label, compute_name):
+ logging.debug("REST CALL: update container resources")
+
+ try:
+ c = self._update_resources(dc_label, compute_name)
+ return c.getStatus(), 200, CORS_HEADER
+ except Exception as ex:
+ logging.exception("API error.")
+ return ex.message, 500, CORS_HEADER
+
+ def _update_resources(self, dc_label, compute_name):
+
+ # get URL parameters
+ params = request.args
+ # then no data
+ if params is None:
+ params = {}
+ logging.debug("REST CALL: update container resources {0}".format(params))
+ #check if container exists
+ d = dcs.get(dc_label).net.getNodeByName(compute_name)
+
+ # general request of cpu percentage
+ # create a mutable copy
+ params = params.to_dict()
+ if 'cpu_bw' in params:
+ cpu_period = int(dcs.get(dc_label).net.cpu_period)
+ value = params.get('cpu_bw')
+ cpu_quota = int(cpu_period * float(value))
+ #put default values back
+ if float(value) <= 0:
+ cpu_period = 100000
+ cpu_quota = -1
+ params['cpu_period'] = cpu_period
+ params['cpu_quota'] = cpu_quota
+ #d.updateCpuLimit(cpu_period=cpu_period, cpu_quota=cpu_quota)
+
+ # only pass allowed keys to docker
+ allowed_keys = ['blkio_weight', 'cpu_period', 'cpu_quota', 'cpu_shares', 'cpuset_cpus',
+ 'cpuset_mems', 'mem_limit', 'mem_reservation', 'memswap_limit',
+ 'kernel_memory', 'restart_policy']
+ filtered_params = {key:params[key] for key in allowed_keys if key in params}
+
+ d.update_resources(**filtered_params)
+
+ return d
class DatacenterList(Resource):
global dcs
# need to import total module to set its global variable dcs
import compute
-from compute import dcs, ComputeList, Compute, DatacenterList, DatacenterStatus
+from compute import dcs, ComputeList, Compute, ComputeResources, DatacenterList, DatacenterStatus
# need to import total module to set its global variable net
import network
# setup endpoints
# compute related actions (start/stop VNFs, get info)
- self.api.add_resource(Compute,
- "/restapi/compute/<dc_label>/<compute_name>",
- "/restapi/compute/<dc_label>/<compute_name>/<resource>/<value>")
+ self.api.add_resource(Compute, "/restapi/compute/<dc_label>/<compute_name>")
self.api.add_resource(ComputeList,
"/restapi/compute",
"/restapi/compute/<dc_label>")
+ self.api.add_resource(ComputeResources, "/restapi/compute/resources/<dc_label>/<compute_name>")
self.api.add_resource(DatacenterStatus, "/restapi/datacenter/<dc_label>")
self.api.add_resource(DatacenterList, "/restapi/datacenter")
while not started:\r
list1 = self.dockercli.containers.list(filters={'status': 'running', 'name': 'prometheus'})\r
if len(list1) >= 1:\r
+ time.sleep(1)\r
started = True\r
if wait_time > 5:\r
return 'skewmon not started'\r
status["docker_network"] = self.dcinfo['NetworkSettings']['IPAddress']
status["image"] = self.dimage
status["flavor_name"] = self.flavor_name
- status["cpu_quota"] = self.cpu_quota
- status["cpu_period"] = self.cpu_period
- status["cpu_shares"] = self.cpu_shares
- status["cpuset"] = self.cpuset
- status["mem_limit"] = self.mem_limit
- status["memswap_limit"] = self.memswap_limit
+ status["cpu_quota"] = self.resources.get('cpu_quota')
+ status["cpu_period"] = self.resources.get('cpu_period')
+ status["cpu_shares"] = self.resources.get('cpu_shares')
+ status["cpuset"] = self.resources.get('cpuset_cpus')
+ status["mem_limit"] = self.resources.get('mem_limit')
+ status["memswap_limit"] = self.resources.get('memswap_limit')
status["state"] = self.dcli.inspect_container(self.dc)["State"]
status["id"] = self.dcli.inspect_container(self.dc)["Id"]
status["short_id"] = self.dcli.inspect_container(self.dc)["Id"][:12]
# calculate input values for CFS scheduler bandwidth limitation
cpu_period, cpu_quota = self._calculate_cpu_cfs_values(cpu_time_percentage)
# apply limits to container if changed
- if d.cpu_period != cpu_period or d.cpu_quota != cpu_quota:
+ if d.resources['cpu_period'] != cpu_period or d.resources['cpu_quota'] != cpu_quota:
LOG.debug("Setting CPU limit for %r: cpu_quota = cpu_period * limit = %f * %f = %f (op_factor=%f)" % (
d.name, cpu_period, cpu_time_percentage, cpu_quota, self.cpu_op_factor))
d.updateCpuLimit(cpu_period=int(cpu_period), cpu_quota=int(cpu_quota))
mem_limit = self.single_mu * number_mu
mem_limit = self._calculate_mem_limit_value(mem_limit)
# apply to container if changed
- if d.mem_limit != mem_limit:
+ if d.resources['mem_limit'] != mem_limit:
LOG.debug("Setting MEM limit for %r: mem_limit = %f MB (op_factor=%f)" %
(d.name, mem_limit/1024/1024, self.mem_op_factor))
d.updateMemoryLimit(mem_limit=mem_limit)
class DummyContainer(object):
def __init__(self):
- self.cpu_period = -1
- self.cpu_quota = -1
- self.mem_limit = -1
- self.memswap_limit = -1
+ # take defaukt values from son-emu
+ self.resources = dict(
+ cpu_period = -1,
+ cpu_quota = -1,
+ mem_limit = -1,
+ memswap_limit = -1
+ )
+ #self.cpu_period = self.resources['cpu_period']
+ #self.cpu_quota = self.resources['cpu_quota']
+ #self.mem_limit = self.resources['mem_limit']
+ #self.memswap_limit = self.resources['memswap_limit']
def updateCpuLimit(self, cpu_period, cpu_quota):
- self.cpu_period = cpu_period
- self.cpu_quota = cpu_quota
+ self.resources['cpu_period'] = cpu_period
+ self.resources['cpu_quota'] = cpu_quota
def updateMemoryLimit(self, mem_limit):
- self.mem_limit = mem_limit
+ self.resources['mem_limit'] = mem_limit
d = DummyContainer()
d.name = name
c1 = createDummyContainerObject("c1", flavor="tiny")
rm.allocate(c1) # calculate allocation
- self.assertEqual(float(c1.cpu_quota) / c1.cpu_period, E_CPU / MAX_CU * 0.5) # validate compute result
- self.assertEqual(float(c1.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 32) # validate memory result
+ self.assertEqual(float(c1.resources['cpu_quota']) / c1.resources['cpu_period'], E_CPU / MAX_CU * 0.5) # validate compute result
+ self.assertEqual(float(c1.resources['mem_limit']/1024/1024), float(E_MEM) / MAX_MU * 32) # validate memory result
c2 = createDummyContainerObject("c2", flavor="small")
rm.allocate(c2) # calculate allocation
- self.assertEqual(float(c2.cpu_quota) / c2.cpu_period, E_CPU / MAX_CU * 1) # validate compute result
- self.assertEqual(float(c2.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 128) # validate memory result
+ self.assertEqual(float(c2.resources['cpu_quota']) / c2.resources['cpu_period'], E_CPU / MAX_CU * 1) # validate compute result
+ self.assertEqual(float(c2.resources['mem_limit']/1024/1024), float(E_MEM) / MAX_MU * 128) # validate memory result
c3 = createDummyContainerObject("c3", flavor="medium")
rm.allocate(c3) # calculate allocation
- self.assertEqual(float(c3.cpu_quota) / c3.cpu_period, E_CPU / MAX_CU * 4) # validate compute result
- self.assertEqual(float(c3.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 256) # validate memory result
+ self.assertEqual(float(c3.resources['cpu_quota']) / c3.resources['cpu_period'], E_CPU / MAX_CU * 4) # validate compute result
+ self.assertEqual(float(c3.resources['mem_limit']/1024/1024), float(E_MEM) / MAX_MU * 256) # validate memory result
c4 = createDummyContainerObject("c4", flavor="large")
rm.allocate(c4) # calculate allocation
- self.assertEqual(float(c4.cpu_quota) / c4.cpu_period, E_CPU / MAX_CU * 8) # validate compute result
- self.assertEqual(float(c4.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 512) # validate memory result
+ self.assertEqual(float(c4.resources['cpu_quota']) / c4.resources['cpu_period'], E_CPU / MAX_CU * 8) # validate compute result
+ self.assertEqual(float(c4.resources['mem_limit']/1024/1024), float(E_MEM) / MAX_MU * 512) # validate memory result
c5 = createDummyContainerObject("c5", flavor="xlarge")
rm.allocate(c5) # calculate allocation
- self.assertEqual(float(c5.cpu_quota) / c5.cpu_period, E_CPU / MAX_CU * 16) # validate compute result
- self.assertEqual(float(c5.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 1024) # validate memory result
+ self.assertEqual(float(c5.resources['cpu_quota']) / c5.resources['cpu_period'], E_CPU / MAX_CU * 16) # validate compute result
+ self.assertEqual(float(c5.resources['mem_limit']/1024/1024), float(E_MEM) / MAX_MU * 1024) # validate memory result
def testAllocationCpuLimit(self):
# check if there is a real limitation set for containers cgroup
# deactivated for now, seems not to work in docker-in-docker setup used in CI
- self.assertEqual(float(tc1.cpu_quota)/tc1.cpu_period, 0.005)
+ self.assertEqual(float(tc1.resources['cpu_quota'])/tc1.resources['cpu_period'], 0.005)
# check if free was called during stopCompute
self.dc[0].stopCompute("tc1")
c1 = createDummyContainerObject("c1", flavor="small")
rm.allocate(c1) # calculate allocation
- self.assertAlmostEqual(float(c1.cpu_quota) / c1.cpu_period, E_CPU / MAX_CU * 1.0, places=5)
- self.assertAlmostEqual(float(c1.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 128)
+ self.assertAlmostEqual(float(c1.resources['cpu_quota']) / c1.resources['cpu_period'], E_CPU / MAX_CU * 1.0, places=5)
+ self.assertAlmostEqual(float(c1.resources['mem_limit']/1024/1024), float(E_MEM) / MAX_MU * 128)
self.assertAlmostEqual(rm.cpu_op_factor, 1.0)
c2 = createDummyContainerObject("c2", flavor="small")
rm.allocate(c2) # calculate allocation
- self.assertAlmostEqual(float(c2.cpu_quota) / c2.cpu_period, E_CPU / MAX_CU * 1.0, places=5)
- self.assertAlmostEqual(float(c2.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 128)
+ self.assertAlmostEqual(float(c2.resources['cpu_quota']) / c2.resources['cpu_period'], E_CPU / MAX_CU * 1.0, places=5)
+ self.assertAlmostEqual(float(c2.resources['mem_limit']/1024/1024), float(E_MEM) / MAX_MU * 128)
self.assertAlmostEqual(rm.cpu_op_factor, 1.0)
c3 = createDummyContainerObject("c3", flavor="small")
rm.allocate(c3) # calculate allocation
- self.assertAlmostEqual(float(c3.cpu_quota) / c3.cpu_period, E_CPU / MAX_CU * 1.0, places=5)
- self.assertAlmostEqual(float(c3.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 128)
+ self.assertAlmostEqual(float(c3.resources['cpu_quota']) / c3.resources['cpu_period'], E_CPU / MAX_CU * 1.0, places=5)
+ self.assertAlmostEqual(float(c3.resources['mem_limit']/1024/1024), float(E_MEM) / MAX_MU * 128)
self.assertAlmostEqual(rm.cpu_op_factor, 1.0)
# from this container onwards, we should go to over provisioning mode:
c4 = createDummyContainerObject("c4", flavor="small")
rm.allocate(c4) # calculate allocation
- self.assertAlmostEqual(float(c4.cpu_quota) / c4.cpu_period, E_CPU / MAX_CU * (float(3) / 4), places=5)
- self.assertAlmostEqual(float(c4.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 128, places=5)
+ self.assertAlmostEqual(float(c4.resources['cpu_quota']) / c4.resources['cpu_period'], E_CPU / MAX_CU * (float(3) / 4), places=5)
+ self.assertAlmostEqual(float(c4.resources['mem_limit']/1024/1024), float(E_MEM) / MAX_MU * 128, places=5)
self.assertAlmostEqual(rm.cpu_op_factor, 0.75)
c5 = createDummyContainerObject("c5", flavor="small")
rm.allocate(c5) # calculate allocation
- self.assertAlmostEqual(float(c5.cpu_quota) / c5.cpu_period, E_CPU / MAX_CU * (float(3) / 5), places=5)
- self.assertAlmostEqual(float(c5.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 128)
+ self.assertAlmostEqual(float(c5.resources['cpu_quota']) / c5.resources['cpu_period'], E_CPU / MAX_CU * (float(3) / 5), places=5)
+ self.assertAlmostEqual(float(c5.resources['mem_limit']/1024/1024), float(E_MEM) / MAX_MU * 128)
self.assertAlmostEqual(rm.cpu_op_factor, 0.6)
RUN echo 'install son-emu'
RUN apt-get install -y python-dev python-zmq libzmq-dev libffi-dev libssl-dev
RUN pip install -U zerorpc tabulate argparse networkx six ryu oslo.config pytest Flask flask_restful requests prometheus_client pyaml
-WORKDIR /
-RUN git clone https://github.com/sonata-nfv/son-emu.git
+WORKDIR /
+#avoid pulling not the latest git, copy the current dir, to run this from Jenkins
+#RUN git clone https://github.com/sonata-nfv/son-emu.git
+COPY . /son-emu
WORKDIR son-emu/
RUN python setup.py develop
WORKDIR /