Commit 9a742ce1 authored by Daniele Venzano's avatar Daniele Venzano

The Big Rename

Rename containers to services, applications to application descriptions, processes into service descriptions.
parent 8d3c0fde
......@@ -13,3 +13,30 @@ Authentication: HTTP basic auth is used, as it is the simplest reliable mechanis
There advantages and disadvantages to this choice, but for now we wnat to concentrate on different, more core-related features of Zoe.
Synchronous API. The Zoe Scheduler is not multi-thread, all requests to the API are served immediately. Again, this is done to keep the system simple and is by no means a decision set in stone.
Object naming
-------------
Every object in Zoe has a unique name. Zoe uses a dotted notation, with a hierarchical structure, left to right, from specific to generic, like the DNS system.
These names are used throughout the API.
A service (one service corresponds to one Docker container) is identified by this name:
<service_name>.<execution_name>.<owner>.<deployment_name>
An execution is identified by:
<execution_name>.<owner>.<deployment_name>
A user is:
<owner>.<deployment_name>
And a Zoe instance is:
<deployment_name>
Where:
* service name: the name of the service as written in the application description
* execution name: name of the execution as passed to the start API call
* owner: user name of the owner of the execution
* deployment name: configured deployment for this Zoe instance
Docker hostnames
^^^^^^^^^^^^^^^^
......@@ -12,6 +12,6 @@ service_time
Time in milliseconds taken to service a request. The tags associated with the request will add more details:
* action: get, post, delete, ...
* object: user, application, container, ...
* object: user, execution, service, ...
* user: user name of the authenticated user that performed the request
......@@ -25,7 +25,7 @@ from argparse import ArgumentParser, Namespace, FileType, RawDescriptionHelpForm
from pprint import pprint
from zoe_cmd import utils
from zoe_lib.containers import ZoeContainerAPI
from zoe_lib.services import ZoeServiceAPI
from zoe_lib.exceptions import ZoeAPIException
from zoe_lib.executions import ZoeExecutionsAPI
from zoe_lib.predefined_apps import hadoop, spark, lab_spark, test_sleep, copier
......@@ -134,7 +134,7 @@ def exec_start_cmd(args):
def exec_get_cmd(args):
exec_api = ZoeExecutionsAPI(utils.zoe_url(), utils.zoe_user(), utils.zoe_pass())
cont_api = ZoeContainerAPI(utils.zoe_url(), utils.zoe_user(), utils.zoe_pass())
cont_api = ZoeServiceAPI(utils.zoe_url(), utils.zoe_user(), utils.zoe_pass())
execution = exec_api.execution_get(args.id)
if execution is None:
print('Execution not found')
......@@ -159,24 +159,6 @@ def exec_kill_cmd(args):
exec_api.terminate(args.id)
def log_get_cmd(args):
cont_api = ZoeContainerAPI(utils.zoe_url(), utils.zoe_user(), utils.zoe_pass())
log = cont_api.log(args.container_id)
if log is None:
print("Error: No log found for container ID {}".format(args.container_id))
return
try:
print(log)
except BrokenPipeError:
pass
def container_stats_cmd(args):
cont_api = ZoeContainerAPI(utils.zoe_url(), utils.zoe_user(), utils.zoe_pass())
stats = cont_api.stats(args.container_id)
print(stats)
ENV_HELP_TEXT = '''To use this tool you need also to define three environment variables:
ZOE_URL: point to the URL of the Zoe Scheduler (ex.: http://localhost:5000/
ZOE_USER: the username used for authentication
......@@ -236,14 +218,6 @@ def process_arguments() -> Namespace:
argparser_execution_kill.add_argument('id', type=int, help="Execution id")
argparser_execution_kill.set_defaults(func=exec_kill_cmd)
argparser_log_get = subparser.add_parser('log-get', help="Retrieves the logs of a running container")
argparser_log_get.add_argument('container_id', type=int, help="Container id")
argparser_log_get.set_defaults(func=log_get_cmd)
argparser_container_stats = subparser.add_parser('container-stats', help="Retrieve statistics on a running container")
argparser_container_stats.add_argument('container_id', type=int, help="ID of the container")
argparser_container_stats.set_defaults(func=container_stats_cmd)
return parser, parser.parse_args()
......
......@@ -52,11 +52,11 @@ def app_validate(data):
if priority < 0 or priority > 1024:
raise InvalidApplicationDescription(msg="priority must be between 0 and 1024")
for p in data['processes']:
_process_check(p)
for p in data['services']:
_service_check(p)
found_monitor = False
for p in data['processes']:
for p in data['services']:
if p['monitor']:
found_monitor = True
break
......@@ -64,7 +64,7 @@ def app_validate(data):
raise InvalidApplicationDescription(msg="at least one process should have monitor set to True")
def _process_check(data):
def _service_check(data):
required_keys = ['name', 'docker_image', 'monitor', 'ports', 'required_resources']
for k in required_keys:
if k not in data:
......
......@@ -34,7 +34,7 @@ class ZoeExecutionsAPI(ZoeAPIBase):
Terminates an execution.
:param execution_id: the execution to delete
:return: True is the operation was successful, False otherwise
:return: True if the operation was successful, False otherwise
"""
data, status_code = self._rest_delete('/execution/' + str(execution_id))
if status_code == 204:
......
......@@ -14,7 +14,7 @@
# limitations under the License.
"""
This module contains all container-related API calls that a Zoe client can use.
This module contains all service-related API calls that a Zoe client can use.
"""
import logging
......@@ -24,68 +24,32 @@ from zoe_lib.exceptions import ZoeAPIException
log = logging.getLogger(__name__)
class ZoeContainerAPI(ZoeAPIBase):
class ZoeServiceAPI(ZoeAPIBase):
"""
The container API class. Containers are read-only objects. The delete operation merely informs the master that a container has died outside of its control.
The service API class. Services are read-only objects. The delete operation merely informs the master that a service has died outside of its control.
"""
def get(self, container_id: int) -> dict:
"""
Retrieve container state.
Retrieve service state.
:param container_id: the container to query
:param container_id: the service to query
:return:
"""
c, status_code = self._rest_get('/container/' + str(container_id))
c, status_code = self._rest_get('/service/' + str(container_id))
if status_code == 200:
return c
elif status_code == 404:
raise ZoeAPIException('container "{}" not found'.format(container_id))
raise ZoeAPIException('service "{}" not found'.format(container_id))
else:
raise ZoeAPIException('error retrieving container {}'.format(container_id))
def log(self, container_id: int) -> str:
"""
Get the standard output/error of the processes running in the given container.
:param container_id: the container to examine
:return: a string containing the log
"""
q = {
'what': 'container logs',
'filters': {'id': container_id}
}
data, status_code = self._rest_post('/query', q)
if status_code != 200:
raise ZoeAPIException(data['message'])
else:
return data[0]
def stats(self, container_id: int) -> dict:
"""
Get low-level statistics about a container. These come directly from Docker.
:param container_id: The container to examine
:return: the statistics. The format of this dictionary is not set in stone and could change.
"""
q = {
'what': 'container stats',
'filters': {'id': container_id}
}
data, status_code = self._rest_post('/query', q)
if status_code != 200:
raise ZoeAPIException(data['message'])
else:
return data
raise ZoeAPIException('error retrieving service {}'.format(container_id))
def died(self, container_id: int):
"""
Inform the master that a container died. Used by the observer process.
Inform the master that a service died. Used by the observer process.
:param container_id: Zoe ID of the container that died
:param container_id: Zoe ID of the service that died
:return:
"""
data, status_code = self._rest_delete('/container/{}'.format(container_id))
data, status_code = self._rest_delete('/service/{}'.format(container_id))
if status_code != 204:
raise ZoeAPIException(data['message'])
......@@ -151,7 +151,7 @@ class SwarmClient:
except requests.exceptions.ConnectionError:
if cont is not None:
self.cli.remove_container(container=cont.get('Id'), force=True)
raise ZoeException('Connection error while creating the container')
raise ZoeException('Connection error while creating the service')
info = self.inspect_container(cont.get('Id'))
return info
......@@ -203,24 +203,12 @@ class SwarmClient:
try:
self.cli.remove_container(docker_id, force=True)
except docker.errors.NotFound:
log.warning("cannot remove a non-existent container")
log.warning("cannot remove a non-existent service")
else:
try:
self.cli.kill(docker_id)
except docker.errors.NotFound:
log.warning("cannot remove a non-existent container")
def log_get(self, docker_id) -> str:
try:
logdata = self.cli.logs(container=docker_id, stdout=True, stderr=True, stream=False, timestamps=False, tail="all")
except docker.errors.NotFound:
return None
return logdata.decode("utf-8")
def stats(self, docker_id) -> ContainerStats:
stats_stream = self.cli.stats(docker_id, decode=True)
for s in stats_stream:
return ContainerStats(s)
log.warning("cannot remove a non-existent service")
def event_listener(self, callback):
for event in self.cli.events(decode=True):
......@@ -253,13 +241,13 @@ class SwarmClient:
try:
self.cli.connect_container_to_network(container_id, network_id)
except docker.errors.APIError:
log.exception('cannot connect container {} to network {}'.format(container_id, network_id))
log.exception('cannot connect service {} to network {}'.format(container_id, network_id))
def disconnect_from_network(self, container_id, network_id):
try:
self.cli.disconnect_container_from_network(container_id, network_id)
except docker.errors.APIError:
log.exception('cannot disconnect container {} from network {}'.format(container_id, network_id))
log.exception('cannot disconnect service {} from network {}'.format(container_id, network_id))
def list(self, only_label=None) -> list:
if only_label is None:
......@@ -282,7 +270,7 @@ class SwarmClient:
return conts
class ContainerOptions:
class DockerContainerOptions:
def __init__(self):
self.env = {}
self.volume_binds = []
......
......@@ -17,7 +17,7 @@ import time
from zoe_lib.workspace import ZoeWorkspace
from zoe_lib.executions import ZoeExecutionsAPI
from zoe_lib.containers import ZoeContainerAPI
from zoe_lib.services import ZoeServiceAPI
from zoe_lib.info import ZoeInfoAPI
......@@ -28,7 +28,7 @@ class ZoeWorkFlow:
self.workspace = ZoeWorkspace(workspace_base_path, identity, name)
self.exec_api = ZoeExecutionsAPI(self.identity['zoe_url'], self.identity['username'], self.identity['password'])
self.cont_api = ZoeContainerAPI(self.identity['zoe_url'], self.identity['username'], self.identity['password'])
self.cont_api = ZoeServiceAPI(self.identity['zoe_url'], self.identity['username'], self.identity['password'])
info_api = ZoeInfoAPI(self.identity['zoe_url'], self.identity['username'], self.identity['password'])
zoe_info = info_api.info()
......
......@@ -31,9 +31,9 @@ class GELFUDPHandler(socketserver.DatagramRequestHandler):
data = self.rfile.read()
data = gzip.decompress(data)
data = json.loads(data.decode('utf-8'))
cont_id = '.'.join([data['_zoe.container.name'], data['_zoe.execution.name'], data['_zoe.owner'], data['_zoe.prefix']])
log_line = ' '.join([data['timestamp'], data['host'], cont_id, data['short_message']])
self.server.kafka_producer.send(topic=cont_id, value=log_line)
service_id = '.'.join([data['_zoe.service.name'], data['_zoe.execution.name'], data['_zoe.owner'], data['_zoe.prefix']])
log_line = ' '.join([data['timestamp'], data['host'], service_id, data['short_message']])
self.server.kafka_producer.send(topic=service_id, value=log_line)
# log.debug(log_line)
......
......@@ -15,12 +15,12 @@
import logging
from zoe_lib.swarm_client import SwarmClient, ContainerOptions
from zoe_lib.swarm_client import SwarmClient, DockerContainerOptions
from zoe_lib.exceptions import ZoeException
from zoe_master.config import get_conf, singletons
from zoe_master.scheduler import ZoeScheduler
from zoe_master.state import execution as execution_module, application as application_module, container as container_module
from zoe_master.state import execution as execution_module, application as application_module, service as service_module
from zoe_master.state.manager import StateManager
from zoe_master.stats import ContainerStats
from zoe_master.stats import SwarmStats, SchedulerStats
......@@ -54,31 +54,29 @@ class PlatformManager:
self.state_manager.state_updated()
def _application_to_containers(self, execution: execution_module.Execution):
for process in execution.application.processes:
self._spawn_process(execution, process)
for service in execution.application.services:
self._spawn_service(execution, service)
def _spawn_process(self, execution: execution_module.Execution, process_description: application_module.Process) -> bool:
copts = ContainerOptions()
def _spawn_service(self, execution: execution_module.Execution, service_description: application_module.ServiceDescription) -> bool:
copts = DockerContainerOptions()
copts.gelf_log_address = get_conf().gelf_address
copts.name = get_conf().deployment_name + '-' + process_description.name + "-{}".format(execution.owner.name)
copts.set_memory_limit(process_description.required_resources['memory'])
copts.name = get_conf().deployment_name + '-' + service_description.name + "-{}".format(execution.owner.name)
copts.set_memory_limit(service_description.required_resources['memory'])
copts.network_name = '{}-usernet-{}'.format(get_conf().deployment_name, execution.owner.id)
container_id = self.state_manager.gen_id()
copts.labels = {
'zoe.{}'.format(get_conf().deployment_name): '',
'zoe.execution.id': str(execution.id),
'zoe.execution.name': execution.name,
'zoe.container.id': str(container_id),
'zoe.container.name': process_description.name,
'zoe.service.name': service_description.name,
'zoe.owner': execution.owner.name,
'zoe.prefix': get_conf().deployment_name,
'zoe.type': 'app_process'
'zoe.type': 'app_service'
}
if process_description.monitor:
if service_description.monitor:
copts.labels['zoe.monitor'] = ''
else:
copts.labels['zoe.normal'] = ''
copts.restart = not process_description.monitor # Monitor containers should not restart
copts.restart = not service_description.monitor # Monitor containers should not restart
# Generate a dictionary containing the current cluster status (before the new container is spawned)
# This information is used to substitute template strings in the environment variables
......@@ -88,36 +86,36 @@ class PlatformManager:
'user_name': execution.owner.name,
'name_prefix': get_conf().deployment_name
}
for env_name, env_value in process_description.environment:
for env_name, env_value in service_description.environment:
try:
env_value = env_value.format(**subst_dict)
except KeyError:
raise ZoeException("cannot find variable to substitute in expression {}".format(env_value))
copts.add_env_variable(env_name, env_value)
for path, mountpoint, readonly in process_description.volumes:
for path, mountpoint, readonly in service_description.volumes:
copts.add_volume_bind(path, mountpoint, readonly)
# The same dictionary is used for templates in the command
if process_description.command is not None:
copts.set_command(process_description.command.format(**subst_dict))
if service_description.command is not None:
copts.set_command(service_description.command.format(**subst_dict))
cont_info = self.swarm.spawn_container(process_description.docker_image, copts)
container = container_module.Container(self.state_manager)
container.docker_id = cont_info["docker_id"]
container.ip_address = cont_info["ip_address"]
container.name = copts.name
container.is_monitor = process_description.monitor
container.ports = [p.to_dict() for p in process_description.ports]
cont_info = self.swarm.spawn_container(service_description.docker_image, copts)
service = service_module.Service(self.state_manager)
service.docker_id = cont_info["docker_id"]
service.ip_address = cont_info["ip_address"]
service.name = copts.name
service.is_monitor = service_description.monitor
service.ports = [p.to_dict() for p in service_description.ports]
container.id = container_id
execution.containers.append(container)
container.execution = execution
service.id = container_id
execution.services.append(service)
service.execution = execution
for net in process_description.networks:
self.swarm.connect_to_network(container.docker_id, net)
for net in service_description.networks:
self.swarm.connect_to_network(service.docker_id, net)
self.state_manager.new('container', container)
self.state_manager.new('service', service)
return True
def execution_terminate(self, execution: execution_module.Execution, reason):
......@@ -126,18 +124,13 @@ class PlatformManager:
:param reason: termination reason
:return:
"""
logs = []
if len(execution.containers) > 0:
containers = execution.containers.copy()
if len(execution.services) > 0:
containers = execution.services.copy()
for c in containers:
assert isinstance(c, container_module.Container)
l = self.log_get(c.id)
if l is not None:
logs.append((c.name, l))
assert isinstance(c, service_module.Service)
self.swarm.terminate_container(c.docker_id, delete=True)
self.state_manager.delete('container', c.id)
log.info('Container {} terminated'.format(c.name))
execution.store_logs(logs)
self.state_manager.delete('service', c.id)
log.info('Service {} terminated'.format(c.name))
if reason == 'error':
execution.set_error()
......@@ -149,12 +142,12 @@ class PlatformManager:
self.scheduler.execution_terminate(execution)
def start_gateway_container(self, user):
copts = ContainerOptions()
copts = DockerContainerOptions()
copts.name = '{}-gateway-{}'.format(get_conf().deployment_name, user.id)
copts.network_name = '{}-usernet-{}'.format(get_conf().deployment_name, user.id)
copts.ports.append(1080)
copts.labels = {
'zoe.{}.gateway'.format(get_conf().deployment_name): '',
'zoe.{}'.format(get_conf().deployment_name): '',
'zoe.owner': user.name,
'zoe.prefix': get_conf().deployment_name,
'zoe.type': 'gateway'
......@@ -186,19 +179,8 @@ class PlatformManager:
log.info('Removing network for user {}'.format(user.name))
self.swarm.network_remove(user.network_id)
def log_get(self, container_id: int) -> str:
container = self.state_manager.get_one('container', id=container_id)
if container is None:
return ''
else:
return self.swarm.log_get(container.docker_id)
def container_stats(self, container_id: int) -> ContainerStats:
container = self.state_manager.get_one('container', id=container_id)
return self.swarm.stats(container.docker_id)
def is_container_alive(self, container: container_module.Container) -> bool:
ret = self.swarm.inspect_container(container.docker_id)
def is_service_alive(self, service: service_module.Service) -> bool:
ret = self.swarm.inspect_container(service.docker_id)
if ret is None:
return False
return ret["running"]
......@@ -299,12 +281,12 @@ class PlatformManager:
# ### Check executions and container consistency
swarm_containers = self.swarm.list(only_label='zoe.{}'.format(get_conf().deployment_name))
conts_state_to_delete = []
for c_id, c in self.state_manager.containers.items():
for c_id, c in self.state_manager.services.items():
if c.docker_id not in [x['id'] for x in swarm_containers]:
log.error('fixed: removing from state container {} that does not exist in Swarm'.format(c.name))
conts_state_to_delete.append(c_id)
for c_id in conts_state_to_delete:
self.state_manager.delete('container', c_id)
self.state_manager.delete('service', c_id)
if state_changed or len(users_no_gateway) > 0 or len(users_no_network) > 0:
self.state_manager.state_updated()
......@@ -19,7 +19,7 @@ from flask_restful import Api
from zoe_master.rest_api.user import UserAPI, UserCollectionAPI
from zoe_master.rest_api.execution import ExecutionAPI, ExecutionCollectionAPI
from zoe_master.rest_api.container import ContainerAPI
from zoe_master.rest_api.service import ServiceAPI
from zoe_master.rest_api.query import QueryAPI
from zoe_master.rest_api.info import InfoAPI
from zoe_lib.version import ZOE_API_VERSION
......@@ -41,7 +41,7 @@ def init(state, platform) -> Flask:
api.add_resource(UserCollectionAPI, API_PATH + '/user', resource_class_kwargs=args)
api.add_resource(ExecutionAPI, API_PATH + '/execution/<int:execution_id>', resource_class_kwargs=args)
api.add_resource(ExecutionCollectionAPI, API_PATH + '/execution', resource_class_kwargs=args)
api.add_resource(ContainerAPI, API_PATH + '/container/<int:container_id>', resource_class_kwargs=args)
api.add_resource(ServiceAPI, API_PATH + '/service/<int:container_id>', resource_class_kwargs=args)
api.add_resource(QueryAPI, API_PATH + '/query', resource_class_kwargs=args)
return app
......
......@@ -66,16 +66,6 @@ class QueryAPI(Resource):
elif what == 'stats scheduler':
ret = self.platform.scheduler_stats()
ret = {'stats': ret.to_dict()}
elif what == 'execution logs':
ret = None # TODO
elif what == 'container logs':
c_list = self.state.get('container', **filters)
logs = self._get_container_logs(c_list)
ret = logs
elif what == 'container stats':
c_list = self.state.get('container', **filters)
stats = self._get_container_stats(c_list)
ret = [s.to_dict() for s in stats]
else:
ret = self.state.get(what, **filters)
ret = [o.to_dict(checkpoint=False) for o in ret]
......
......@@ -28,7 +28,7 @@ from zoe_master.config import singletons
log = logging.getLogger(__name__)
class ContainerAPI(Resource):
class ServiceAPI(Resource):
"""
:type state: StateManager
:type platform: PlatformManager
......@@ -42,12 +42,12 @@ class ContainerAPI(Resource):
start_time = time.time()
calling_user = authenticate(request, self.state)
c = self.state.get_one('container', id=container_id)
c = self.state.get_one('service', id=container_id)
if c is None:
raise ZoeRestAPIException('No such container', 404)
raise ZoeRestAPIException('No such service', 404)
is_authorized(calling_user, c, 'get')
singletons['metric'].metric_api_call(start_time, 'container', 'get', calling_user)
singletons['metric'].metric_api_call(start_time, 'service', 'get', calling_user)
return c.to_dict(checkpoint=False)
@catch_exceptions
......@@ -55,21 +55,21 @@ class ContainerAPI(Resource):
start_time = time.time()
calling_user = authenticate(request, self.state)
c = self.state.get_one('container', id=container_id)
c = self.state.get_one('service', id=container_id)
if c is None:
raise ZoeRestAPIException('No such container', 404)
raise ZoeRestAPIException('No such service', 404)
is_authorized(calling_user, c, 'delete')
if c.is_monitor:
log.info("Monitor container died ({}), terminating execution {}".format(c.name, c.execution.name))
log.info("Monitor service died ({}), terminating execution {}".format(c.name, c.execution.name))
self.platform.execution_terminate(c.execution, reason='finished')
self.state.state_updated()
else:
# A non-fundamental container died, nothing we can do?
# FIXME: A non-fundamental service died, nothing we can do?
# We leave everything in place, so when the execution terminates we will
# gather the logs also of the containers that died
log.warning("Container {} died by itself".format(c.name))
# gather the logs also of the services that died
log.warning("Service {} died by itself".format(c.name))
singletons['metric'].metric_api_call(start_time, 'container', 'get', calling_user)
singletons['metric'].metric_api_call(start_time, 'service', 'get', calling_user)
return '', 204
......@@ -17,7 +17,7 @@ import logging
import queue
from zoe_lib.exceptions import ZoeException
from zoe_master.state.application import Application
from zoe_master.state.application import ApplicationDescription
from zoe_master.state.execution import Execution
from zoe_master.scheduler_policies.base import BaseSchedulerPolicy
......@@ -42,7 +42,7 @@ class ZoeScheduler:
"""
self.scheduler_policy = policy_class(self.platform.status)
def validate(self, app: Application) -> bool:
def validate(self, app: ApplicationDescription) -> bool:
"""
It is used to validate an execution that is going to be started.
:param app:
......
......@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from zoe_master.state.application import Application
from zoe_master.state.application import ApplicationDescription
from zoe_master.state.execution import Execution
from zoe_master.stats import SchedulerStats
......@@ -22,7 +22,7 @@ class BaseSchedulerPolicy:
def __init__(self, platform):
self.platform = platform
def admission_control(self, app: Application) -> bool:
def admission_control(self, app: ApplicationDescription) -> bool:
"""
Checks whether an execution requiring the specified resources can be run, now or at a later time. This method can be called
from outside the scheduler thread, should not have any side effects nor change any state.
......
......@@ -17,7 +17,7 @@ import time
from zoe_master.stats import SchedulerStats
from zoe_master.scheduler_policies.base import BaseSchedulerPolicy
from zoe_master.state.application import Application
from zoe_master.state.application import ApplicationDescription
from zoe_master.state.execution import Execution
from zoe_master.config import singletons
......@@ -28,7 +28,7 @@ class FIFOSchedulerPolicy(BaseSchedulerPolicy):
self.waiting_list = []
self.starting_list = []
def admission_control(self, app: Application) -> bool:
def admission_control(self, app: ApplicationDescription) -> bool:
swarm_stats = singletons['stats_manager'].swarm_stats
if app.total_memory() < swarm_stats.memory_total:
return True
......
......@@ -16,7 +16,7 @@
from zoe_lib.exceptions import InvalidApplicationDescription
class Application:
class ApplicationDescription:
def __init__(self):
self.name = ''
......@@ -24,7 +24,7 @@ class Application:
self.will_end = True
self.priority = 512
self.requires_binary = False
self.processes = []
self.services = []
def to_dict(self):
d = {
......@@ -33,10 +33,10 @@ class Application:
'will_end': self.will_end,
'priority': self.priority,
'requires_binary': self.requires_binary,
'processes': []
'services': []
}
for p in self.processes:
d['processes'].append(p.to_dict())
for p in self.services:
d['services'].append(p.to_dict())
return d
......@@ -70,30 +70,30 @@ class Application:
if self.priority < 0 or self.priority > 1024:
raise InvalidApplicationDescription("priority must be between 0 and 1024")
for p in data['processes']:
aux = Process()
for p in data['services']:
aux = ServiceDescription()
aux.from_dict(p)
self.processes.append(aux)
self.services.append(aux)
found_monitor = False
for p in self.processes:
for p in self.services:
if p.monitor:
found_monitor = True
break
if not found_monitor:
raise InvalidApplicationDescription("at least one process should have monitor set to True")
raise InvalidApplicationDescription("at least one service should have monitor set to True")
def total_memory(self) -> int:
memory = 0
for p in self.processes:
for p in self.services:
memory += p.required_resources['memory']
return memory
def container_count(self) -> int:
return len(self.processes)
def service_count(self) -> int:
return len(self.services)