cloudroid/app/dockerops.py~

337 lines
14 KiB
Python

# coding:utf-8
# Software License Agreement (BSD License)
#
# Copyright (c) 2016, micROS Team
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of micROS-drt nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import zipfile, os, shutil, json, time, logging
from docker import Client
import docker
from app import db, models
from app.models import *
from app.commonset import *
from datetime import datetime
from flask_login import current_user
from flask.templating import render_template
from flask.globals import session
from Tkinter import image_names
registry = '127.0.0.1:5000'
current_milli_time = lambda: int(round(time.time() * 1000))
class StreamLineBuildGenerator(object):
def __init__(self, json_data):
self.__dict__ = json.loads(json_data)
def downloadFileBuild(downloadFileName):
images = models.Image.query.filter_by(imagename = downloadFileName).first()
subscribed_topics = StringToList(images.subscribed_topics)
published_topics = StringToList(images.published_topics)
advertised_services = StringToList(images.advertised_services)
image_name = images.imagename
'''Generating client-side proxy'''
client_path='./client'
download_path = './app/download'
try:
if os.path.exists(client_path):
shutil.rmtree(client_path)
if os.path.exists(download_path):
shutil.rmtree(download_path)
os.mkdir(download_path)
unzip_cmd = 'unzip cloudproxy.zip -d ' + client_path
os.system(unzip_cmd)
client_url = url()
client_launch = render_template('client.launch', published_topics = subscribed_topics, subscribed_topics = published_topics,
advertised_services = advertised_services, url = client_url, image_id = image_name)
with open("./client/cloudproxy/launch/client.launch", "wb") as fh:
fh.write(client_launch)
zip_cmd = 'zip -r ./client/' + image_name +".zip " + "./client/cloudproxy/"
os.system(zip_cmd)
os.system("mv ./client/" + image_name + ".zip ./app/download/")
except Exception, e:
error_string = 'Unable to generating client proxy for image {}. \nReason: {}'.format(image_name, str(e))
logging.error(error_string)
return error_string
return None
def uploadFile(ros_file, manifest_file, comments):
upload_path='./upload'
logging.info('Uploading %s to path %s', ros_file.filename, upload_path)
'''The internal unique id of a uploaded file will be the mill time since 1970'''
image_name = str(current_milli_time())
save_filename = image_name + '.zip'
'''Save file to the upload directory. Replace filename with the internal unique id'''
try:
if not os.path.exists(upload_path):
os.mkdir(upload_path)
ros_file.save(os.path.join(upload_path, save_filename))
except Exception, e:
error_string = 'Unable to save file {} to path {}. \nReason: {}'.format(save_filename, upload_path, str(e))
logging.error(error_string)
return error_string
logging.info('Unzipping uploaded file %s', ros_file.filename)
'''Unzip the uploaded file'''
temp_path='./temp'
try:
if os.path.exists(temp_path):
shutil.rmtree(temp_path)
unzip_cmd = 'unzip ' + os.path.join(upload_path, save_filename) + ' -d ' + temp_path
os.system(unzip_cmd)
except Exception, e:
error_string = 'Unzip file {} to path {} failure. \nReason: {}'.format(save_filename, temp_path, str(e))
logging.error(error_string)
return error_string
manifest = json.load(manifest_file)
published_topics = manifest.get('published_topics')
subscribed_topics = manifest.get('subscribed_topics')
advertised_services = manifest.get('advertised_services')
advertised_actions = manifest.get('advertised_actions')
start_cmds = manifest.get('start_cmds')
mem_limit = manifest.get('mem_limit')
memswap_limit = manifest.get('memswap_limit')
cpushares = manifest.get('cpushares')
cpusetcpus = manifest.get('cpusetcpus')
container_limits = {'memory':int(mem_limit), 'memswap':int(memswap_limit), 'cpushares':int(cpushares), 'cpusetcpus':cpusetcpus}
if start_cmds == None :
error_string = 'Manifest file {} does not contain start_cmds. Generate docker image failed'.format(manifest_file.getFileName())
logging.error(error_string)
return error_string
rosentry = render_template('ros_entry.sh', start_cmds = start_cmds)
with open("./temp/ros_entry.sh", "wb") as fh:
fh.write(rosentry)
'''Building the docker image'''
logging.info('Generating docker image with tag %s', image_name)
try:
docker_client = docker.Client(base_url = 'unix://var/run/docker.sock')
registry_imagename = registry +'/'+ image_name
generator = docker_client.build(path=".", rm = True, tag = registry_imagename, container_limits = container_limits)
'''Check any error by inspecting the output of build()'''
for line in generator:
try:
stream_line = StreamLineBuildGenerator(line)
if hasattr(stream_line, "error"):
error_string = 'Unable to generating docker image with name {}. \nReason: {}'.format(registry_imagename, stream_line.error)
logging.error(error_string)
return error_string
except ValueError:
''' If we are not able to deserialize the received line as JSON object, just ignore it'''
continue
'''Push the images to private repository'''
response_push = docker_client.push(registry_imagename, stream = True)
'''Check any error by inspecting the output of push()'''
for line in response_push:
try:
json_line = json.loads(line)
if 'error' in json_line.keys():
error_string = 'Unable to push docker image with name {}. \nReason: {}'.format(registry_imagename, json_line['error'])
logging.error(error_string)
return error_string
except ValueError:
''' If we are not able to deserialize the received line as JSON object, just ignore it'''
continue
except Exception, e:
error_string = 'Unable to generating docker image with name {}. \nReason: {}'.format(image_name, str(e))
logging.error(error_string)
return error_string
shutil.rmtree(temp_path)
'''Insert a new record to the image table in the database'''
image_record = Image(imagename = image_name, uploadname = ros_file.filename, comments = comments, uploadtime = datetime.now(), uploaduser = current_user.email, published_topics = ListToString(published_topics), subscribed_topics = ListToString(subscribed_topics), advertised_services = ListToString(advertised_services), advertised_actions = ListToString(advertised_actions))
db.session.add(image_record)
db.session.commit()
logging.info('Uploading file %s to robotcloud successfully!', ros_file.filename)
return "None;"+image_name
def getServicePort(image_name):
logging.info('Starting a new services with image %s', image_name)
try:
client = docker.Client(base_url=DOCKER_PORT)
image = registry+'/'+image_name
com_cre_ser = 'sudo docker service create --replicas 1 --publish ' + ':9090 ' + image
service_ps = os.popen(com_cre_ser).read().split('\n')
service_id = service_ps[0]
time.sleep(10)
ser_ins = client.inspect_service(service_id)
port = ser_ins['Endpoint']['Ports'][0]['PublishedPort']
print port
ser_ps_com = "sudo docker service ps " + service_id
service_ps = os.popen(ser_ps_com).read().split('\n')
if service_ps[1].split()[4] == 'Running':
node = service_ps[1].split()[3]
else:
logging.error('Unable to create the service with image %s. \nReason: %s', service_ps[1].split()[6])
return
print node
get_node = models.Node.query.filter_by(nodename = node).first()
ip = get_node.nodeip
'''
if (node == 'micros-PowerEdge-R430'):
ip = '192.168.1.121'
elif (node == 'huben-PowerEdge-R430'):
ip = '192.168.1.101'
'''
except Exception, e:
logging.error('Unable to create the service with image %s. \nReason: %s', image_name, str(e))
return
logging.info('Store the service infomation to the db')
try:
imageinfo = models.Image.query.filter_by(imagename = image_name).first()
uploadn = imageinfo.uploadname
usern = imageinfo.uploaduser
service_record = Service(serviceid = service_id, createdtime = str(time.time()), imagename = image_name, uploadname = uploadn, username = usern, firstcreatetime = datetime.now())
db.session.add(service_record)
db.session.commit()
except Exception, e:
logging.error('Failed to store the service info to the db. \nReason: %s', str(e))
return
return ip+':'+str(port)+" "+service_id
def serviceinfo():
logging.info('The query of services info list')
try:
services = models.Service.query.all()
result = []
part_line = {'serviceid':'default','imagename':'default','filename':'default','user':'default','createtime':'default'}
for i in services:
part_line['serviceid'] = i.serviceid
part_line['imagename'] = i.imagename
part_line['filename'] = i.uploadname
part_line['user'] = i.username
part_line['createtime'] = i.firstcreatetime
result.append(part_line)
part_line = {}
return result
except Exception, e:
logging.error('Unable to list the services info. \nReason: %s', str(e))
return
def removeServices(serviceid):
logging.info('Remove the service %s', serviceid)
try:
docker_client = docker.Client(base_url=DOCKER_PORT)
docker_client.remove_service(serviceid)
remove_con = models.Service.query.all()
for i in remove_con:
if (i.serviceid == serviceid):
db.session.delete(i)
db.session.commit()
break
except Exception, e:
logging.error('Unable to remove the service %s. \nReason: %s', serviceid, str(e))
remove_con = models.Service.query.all()
for i in remove_con:
if (i.serviceid == serviceid):
db.session.delete(i)
db.session.commit()
break
return
def deleteImage(image_name):
logging.info('Delete the image %s', image_name)
try:
#docker_client = docker.Client(base_url=DOCKER_PORT)
#docker_client.remove_image(image = image_name, force = True)
image = models.Image.query.filter_by(imagename = image_name).first()
db.session.delete(image)
db.session.commit()
except Exception, e:
print str(e)
if (str(e).find('No such image:')):
image = models.Image.query.filter_by(imagename = image_name).first()
db.session.delete(image)
db.session.commit()
error_string = 'Unable to delete the image {}. \nReason: {}. Delete the record'.format(image_name, str(e))
else:
error_string = 'Unable to delete the image {}. \nReason: {}'.format(image_name, str(e))
logging.error(error_string)
return error_string
return None
def ListToString(lista):
if lista.__len__() == 0:
stringa = "None"
return stringa
else:
stringa = ""
for i in range(0,lista.__len__()):
if (lista[i].split("#")).__len__() <= 1:
lista[i] = str(lista[i]+"#")
stringa = stringa.join(lista)
return stringa
def StringToList(stringa):
if stringa == "None":
lista = []
return lista
else:
lista = stringa.split('#')
lista.pop()
return lista