#coding: utf-8 #------------------------------------------------------------------- # YakPanel #------------------------------------------------------------------- # Copyright (c) 2015-2099 YakPanel(www.yakpanel.com) All rights reserved. #------------------------------------------------------------------- # Author: zouhw #------------------------------------------------------------------- #------------------------------ # 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!") # 登录容器 # 获取容器配置文件