Initial YakPanel commit
This commit is contained in:
473
class/projectModel/bt_docker/dk_container.py
Normal file
473
class/projectModel/bt_docker/dk_container.py
Normal file
@@ -0,0 +1,473 @@
|
||||
#coding: utf-8
|
||||
#-------------------------------------------------------------------
|
||||
# YakPanel
|
||||
#-------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 YakPanel(www.yakpanel.com) All rights reserved.
|
||||
#-------------------------------------------------------------------
|
||||
# Author: zouhw <zhw@yakpanel.com>
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
#------------------------------
|
||||
# Docker模型
|
||||
#------------------------------
|
||||
import public
|
||||
import docker.errors
|
||||
import projectModel.bt_docker.dk_public as dp
|
||||
class main:
|
||||
|
||||
def __init__(self):
|
||||
self.alter_table()
|
||||
|
||||
def alter_table(self):
|
||||
if not dp.sql('sqlite_master').where('type=? AND name=? AND sql LIKE ?',
|
||||
('table', 'container', '%sid%')).count():
|
||||
dp.sql('container').execute("alter TABLE container add container_name VARCHAR DEFAULT ''", ())
|
||||
|
||||
def docker_client(self,url):
|
||||
return dp.docker_client(url)
|
||||
|
||||
# 添加容器
|
||||
def run(self,args):
|
||||
"""
|
||||
:param name:容器名
|
||||
:param image: 镜像
|
||||
:param publish_all_ports 暴露所有端口 1/0
|
||||
:param ports 暴露某些端口 {'1111/tcp': ('127.0.0.1', 1111)}
|
||||
:param command 命令
|
||||
:param entrypoint 配置容器启动后执行的命令
|
||||
:param environment 环境变量 xxx=xxx 一行一条
|
||||
:param auto_remove 当容器进程退出时,在守护进程端启用自动移除容器。 0/1
|
||||
|
||||
:param args:
|
||||
:return:
|
||||
"""
|
||||
if not hasattr(args,'ports'):
|
||||
args.ports = False
|
||||
if not hasattr(args,'volumes'):
|
||||
args.volumes = False
|
||||
#检测端口是否已经在使用
|
||||
if args.ports:
|
||||
for i in args.ports:
|
||||
if dp.check_socket(args.ports[i]):
|
||||
return public.returnMsg(False,"The server port [{}] has been used, please replace it with another port!".format(args.ports[i]))
|
||||
if not args.image:
|
||||
return public.returnMsg(False, "If there is no image selected, please go to the image tab to pull the image you need!")
|
||||
if args.restart_policy['Name'] == "always":
|
||||
args.restart_policy = {"Name":"always"}
|
||||
# return args.restart_policy
|
||||
# if
|
||||
args.cpu_quota = float(args.cpuset_cpus) * 100000
|
||||
# if not args.volumes:
|
||||
# args.volumes = {"/sys/fs/cgroup":{"bind":"/sys/fs/cgroup","mode":"rw"}}
|
||||
# else:
|
||||
# if not "/sys/fs/cgroup" in args.volumes:
|
||||
# args.volumes['/sys/fs/cgroup'] = {"bind":"/sys/fs/cgroup","mode":"rw"}
|
||||
try:
|
||||
if not args.name:
|
||||
args.name = "{}-{}".format(args.image,public.GetRandomString(8))
|
||||
if int(args.cpu_quota) / 100000 > dp.get_cpu_count():
|
||||
return public.returnMsg(False,"The CPU quota has exceeded the number of cores available!")
|
||||
mem_limit_byte = dp.byte_conversion(args.mem_limit)
|
||||
if mem_limit_byte > dp.get_mem_info():
|
||||
return public.returnMsg(False, "The memory quota has exceeded the available number!")
|
||||
res = self.docker_client(args.url).containers.run(
|
||||
name=args.name,
|
||||
image=args.image,
|
||||
detach=True,
|
||||
publish_all_ports=True if args.publish_all_ports == "1" else False,
|
||||
ports=args.ports if args.ports else None,
|
||||
command=args.command,
|
||||
auto_remove=True if str(args.auto_remove) == "1" else False,
|
||||
environment=dp.set_kv(args.environment), #"HOME=/value\nHOME11=value1"
|
||||
volumes=args.volumes, #一个字典对象 {'服务器路径/home/user1/': {'bind': '容器路径/mnt/vol2', 'mode': 'rw'},'/var/www': {'bind': '/mnt/vol1', 'mode': 'ro'}}
|
||||
# cpuset_cpus=args.cpuset_cpus ,#指定容器使用的cpu个数
|
||||
cpu_quota=int(args.cpu_quota),
|
||||
mem_limit=args.mem_limit, #b,k,m,g
|
||||
restart_policy=args.restart_policy,
|
||||
labels=dp.set_kv(args.labels), #"key=value\nkey1=value1"
|
||||
tty=True,
|
||||
stdin_open=True,
|
||||
privileged=True
|
||||
|
||||
)
|
||||
if res:
|
||||
pdata = {
|
||||
"cpu_limit": str(args.cpu_quota),
|
||||
"container_name": args.name
|
||||
}
|
||||
dp.sql('container').insert(pdata)
|
||||
public.set_module_logs('docker', 'run_container', 1)
|
||||
dp.write_log("Create container [{}] successful!".format(args.name))
|
||||
return public.returnMsg(True,"The container was created successfully!")
|
||||
return public.returnMsg(False, 'Create failed!')
|
||||
except docker.errors.APIError as e:
|
||||
if "container to be able to reuse that name." in str(e):
|
||||
return public.returnMsg(False, "The container name already exists!")
|
||||
if "Invalid container name" in str(e):
|
||||
return public.returnMsg(False, "The container name is illegal, please do not use Chinese container name!")
|
||||
if "bind: address already in use" in str(e):
|
||||
port = ""
|
||||
for i in args.ports:
|
||||
if ":{}:".format(args.ports[i]) in str(e):
|
||||
port = args.ports[i]
|
||||
args.id = args.name
|
||||
self.del_container(args)
|
||||
return public.returnMsg(False, "Server port {} is in use! Please change other ports".format(port))
|
||||
return public.returnMsg(False, 'Create failed! {}'.format(public.get_error_info()))
|
||||
|
||||
# 保存为镜像
|
||||
def commit(self,args):
|
||||
"""
|
||||
:param repository 推送到的仓库
|
||||
:param tag 镜像标签 jose:v1
|
||||
:param message 提交的信息
|
||||
:param author 镜像作者
|
||||
:param changes
|
||||
:param conf dict
|
||||
:param path 导出路径
|
||||
:param name 导出文件名
|
||||
:param args:
|
||||
:return:
|
||||
"""
|
||||
if not hasattr(args,'conf') or not args.conf:
|
||||
args.conf = None
|
||||
if args.repository == "docker.io":
|
||||
args.repository = ""
|
||||
container = self.docker_client(args.url).containers.get(args.id)
|
||||
container.commit(
|
||||
repository=args.repository if args.repository else None,
|
||||
tag=args.tag if args.tag else None,
|
||||
message=args.message if args.message else None,
|
||||
author=args.author if args.author else None,
|
||||
# changes=args.changes if args.changes else None,
|
||||
conf=args.conf
|
||||
)
|
||||
if hasattr(args,"path") and args.path:
|
||||
args.id = "{}:{}".format(args.name,args.tag)
|
||||
import projectModel.bt_docker.dk_image as dk
|
||||
return dk.main().save(args)
|
||||
dp.write_log("Submitting container [{}] as image [{}] succeeded!".format(container.attrs['Name'],args.tag))
|
||||
return public.returnMsg(True,"提交成功!")
|
||||
|
||||
# 容器执行命令
|
||||
def docker_shell(self, args):
|
||||
"""
|
||||
:param container_id
|
||||
:param args:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
self.docker_client(args.url).containers.get(args.container_id)
|
||||
cmd = 'docker container exec -it {} /bin/bash'.format(args.container_id)
|
||||
return public.returnMsg(True, cmd)
|
||||
except docker.errors.APIError as ex:
|
||||
return public.returnMsg(False, 'Failed to get container')
|
||||
|
||||
# 导出容器为tar 没有导入方法,目前弃用
|
||||
def export(self,args):
|
||||
"""
|
||||
:param path 保存路径
|
||||
:param name 包名
|
||||
:param args:
|
||||
:return:
|
||||
"""
|
||||
from os import path as ospath
|
||||
from os import makedirs as makedirs
|
||||
try:
|
||||
if "tar" in args.name:
|
||||
file_name = '{}/{}'.format(args.path,args.name)
|
||||
else:
|
||||
file_name = '{}/{}.tar'.format(args.path, args.name)
|
||||
if not ospath.exists(args.path):
|
||||
makedirs(args.path)
|
||||
public.writeFile(file_name,'')
|
||||
f = open(file_name, 'wb')
|
||||
container = self.docker_client(args.url).containers.get(args.id)
|
||||
data = container.export()
|
||||
for i in data:
|
||||
f.write(i)
|
||||
f.close()
|
||||
return public.returnMsg(True, "Successfully exported to: {}".format(file_name))
|
||||
except:
|
||||
return public.returnMsg(False, 'Operation failed:' + str(public.get_error_info()))
|
||||
|
||||
# 删除容器
|
||||
def del_container(self,args):
|
||||
"""
|
||||
:return:
|
||||
"""
|
||||
import projectModel.bt_docker.dk_public as dp
|
||||
container = self.docker_client(args.url).containers.get(args.id)
|
||||
container.remove(force=True)
|
||||
dp.sql("cpu_stats").where("container_id=?", (args.id,)).delete()
|
||||
dp.sql("io_stats").where("container_id=?", (args.id,)).delete()
|
||||
dp.sql("mem_stats").where("container_id=?", (args.id,)).delete()
|
||||
dp.sql("net_stats").where("container_id=?", (args.id,)).delete()
|
||||
dp.sql("container").where("container_nam=?", (container.attrs['Name'])).delete()
|
||||
dp.write_log("Delete container [{}] succeeded!".format(container.attrs['Name']))
|
||||
return public.returnMsg(True,"Successfully deleted!")
|
||||
|
||||
# 设置容器状态
|
||||
def set_container_status(self,args):
|
||||
import time
|
||||
container = self.docker_client(args.url).containers.get(args.id)
|
||||
if args.act == "start":
|
||||
container.start()
|
||||
elif args.act == "stop":
|
||||
container.stop()
|
||||
elif args.act == "pause":
|
||||
container.pause()
|
||||
elif args.act == "unpause":
|
||||
container.unpause()
|
||||
elif args.act == "reload":
|
||||
container.reload()
|
||||
else:
|
||||
container.restart()
|
||||
time.sleep(1)
|
||||
tmp = self.docker_client(args.url).containers.get(args.id)
|
||||
return {"name":container.attrs['Name'].replace('/',''),"status":tmp.attrs['State']['Status']} #返回设置后的状态
|
||||
|
||||
|
||||
# 停止容器
|
||||
def stop(self,args):
|
||||
"""
|
||||
:param url
|
||||
:param id
|
||||
:param args:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
args.act = "stop"
|
||||
data = self.set_container_status(args)
|
||||
if data['status'] != "exited":
|
||||
return public.returnMsg(False, "Stop failing!")
|
||||
dp.write_log("Stop container [{}] succeeded!".format(data['name']))
|
||||
return public.returnMsg(True, "Stop success!")
|
||||
except docker.errors.APIError as e:
|
||||
if "is already paused" in str(e):
|
||||
return public.returnMsg(False,"The container has been suspended!")
|
||||
if "No such container" in str(e):
|
||||
return public.returnMsg(True, "The container has been stopped and deleted because the container has the option to automatically delete after stopping!")
|
||||
return public.returnMsg(False,"Stop failing!{}".format(e))
|
||||
|
||||
def start(self,args):
|
||||
"""
|
||||
:param url
|
||||
:param id
|
||||
:param args:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
args.act = "start"
|
||||
data = self.set_container_status(args)
|
||||
if data['status'] != "running":
|
||||
return public.returnMsg(False, "Startup failed!")
|
||||
dp.write_log("Start the container [{}] successfully!".format(data['name']))
|
||||
return public.returnMsg(True, "Started successfully!")
|
||||
except docker.errors.APIError as e:
|
||||
if "cannot start a paused container, try unpause instead" in str(e):
|
||||
return self.unpause(args)
|
||||
|
||||
def pause(self,args):
|
||||
"""
|
||||
Pauses all processes within this container.
|
||||
:param url
|
||||
:param id
|
||||
:param args:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
args.act = "pause"
|
||||
data = self.set_container_status(args)
|
||||
if data['status'] != "paused":
|
||||
return public.returnMsg(False, "Container pause failed!")
|
||||
dp.write_log("Suspended container [{}] succeeded!".format(data['name']))
|
||||
return public.returnMsg(True, "Container paused successfully!")
|
||||
except docker.errors.APIError as e:
|
||||
if "is already paused" in str(e):
|
||||
return public.returnMsg(False,"The container has been suspended!")
|
||||
if "is not running" in str(e):
|
||||
return public.returnMsg(False, "The container is not started and cannot be paused!")
|
||||
if "is not paused" in str(e):
|
||||
return public.returnMsg(False, "The container has not been suspended!")
|
||||
return str(e)
|
||||
|
||||
def unpause(self,args):
|
||||
"""
|
||||
unPauses all processes within this container.
|
||||
:param url
|
||||
:param id
|
||||
:param args:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
args.act = "unpause"
|
||||
data = self.set_container_status(args)
|
||||
if data['status'] != "running":
|
||||
return public.returnMsg(False, "Startup failed!")
|
||||
dp.write_log("Unpausing the container [{}] succeeded!".format(data['name']))
|
||||
return public.returnMsg(True, "Container unpause succeeded!")
|
||||
except docker.errors.APIError as e:
|
||||
if "is already paused" in str(e):
|
||||
return public.returnMsg(False,"The container has been suspended!")
|
||||
if "is not running" in str(e):
|
||||
return public.returnMsg(False, "The container is not started and cannot be paused!")
|
||||
if "is not paused" in str(e):
|
||||
return public.returnMsg(False, "The container has not been suspended!")
|
||||
return str(e)
|
||||
|
||||
def reload(self,args):
|
||||
"""
|
||||
Load this object from the server again and update attrs with the new data.
|
||||
:param url
|
||||
:param id
|
||||
:param args:
|
||||
:return:
|
||||
"""
|
||||
args.act = "reload"
|
||||
data = self.set_container_status(args)
|
||||
if data['status'] != "running":
|
||||
return public.returnMsg(False, "Startup failed!")
|
||||
dp.write_log("Reloading container [{}] succeeded!".format(data['name']))
|
||||
return public.returnMsg(True, "Container reload succeeded!")
|
||||
|
||||
def restart(self,args):
|
||||
"""
|
||||
Restart this container. Similar to the docker restart command.
|
||||
:param url
|
||||
:param id
|
||||
:param args:
|
||||
:return:
|
||||
"""
|
||||
args.act = "restart"
|
||||
data = self.set_container_status(args)
|
||||
if data['status'] != "running":
|
||||
return public.returnMsg(False, "Startup failed!")
|
||||
dp.write_log("Restarting the container [{}] succeeded!".format(data['name']))
|
||||
return public.returnMsg(True, "The container restarted successfully!")
|
||||
|
||||
def get_container_ip(self,container_networks):
|
||||
data = list()
|
||||
for network in container_networks:
|
||||
data.append(container_networks[network]['IPAddress'])
|
||||
return data
|
||||
|
||||
def get_container_path(self,detail):
|
||||
import os
|
||||
if not "GraphDriver" in detail:
|
||||
return False
|
||||
if "Data" not in detail["GraphDriver"]:
|
||||
return False
|
||||
if "MergedDir" not in detail["GraphDriver"]["Data"]:
|
||||
return False
|
||||
path = detail["GraphDriver"]["Data"]["MergedDir"]
|
||||
if not os.path.exists(path):
|
||||
return ""
|
||||
return path
|
||||
|
||||
# 获取容器列表所需的外部数据
|
||||
def get_other_data_for_container_list(self,args):
|
||||
import projectModel.bt_docker.dk_image as di
|
||||
import projectModel.bt_docker.dk_volume as dv
|
||||
import projectModel.bt_docker.dk_compose as dc
|
||||
import projectModel.bt_docker.dk_setup as ds
|
||||
# 获取镜像列表
|
||||
images = di.main().image_list(args)
|
||||
if images['status']:
|
||||
images = images['msg']['images_list']
|
||||
else:
|
||||
images = list()
|
||||
# 获取卷列表
|
||||
volumes = dv.main().get_volume_list(args)
|
||||
if volumes['status']:
|
||||
volumes = volumes['msg']['volume']
|
||||
else:
|
||||
volumes = list()
|
||||
# 获取模板列表
|
||||
template = dc.main().template_list(args)
|
||||
if template['status']:
|
||||
template = template['msg']['template']
|
||||
else:
|
||||
template = list()
|
||||
online_cpus = dp.get_cpu_count()
|
||||
mem_total = dp.get_mem_info()
|
||||
docker_setup = ds.main()
|
||||
return {
|
||||
"images":images,
|
||||
"volumes":volumes,
|
||||
"template":template,
|
||||
"online_cpus":online_cpus,
|
||||
"mem_total":mem_total,
|
||||
"installed":docker_setup.check_docker_program(),
|
||||
"service_status":docker_setup.get_service_status()
|
||||
}
|
||||
|
||||
# 获取容器列表
|
||||
def get_list(self,args):
|
||||
"""
|
||||
:param url
|
||||
:return:
|
||||
"""
|
||||
# 判断docker是否安装
|
||||
import projectModel.bt_docker.dk_setup as ds
|
||||
data = self.get_other_data_for_container_list(args)
|
||||
if not ds.main().check_docker_program():
|
||||
data['container_list'] = list()
|
||||
return public.returnMsg(True,data)
|
||||
client = self.docker_client(args.url)
|
||||
if not client:
|
||||
|
||||
return public.returnMsg(True,data)
|
||||
containers = client.containers
|
||||
attr_list = self.get_container_attr(containers)
|
||||
# data = self.get_other_data_for_container_list(args)
|
||||
container_detail = list()
|
||||
for attr in attr_list:
|
||||
cpu_usage = dp.sql("cpu_stats").where("container_id=?",(attr["Id"],)).select()
|
||||
if cpu_usage and isinstance(cpu_usage,list):
|
||||
cpu_usage = cpu_usage[-1]['cpu_usage']
|
||||
else:
|
||||
cpu_usage = "0.0"
|
||||
tmp = {
|
||||
"id": attr["Id"],
|
||||
"name": attr['Name'].replace("/",""),
|
||||
"status": attr["State"]["Status"],
|
||||
"image": attr["Config"]["Image"],
|
||||
"time": attr["Created"],
|
||||
"merged": self.get_container_path(attr),
|
||||
"ip": self.get_container_ip(attr["NetworkSettings"]['Networks']),
|
||||
"ports": attr["NetworkSettings"]["Ports"],
|
||||
"detail": attr,
|
||||
"cpu_usage":cpu_usage if attr["State"]["Status"] == "running" else ""
|
||||
}
|
||||
container_detail.append(tmp)
|
||||
data['container_list'] = container_detail
|
||||
return public.returnMsg(True,data)
|
||||
|
||||
# 获取容器的attr
|
||||
def get_container_attr(self,containers):
|
||||
c_list = containers.list(all=True)
|
||||
return [container_info.attrs for container_info in c_list]
|
||||
|
||||
# 获取容器日志
|
||||
def get_logs(self,args):
|
||||
"""
|
||||
:param url
|
||||
:param id
|
||||
:param args:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
container = self.docker_client(args.url).containers.get(args.id)
|
||||
res = container.logs().decode()
|
||||
return public.returnMsg(True,res)
|
||||
except docker.errors.APIError as e:
|
||||
if "configured logging driver does not support reading" in str(e):
|
||||
return public.returnMsg(False,"The container has no log files!")
|
||||
|
||||
|
||||
|
||||
# 登录容器
|
||||
|
||||
|
||||
# 获取容器配置文件
|
||||
Reference in New Issue
Block a user