Skip to content

Commit

Permalink
+ logs, activities, bugfix
Browse files Browse the repository at this point in the history
  • Loading branch information
Lksman committed Apr 19, 2021
1 parent 59a2f11 commit 2743228
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 97 deletions.
63 changes: 31 additions & 32 deletions sys-commander/backend/app/api/activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@

'''
TODO
I've implemented these wrong, only the scheduled api route seems ok.
I've implemented these wrong - needs rework
- Lukas
'''

@api.route("/")
class ActivityListAll(Resource):
@api.doc("get all celery activities")
@api.doc("get all activities")
def get(self):
# task_service.all()

print ("get all celery activities")
i = app_celery.control.inspect()
activeTasks = i.active()
Expand All @@ -34,42 +35,40 @@ def get(self):
return jsonify( activeTasks, scheduledTasks, finishedTasks )


@api.route("/active")
class ActivityListActive(Resource):
@api.doc("get all active celery activities")
def get(self):
print ("get all active celery activities")
i = app_celery.control.inspect()
activeTasks = i.active()
# @api.route("/active")
# class ActivityListActive(Resource):
# @api.doc("get all active celery activities")
# def get(self):
# print ("get all active celery activities")
# i = app_celery.control.inspect()
# activeTasks = i.active()

return jsonify( activeTasks )
# return jsonify( activeTasks )


@api.route("/scheduled")
class ActivityListScheduled(Resource):
@api.doc("get all scheduled activities")
def get(self):
print ("get all scheduled celery activities")
i = app_celery.control.inspect()
scheduledTasks = i.reserved()
# @api.route("/scheduled")
# class ActivityListScheduled(Resource):
# @api.doc("get all scheduled activities")
# def get(self):
# print ("get all scheduled celery activities")
# i = app_celery.control.inspect()
# scheduledTasks = i.reserved()

return jsonify( scheduledTasks )
# return jsonify( scheduledTasks )


@api.route("/finished")
class ActivityListFinished(Resource):
@api.doc("get all finished celery activities")
def get(self):
print ("get all scheduled celery activities")
i = app_celery.control.inspect()
doneTasks = "done placeholder" #i.done()
# @api.route("/finished")
# class ActivityListFinished(Resource):
# @api.doc("get all finished celery activities")
# def get(self):
# print ("get all scheduled celery activities")
# i = app_celery.control.inspect()
# doneTasks = "done placeholder" #i.done()

return jsonify( doneTasks )
# return jsonify( doneTasks )

@api.route("/<id>")
@api.doc(params={'id': 'Activity id'})
@api.route("/active")
class Activity(Resource):
@api.doc("get celery task by ID")
def get(self, id):
print("get task-info of activity: {}".format(id))
return jsonify({"status": 200, "msg":"Details for Celery Task %s are"%id } )
@api.doc("get active activities")
def get(self):
return jsonify({"status": 200, "msg":"Details for Activity"} )
59 changes: 40 additions & 19 deletions sys-commander/backend/app/api/instance.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import os
import json
import docker

from flask import Flask, request
from flask_restplus import Namespace, Api, Resource, fields
from backend.app import app, db, restapi

from backend.app.bibbox.instance_controler import installInstance, stopInstance, deleteInstance, testProcessAsync, updateInstanceInfos
from backend.app.bibbox.instance_controler import installInstance, startInstance, stopInstance, restartInstance, deleteInstance, testProcessAsync, updateInstanceInfos
from backend.app.bibbox.file_handler import FileHandler
from backend.app.bibbox.docker_handler import DockerHandler

Expand Down Expand Up @@ -45,6 +46,36 @@ def get(self):
testProcessAsync.delay()
return {"reply":"PING"}


@api.route('/stop/<string:id>')
@api.doc("Stop all Instance Containers")
class Ping(Resource):
def get(self, id):

stopInstance.delay(id)

return {"stopping instance": id}, 200

@api.route('/start/<string:id>')
@api.doc("Start all Instance Containers")
class Ping(Resource):
def get(self, id):

startInstance.delay(id)

return {"starting instance": id}, 200

@api.route('/restart/<string:id>')
@api.doc("Restart all Instance Containers")
class Ping(Resource):
def get(self, id):

restartInstance.delay(id)

return {"restarting instance": id}, 200



@api.route('/')
class InstanceList(Resource):
def get(self):
Expand Down Expand Up @@ -78,10 +109,10 @@ def post(self, id):
instanceDescr = request.json
instanceDescr['instancename'] = id
instanceDescr['state'] = 'JUSTBORN'
instanceDescr['displayname_long'] = '' # TODO remove later, as we put these key-value pairs into the payload sent from the frontend
instanceDescr['description_short'] = '' # remove later
instanceDescr['description_long'] = '' # remove later

instanceDescr['displayname_long'] = ''
instanceDescr['description_short'] = ''
instanceDescr['description_long'] = ''
jobID = 27
jobURL = "api/v1/activities/27"

Expand Down Expand Up @@ -156,22 +187,12 @@ def get(self, id):
logs = {}

try:
# I'm getting an Error trying to instanciate the docker handler.
# -> Error while fetching server API version: ('Connection aborted.', PermissionError(13, 'Permission denied'))
# Does not show up when DockerHandler is instanciated during celery task
# dh = DockerHandler()
# logs = dh.docker_getContainerLogs(id)

logs = {
f'{id}-wordpress': ['log1', 'log2 testing linewrap ' + '#'*200, 'log3', 'log4', '', 'log6'], # testing empty log
f'{id}-wordpress-adminer': ['testing scrolling: {}'.format(x) for x in range(100)],
f'{id}-wordpress-db': ['log1'] # testing if container shows

}

dh = DockerHandler()
logs = dh.docker_getContainerLogs(id)

except Exception as ex:
print(ex)
logs = {}
logs = {'error': ex}


return logs, 200
48 changes: 20 additions & 28 deletions sys-commander/backend/app/bibbox/docker_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,37 @@ class DockerHandler():

def __init__(self):
self.client = docker.from_env()
self.INSTANCEPATH = "/opt/bibbox/instances/"


# unused
def docker_stopAllApps(self):
app_containers = []
for container in self.client.containers.list():
if "bibbox-sys-commander" not in container.name:
app_containers.append(container)

self.__stopContainers(app_containers)


# TODO: docker-compose stop
def docker_stopInstance(self, instance_name):
instance_containers = []
for container in self.client.containers.list():
if "{}-".format(instance_name) in container.name:
instance_containers.append(container)
try:
# TODO:
# shell=True is a security hazard, so we must sanitize the input
command = f"cd {self.INSTANCEPATH}{instance_name}; docker-compose stop"
subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf8", shell=True)

self.__stopContainers(instance_containers)


# unused
def docker_deleteAllStoppedApps(self):
# TODO: exclude bibbox-sys-commander-* containers
except Exception as ex:
print(ex)

def docker_startInstance(self, instance_name):
try:
# TODO:
# shell=True is a security hazard, so we must sanitize the input
command = f"cd {self.INSTANCEPATH}{instance_name}; docker-compose start"
subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf8", shell=True)

except Exception as ex:
print(ex)

print("Removing stopped containers...")
removed = self.client.containers.prune()
if removed['ContainersDeleted']:
print("Removed containers: {}".format([container_id for container_id in removed['ContainersDeleted']]))
else:
print("No containers to remove.")


def docker_getContainerLogs(self, instance_name):
container_logs_dict = {}
containers = self.client.containers.list(filters={"label":["com.docker.compose.project={}".format(instance_name)]})

for container in containers:
container_logs_dict[container.name] = str(container.logs(tail=200)).split('\\n')
container_logs_dict[container.name] = str(container.logs(tail=200))[3:-1].split('\\n')

## subprocess implementation
# command = 'docker logs {} --tail 200'.format(container.name).split()
Expand Down
6 changes: 4 additions & 2 deletions sys-commander/backend/app/bibbox/file_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# which makes a local copy/cache of the github repository with the correct
# version.
#
# in offline mode, we couöd then just read from the local dir
# in offline mode, we could then just read from the local dir
# => we need a global config saying, that we work in offline mode
# => and a cache-all function downling all the reproes and builiding all images
#
Expand Down Expand Up @@ -117,10 +117,12 @@ def checkDirectoryStructure(self):
def updateInstanceJsonState (self, instance_name, state_to_set):
# set state of instance
# info: may soon be deprecated as we modify the instance / instanceDescription class
#content = self.__readJsonFile(self.INSTANCEPATH + instance_name + "/instance.json")

if state_to_set not in InstanceDescription().states():
raise Exception("Error occurred during update of instance.json: Trying to set unknown instance state.")
else:
content["state"] = state_to_set
#content["state"] = state_to_set
try:
self.updateInstanceJsonInfo(instance_name, {'state' : state_to_set})
except Exception as ex:
Expand Down
33 changes: 22 additions & 11 deletions sys-commander/backend/app/bibbox/instance_controler.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,37 +31,48 @@


@app_celerey.task(bind=True, name='instance.stopInstance')
def stopInstance (self, instanceName):
dh = DockerHandler();
dh.docker_stopInstance(instanceName)
def stopInstance (self, instance_name):
dh = DockerHandler()
dh.docker_stopInstance(instance_name)

@app_celerey.task(bind=True, name='instance.startInstance')
def startInstance (self, instanceName):
pass
def startInstance (self, instance_name):
dh = DockerHandler()
dh.docker_startInstance(instance_name)

@app_celerey.task(bind=True, name='instance.restartInstance')
def restartInstance (self, instance_name):
dh = DockerHandler()
dh.docker_stopInstance(instance_name)
dh.docker_startInstance(instance_name)

@app_celerey.task(bind=True, name='instance.copyInstance')
def copyInstance (self, instanceNameSrc, instanceNameDest):
pass

@app_celerey.task(bind=True, name='instance.updateInstanceInfos')
def updateInstanceInfos (self, instanceName, payload):
def updateInstanceInfos (self, instance_name, payload):
fh = FileHandler()

# activity service for db-stuff with activity entries
activity_service = ActivityService()


# create activity entry in db -> returns ID of created entry
activity_id = activity_service.create(f"Update instance: {instanceName}", "UPDATE_INSTANCE_INFOS")
activity_id = activity_service.create(f"Update instance: {instance_name}", "UPDATE_INSTANCE")

# logger service for creating custom logger
logger_serv = DBLoggerService(activity_id, f"[UPDATE INFOS] {instanceName}")
logger_serv = DBLoggerService(activity_id, f"[UPDATE INFOS] {instance_name}")
logger = logger_serv.getLogger()
fh = FileHandler()
try:
fh.updateInstanceJsonInfo(instanceName, payload)
fh.updateInstanceJsonInfo(instance_name, payload)
except Exception as ex:
logger.error(f"Updating instance.json file of instance {instanceName} failed. Exception: {ex}.")
logger.error(f"Updating instance.json file of instance {instance_name} failed. Exception: {ex}.")
activity_service.update(activity_id, "ERROR", "FAILURE")
else:
logger.info(f"Successfully updated instance.json of instance {instanceName}.")
logger.info(f"Successfully updated instance.json of instance {instance_name}.")
activity_service.update(activity_id, "FINISHED", "SUCCESS")

@app_celerey.task(bind=True, name='instance.installInstance')
def installInstance (self, instanceDescr):
Expand Down
5 changes: 2 additions & 3 deletions sys-commander/backend/app/services/log_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ def __init__(self):


def select(self, activity_id, limit=None):
pass

## TODO:
# SELECT "id" FROM "Activities" WHERE "name" LIKE '%instance_name%' AND "name" NOT LIKE '%Delete%' ORDER BY "id" DESC LIMIT 1
# --> do we allow duplicate InstanceNames when original instance is deleted already?
#
# SELECT * FROM "Logs" WHERE "activity_id" = 'activity_id'

logs = db.session.query(Log).filter(activity_id == activity_id)
return logs
4 changes: 2 additions & 2 deletions sys-commander/backend/uwsgi.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
http = :5000
module=wsgi
master = true
uid = 1
gid = 1
# uid = 1
# gid = 1
die-on-term = true
processes = 4
threads = 2
Expand Down

0 comments on commit 2743228

Please # to comment.