diff --git a/README.md b/README.md index 9e381d6..f1a6c37 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,38 @@ # GoCloud +_Containers_ + +Statuses: + +- 0: Неактивен +- 1: Активен +- 2: Процесс активации +- 3: Процесс деактивации +- 4: Создание... +- 5: Удаление... + +_Tasks_ + +Statuses: + +- 0: new +- 1: working +- 2: completed + ## Develop ### Public to local PyPI server `python setup.py sdist upload -r local` + +## Test + +### User API + +### Server API + +_Get containers status_ + +`curl -X GET http://127.0.0.1:5000/server_api/containers/status/ -u f411b7d6-bf93-4fcd-91ee-03e5343d0187:b3c9a8b0-95ca-11e5-bec1-28d244e159e9` + +`curl -X POST http://127.0.0.1:5000/server_api/containers/status/663b31b4-22b1-4846-bfaf-27d6389beef4 -u f411b7d6-bf93-4fcd-91ee-03e5343d0187:b3c9a8b0-95ca-11e5-bec1-28d244e159e9 -d 'status=0&message="test"'` \ No newline at end of file diff --git a/SWSCloudCore/controllers/containers/server.py b/SWSCloudCore/controllers/containers/server.py index 6ae8a5c..68f810e 100644 --- a/SWSCloudCore/controllers/containers/server.py +++ b/SWSCloudCore/controllers/containers/server.py @@ -5,6 +5,9 @@ from SWSCloudCore import models class ControllerContainersServer: def status_set(self, container_id, status): + return self.set_container_status(container_id, status) + + def set_container_status(self, container_id, status): ns = models.Containers.update(status=status).where( models.Containers.id == container_id ) @@ -12,6 +15,40 @@ class ControllerContainersServer: return True def exists(self, container_id): - if models.Containers.select().where(models.Containers.id == container_id).count() == 0: + if models.Containers.select().where( + models.Containers.id == container_id + ).count() == 0: return False return True + + def get_container(self, container_id): + x = models.Containers.select().where( + models.Containers.id == container_id + ).get() + + return { + 'id': str(x.id), + 'datacenter_id': str(x.datacenter.id), + 'server_id': str(x.server.id), + 'ipv4': x.ipv4, + 'ipv6': x.ipv6, + 'status': x.status + } + + def get_containers_by_server(self, server_id): + x = models.Containers.select().where( + models.Containers.server == server_id + ).execute() + containers = list() + for i in x: + containers.append({ + 'id': str(i.id), + 'datacenter_id': str(i.datacenter.id), + 'server_id': str(i.server.id), + 'ipv4': i.ipv4, + 'ipv6': i.ipv6, + 'status': i.status + }) + return containers + + diff --git a/SWSCloudCore/controllers/servers/server.py b/SWSCloudCore/controllers/servers/server.py index c707f03..afffebf 100644 --- a/SWSCloudCore/controllers/servers/server.py +++ b/SWSCloudCore/controllers/servers/server.py @@ -3,6 +3,20 @@ import datetime from SWSCloudCore import models +class ControllerServerServers: + def get_secret(self, server_id): + return models.Servers.select(models.Servers.secret).where(models.Servers.id == server_id).get().secret + + def exists(self, server_id): + try: + if models.Servers.select().where(models.Servers.id == server_id).count() == 0: + return False + except Exception as e: + return False + else: + return True + + class ControllerServerStatistics: def write(self, container_id, cpu, memory, size, net_tx, net_rx, net_total): models.ContainersStatistics.create( diff --git a/SWSCloudCore/controllers/tasks/server.py b/SWSCloudCore/controllers/tasks/server.py index 519f2b1..1f512f4 100644 --- a/SWSCloudCore/controllers/tasks/server.py +++ b/SWSCloudCore/controllers/tasks/server.py @@ -1,31 +1,29 @@ # coding: utf-8 import json - from SWSCloudCore import models from SWSCloudCore.controllers.containers.server import ControllerContainersServer -# from peewee import fn - - class ControllerTasksServer: - def __init__(self, server_id, secret): + def __init__(self, server_id): self.server_id = server_id - self.secret = secret - def auth(self): - if models.Servers.select().where( - models.Servers.id == self.server_id, - models.Servers.secret == self.secret - ).count() == 0: - return False - return True + def queue_item_oldest_get(self, status=0): + """ + Вытащить самую старую задачу из всех активных + :param status: + :return: + """ + return models.Tasks.select().where( + models.Tasks.server == self.server_id, + models.Tasks.status == status + ).order_by(models.Tasks.created.asc()).get() - def count(self): - return models.Tasks.select( - models.Servers.id == self.server_id, - models.Servers.secret == self.secret + def count(self, status=0): + return models.Tasks.select().where( + models.Tasks.server == self.server_id, + models.Tasks.status == status ).count() def get(self, status=0): @@ -106,7 +104,7 @@ class ControllerTasksServer: return True def get_item(self, task_id): - get_task = models.Tasks.select().where(models.Tasks.id == task_id)[0] + get_task = models.Tasks.select().where(models.Tasks.id == task_id).get() return { 'id': get_task.id, 'datacenter': get_task.datacenter.id, diff --git a/SWSCloudCore/models.py b/SWSCloudCore/models.py index 73f016e..9732127 100644 --- a/SWSCloudCore/models.py +++ b/SWSCloudCore/models.py @@ -171,9 +171,7 @@ class Tasks(PgSQLModel): user = ForeignKeyField(Users) created = DateTimeField(default=datetime.datetime.now) task = CharField(null=False) - # 0 - new - # 1 - working - # 2 - completed + # 0 - new, 1 - working, 2 - completed status = IntegerField(null=False, default=0) plain = TextField() diff --git a/SWSCloudCore/templates/administrator/servers/edit.html b/SWSCloudCore/templates/administrator/servers/edit.html index 52cd039..c1a71af 100644 --- a/SWSCloudCore/templates/administrator/servers/edit.html +++ b/SWSCloudCore/templates/administrator/servers/edit.html @@ -26,6 +26,10 @@ Hostname + + Secret + + Status @@ -37,5 +41,5 @@ - Cancel + Cancel {% endblock %} \ No newline at end of file diff --git a/SWSCloudCore/views/account/__init__.py b/SWSCloudCore/views/account/__init__.py index f941c05..dbca80d 100644 --- a/SWSCloudCore/views/account/__init__.py +++ b/SWSCloudCore/views/account/__init__.py @@ -6,7 +6,6 @@ from flask import request, flash from sshpubkeys import SSHKey from SWSCloudCore.controllers.billing import ControllerBilling from SWSCloudCore.controllers.common import ControllerMessagesEmail -from SWSCloudCore.controllers.common import special_match from SWSCloudCore.controllers.users import ControllerAPI from SWSCloudCore.controllers.users import ControllerSSHKey from SWSCloudCore.controllers.users import ControllerUsers diff --git a/SWSCloudCore/views/administrator/__init__.py b/SWSCloudCore/views/administrator/__init__.py index 6d222b5..b899224 100644 --- a/SWSCloudCore/views/administrator/__init__.py +++ b/SWSCloudCore/views/administrator/__init__.py @@ -371,7 +371,7 @@ def servers_create(): ) -@viewAdministrator.route('/servers/edit/', methods=['GET', 'POST']) +@viewAdministrator.route('/servers/edit/.html', methods=['GET', 'POST']) def servers_edit(server_id): # check session if not ControllerAdministrators().check_session(): @@ -383,7 +383,7 @@ def servers_edit(server_id): if models.Servers.select().where(models.Servers.id == server_id).count() == 0: return redirect(url_for('administrator.servers')) - server_details = models.Servers.select().where(models.Servers.id == server_id).limit(1)[0] + server_details = models.Servers.select().where(models.Servers.id == server_id).get() return render_template('administrator/servers/edit.html', server=server_details) diff --git a/SWSCloudCore/views/api/__init__.py b/SWSCloudCore/views/api/__init__.py index d18cc67..97cc9d3 100644 --- a/SWSCloudCore/views/api/__init__.py +++ b/SWSCloudCore/views/api/__init__.py @@ -1,6 +1,5 @@ # coding: utf-8 - from flask import Blueprint, jsonify, request from SWSCloudCore.controllers.containers import ControllerContainers from SWSCloudCore.controllers.datacenters import ControllerDataCenters @@ -24,7 +23,6 @@ def auth(): """ email = request.form['email'] secret = request.form['secret'] - # expire = request.form['expire'] if not ControllerAPI().auth(email, secret): return jsonify(status=1) @@ -44,14 +42,13 @@ def datacenter_list(): secret = request.form['secret'] # if not ControllerAPI().auth(email, secret): - return jsonify(status=1) + return jsonify(status=403), 403 # user_id = ControllerUsers().get_id_by_email(email) # get containers list datacenters = ControllerDataCenters().get() # return jsonify( - status=0, total=datacenters['total'], items=datacenters['items'] ) diff --git a/SWSCloudCore/views/containers/__init__.py b/SWSCloudCore/views/containers/__init__.py index dfaeed4..25c3c76 100644 --- a/SWSCloudCore/views/containers/__init__.py +++ b/SWSCloudCore/views/containers/__init__.py @@ -2,7 +2,7 @@ import uuid from flask import Blueprint, g, redirect, render_template, request, session -from flask import url_for +from flask import url_for, flash from SWSCloudCore.controllers.billing import ControllerBilling from SWSCloudCore.controllers.common import ControllerCommon @@ -15,7 +15,6 @@ from SWSCloudCore.controllers.ips import ControllerIps from SWSCloudCore.controllers.tasks import ControllerTasks from SWSCloudCore.controllers.users import ControllerSSHKey from SWSCloudCore.controllers.users import ControllerUsers -from SWSCloudCore import models viewContainers = Blueprint('containers', __name__, url_prefix='/containers') diff --git a/SWSCloudCore/views/server_api/__init__.py b/SWSCloudCore/views/server_api/__init__.py index 1e16066..7ba9273 100644 --- a/SWSCloudCore/views/server_api/__init__.py +++ b/SWSCloudCore/views/server_api/__init__.py @@ -1,28 +1,48 @@ # coding: utf-8 import json -from flask import Blueprint, jsonify, request +from flask import Blueprint, jsonify, request, g +from flask_httpauth import HTTPBasicAuth from SWSCloudCore.controllers.servers.server import ControllerServerStatistics +from SWSCloudCore.controllers.servers.server import ControllerServerServers from SWSCloudCore.controllers.tasks.server import ControllerTasksServer from SWSCloudCore.controllers.containers.server import ControllerContainersServer +api_auth = HTTPBasicAuth() viewServerAPI = Blueprint('server_api', __name__, url_prefix='/server_api') +@api_auth.get_password +def get_pw(server_id): + if ControllerServerServers().exists(server_id): + g.server_id = server_id + return ControllerServerServers().get_secret(g.server_id) + return None + + +@api_auth.error_handler +def auth_error(): + return jsonify(status='error', description='Unauthorized'), 403 + + +@viewServerAPI.route('/ping') +@api_auth.login_required +def ping(): + """ + Тест. Проверка соединения и авторизации + """ + return jsonify(ping='pong', server_id=g.server_id), 200 + + +# TASKS @viewServerAPI.route('/tasks', methods=['GET']) +@api_auth.login_required def tasks_list(): - node_id = request.args.get('node_id') - node_secret = request.args.get('node_secret') - server_api = ControllerTasksServer(node_id, node_secret) - - # auth request - if not server_api.auth(): - # status: 403 - access denied - return jsonify({'status': 403}) - - result = dict() - result['status'] = 0 - result['results'] = [] + """ + Список + """ + server_api = ControllerTasksServer(g.server_id) + result = dict(tasks=list()) for task in server_api.get(): result['results'].append({ @@ -38,39 +58,56 @@ def tasks_list(): return jsonify(result) -@viewServerAPI.route('/task_status_update', methods=['GET']) -def task_status_update(): - node_id = request.args.get('node_id') - node_secret = request.args.get('node_secret') - server_api = ControllerTasksServer(node_id, node_secret) +@viewServerAPI.route('/task', methods=['GET']) +@api_auth.login_required +def task_item(): + """ + Самая первая задача в очереди + """ + sapi = ControllerTasksServer(g.server_id) - # auth request - if not server_api.auth(): - # status: 403 - access denied - return jsonify({'status': 403}) + # Если задач нет, то надо вернуть ответ с кодом 204 (no content) + if sapi.count() == 0: + return '', 204 - task_id = request.args.get('task_id') - status = int(request.args.get('status')) + task = sapi.queue_item_oldest_get(status=0) - server_api.update(task_id, status) + result = dict( + id=task.id, + datacenter=task.datacenter.id, + server=task.server.id, + user=task.user.id, + task=task.task, + created=task.created, + status=task.status, + plain=json.loads(task.plain), + ) + return jsonify( + task=result, + status='success' + ) + +@viewServerAPI.route('/tasks/', methods=['PUT']) +@api_auth.login_required +def task_update(task_id): + """ + Обновление статуса задачи + """ + server_api = ControllerTasksServer(g.server_id) + + if 'status' in request.form: + status = int(request.form['status']) + server_api.update(task_id, status) return jsonify({'status': 0}) -@viewServerAPI.route('/report/container_status', methods=['POST']) -def report_container_status(): - node_id = request.form['node_id'] - node_secret = request.form['node_secret'] - - # print request.form['node_id'] - # auth request - if not ControllerTasksServer(node_id, node_secret).auth(): - # status: 403 - access denied - return jsonify({'status': 403}) - +# CONTAINERS +@viewServerAPI.route('/containers/stats/', methods=['POST']) +@api_auth.login_required +def report_container_status(container_id): """ { - "container_id": "16459f60-a1ee-11e5-9108-28d244e159e9", "cpu_use": 644394623336, "memory_use": 614473728, "link": "vethB2RLMU" @@ -79,9 +116,8 @@ def report_container_status(): "total_bytes": 1097776429 } """ + statistics = json.loads(request.form['status']) - # print statistics - container_id = statistics['container_id'] if 'cpu_use' not in statistics: return jsonify({}) @@ -110,5 +146,52 @@ def report_container_status(): int(statistics['rx_bytes']), int(statistics['total_bytes']) ) + return jsonify(status='success') - return jsonify({}) + +@viewServerAPI.route('/containers/status/', methods=['GET']) +@api_auth.login_required +def containers_status(): + """ + Состояние всех контейнеров в биллинге, и привести контейнеры к указанному состоянию + На случай, когда нода была перезагружена. + """ + # Get container list (with status) located on node + return jsonify( + status='success', + containers=ControllerContainersServer().get_containers_by_server(g.server_id) + ) + + +@viewServerAPI.route('/containers/status/', methods=['GET', 'POST']) +@api_auth.login_required +def container_status_update(container_id): + """ + Если на ноде при работе с контейнером что-то поло не так, + то нода отправляет сообщение об ошибке и изменяет статус контейнера. + Отправленное сообщение может быть прочитано администратором или владельцем контейнера + """ + # Check exists container + if not ControllerContainersServer().exists(container_id): + return '', 404 + + # Get information about container + if request.method == 'GET': + return jsonify( + status='success', + container=ControllerContainersServer().get_container(container_id) + ) + + # Set container status + if 'status' in request.form: + ControllerContainersServer().set_container_status( + container_id, + request.form['status'] + ) + # TODO: 0000001 + + return jsonify(status='success') + return jsonify( + status='error', + message='must contain parameter: status' + )