#!/usr/bin/env python # -*- coding: utf-8 -*- """ dever-docker tools name:dever.py author:rabin """ import time import datetime import os import sys import getopt import ConfigParser import commands import re import subprocess import urlparse PATH = '' class Dever(object): config = {} server = [] core = {} base = {} path = '' method = '' conf = '' name = '' param = '' def __init__(self): self.initObject() self.initCore() self.initArgv() state = self.initConfig() if state == 1: self.run() def initObject(self): self.File = File() self.Container = Container() self.Image = Image() self.Alias = Alias() global PATH if PATH == '': PATH = self.File.path() self.path = PATH def initCore(self): core = self.path + 'conf/core.conf' if self.File.exists(core): config = ConfigParser.ConfigParser() config.read(core) for item in config.sections(): self.core[item] = self.readConfig(config, item) else: print core + ' is not exists' sys.exit() def initConfig(self): filename = self.path + 'conf/dever/' + self.conf + '.conf' if self.File.exists(filename): config = ConfigParser.ConfigParser() config.read(filename) for item in config.sections(): if item == 'base': self.base = self.readConfig(config, item) else: self.server.append(item) self.config[item] = self.readConfig(config, item) self.base['path'] = replace('{base}', self.path, self.base['path']) return 1 else: print filename + ' is not exists' return 0 def readConfig(self, config, type): value = config.options(type) result = {} for item in value: result[item] = config.get(type, item) return result def initArgv(self): slen = len(sys.argv) if slen >= 2: self.method = sys.argv[1] if slen >= 3: self.conf = sys.argv[2] if slen >= 4: self.name = sys.argv[3] if slen >= 5: self.param = sys.argv[4] self.options() self.handle() if self.method == '': print 'method error' sys.exit() def options(self): try: options, args = getopt.getopt(sys.argv[1:], "hm:n:a:p:", ['help', "method=", "name=", "action=", "param="]) for name, value in options: if name in ('-h', '--help'): self.usage() elif name in ('-m', '--method'): self.method = value elif name in ('-c', '--conf'): self.conf = value elif name in ('-n', '--name'): self.name = value elif name in ('-p', '--param'): self.param = value except getopt.GetoptError: self.usage() def usage(self): print ' Usage: \n' + \ ' dever [-m] [-n] [-a] [-p] \n' + \ ' dever \n\n' + \ ' -h or --help Help\n' + \ ' -m or --method Method name\n' + \ ' -c or --conf Configuration file name\n' + \ ' -n or --name The index name in the configuration file\n' + \ ' -p or --param Execution parameters are generally judged by actions\n\n' + \ ' list:\n' + \ ' init Initializes and updates the Dever code\n' + \ ' up Update the Dever code\n' + \ ' show Displays the docker container that is currently started\n' + \ ' showi Displays the current docker mirror\n' + \ ' rm Remove an exception or docker container that has not been started\n' + \ ' rmi Delete expired docker images\n\n' + \ ' +list:\n' + \ ' run Run the container\n' + \ ' stop Stop the container\n' + \ ' create Create the container\n' + \ ' call Run the container, run only once\n' + \ ' up Run the container and update the docker mirror\n' + \ ' rm Remove the running docker container\n' + \ ' save Save or backup the running docker container\n' + \ ' load Restores and re run the docker container that is saved or backed up\n\n' + \ ' Example:\n' + \ ' dever -m run -c web -n php\n' + \ ' dever run web php\n' + \ ' dever -m call -c tool -n apidoc -p input=demo^out=output\n' + \ ' dever call tool apidoc input=demo^out=output\n\n' + \ '[Dever is a small docker choreography tool]\n' sys.exit() def handle(self): method = ['build', 'push', 'shell'] if self.method not in method and self.conf != '': return if self.method == 'init': self.update() popen('chmod -R +x ' + self.path + 'shell/') check('curl') check('docker') print 'dever init success' elif self.method == 'up': self.update() print 'dever update success' elif self.method == 'commit': print shell('git.push ' + self.path) elif self.method == 'push': self.push(self.conf) elif self.method == 'package': self.package() elif self.method == 'path': print self.path elif self.method == 'show': self.Container.show() elif self.method == 'showi': self.Image.show() elif self.method == 'rmi': self.Image.delete() print 'docker rm none container:yes' elif self.method == 'rm': self.Container.delete() print 'docker rm image:yes' elif self.method == 'drop': self.Container.drop() self.Image.drop() print 'docker drop image:yes' elif self.method == 'build': self.Image.build(self.core['base'], self.path, self.conf) self.Container.delete() self.Image.delete() print 'docker build '+self.conf+':yes' elif self.method == 'shell': print shell(self.conf) else: return sys.exit() def package(self): dockerHub = self.core['base']['dockerhub'] + '/' dockerMe = self.core['base']['dockerme'] + '/' result = {} for key in self.core['package']: print 'package : [' + dockerHub + key + '] or [' + dockerMe + self.core['package'][key] + ']' result[dockerHub + key] = dockerMe + self.core['package'][key] return result def push(self, name=''): package = self.package() if name != '': if name in package: self.Image.push(package[name], name) else: print 'error ' + name sys.exit() for key in package: self.Image.push(package[key], key) def update(self): print 'loading...' if '/usr/bin/' not in self.path: shell('git.pull ' + self.path) self.code() def code(self): content = self.File.read(self.path, 'dever.py') content = content.replace('PATH = \'\'', 'PATH="'+self.path+'"') self.File.write('/usr/bin/dever', content) content = '#!/usr/bin/env sh \nset -e\ncd ' + self.path self.File.write('/usr/bin/godever', content) def run(self): self.Container.network(self.base) self.rely(self.base, self.method) one = False if self.name in self.config: one = True if one: self.runServer(self.config[self.name], self.name, self.method) else: for item in self.server: if self.checkServer(self.name, item) == True: self.runServer(self.config[item], item, self.method) def checkServer(self, name, item): if '#' in item: return False elif name == '': return True elif name+'-' in item: return True else: return False def runServer(self, config, item, action='run'): self.rely(config, action) if 'num' not in config: config['num'] = 1 num = int(config['num']) i = 1; while (i <= num): self.runServerOne(config, item, i, action) i = i + 1 def runServerOne(self, config, item, i, action=''): name = self.getServerName(item, i) command = '' daemon = '-d' restart = '--restart=always' method = 'run' if 'daemon' in config: daemon = config['daemon'] if daemon == 'false': daemon = '' restart = '' if 'image' not in config: config['image'] = '' if 'restart' in config: restart = '' run = True; if action == 'create': method = action elif action == 'call': runCommand = self.getServerParam(config, 'call', '', name) command = self.getServerParam(config, 'param', '', name) restart = '--entrypoint' + runCommand daemon = '--rm' elif action == 'stop': self.Container.stop(name) self.slave(config, item, action) run = False elif action == 'restart': self.Container.restart(name) self.slave(config, item, action) run = False elif action == 'logs': self.Container.logs(name) run = False elif action == 'show': self.Container.show(name) run = False elif action == 'inspect': self.Container.inspect(name) run = False elif action in ['rm', 'reset']: self.Container.delete(name) self.Alias.delete(self.path, config, name) self.slave(config, item, action) run = False elif action in ['up', 'reset']: self.update() self.Image.install(self.config, self.base['library'], config['image']) elif action in ['save', 'load']: path = self.path + 'data/backup/' + name + '/' self.File.mkdir(path) backup = 'backup/' + name tar = path + name + '.tar' if action == 'save': self.Container.save(self.File, tar, name, backup) elif action == 'load': self.Container.load(tar, name) self.base['library'] = '' config['image'] = backup self.runServerOne(config, item, i) run = False; if run == False: return state = self.Container.check(name) if state == 0: self.hook('start', config, name, self.path) docker = ['-it', '--name='+name, '--hostname='+name, restart, daemon, '-v '+self.path+'data/share:/share -v /etc/hosts:/etc/hosts.main'] param = self.Container.config() for key in param: if param[key] != '': value = self.getServerParam(config, key, param[key], name) if value != '': docker.append(value) docker.append(self.base['library'] + config['image']) if command == '' and 'command' in param: value = self.getServerParam(config, 'command', param['command'], name) command = value if command != '': docker.append(command) command = ' '.join(docker) bg = False print 'setuping ' + name + ', please wait...' else: #if daemon == '': #self.runServerOne(config, item, i, 'rm') #self.runServerOne(config, item, i) #return method = 'restart'; command = name bg = True print 'reloading ' + name + ', please wait...' if action == 'test': print command else: self.Container.run(method, method + ' ' + command, bg) if state == 0: self.Alias.add(self.path, config, name, 'docker ' + method + ' ' + command, self.File, action) self.hook('end', config, name, self.path) self.slave(config, item, action) def rely(self, config, action): if 'rely' in config: if ',' in config['rely']: rely = config['rely'].split(','); for i in rely: popen('dever ' + action + ' ' + i, True) else: popen('dever ' + action + ' ' + config['rely'], True) def hook(self, type, config, name, path): key = 'hook.'+type if key in config: shell('hook.' + config[key] + ' ' + name + ' ' + path, bg=True) def slave(self, config, name, action): if 'slave' in config: i = 1 num = int(config['slave']) key = ['slave', 'command', 'alias', 'port', 'hook.start', 'hook.end'] for k in key: if k in config: del config[k] while (i <= num): self.runServerOne(config, name + '-slave', i, action) i = i + 1 def getServerName(self, name, i): name = self.conf + '-' + name if i > 1: self.base['i'] = '' + str(i) i = i - 1; self.base['num'] = '' + str(i) return name + self.base['num']; self.base['num'] = '' self.base['i'] = '1' return name; def getServerParam(self, config, item, prefix, name): result = '' concat = ' ' if item in config: if '#' not in item: result = config[item] if '[' in config[item]: search = re.search(r'\[(.*?)\]', result, re.M|re.I) temp = search.group(1) if temp: result = replace('['+str(temp)+']', self.getServerName(temp, 1), result) result = replace('{num}', self.base['num'], result) result = replace('{i}', self.base['i'], result) result = replace('{path}', self.base['path'], result) result = replace('{name}', name, result) result = self.getServerParse(result) result = replace(',', ' ' + prefix + ' ', result) if item == 'hostname': name = result elif item == 'network' and 'network' in self.base: result = self.base['network'] if result != '': if '=' in prefix: concat = '' result = prefix + concat + result return result def getServerParse(self, result): if '{$' in result: param = {} if self.param != '': if 'http://' not in self.param: self.param = 'http://shemic.com/?' + self.param; if '^' in self.param: self.param = replace('^', '&', self.param) parse = urlparse.urlparse(self.param) param = urlparse.parse_qs(parse.query,True) search = re.compile(r'\{\$(.*?)\}') search = search.findall(result) for key in search: value = '' index = key if ':' in key: arr = key.split(':'); index = arr[0] value = arr[1] if index in param: value = param[index][0] if value == '': print 'please set param value:' + index sys.exit() else: result = replace('{$'+key+'}', value, result) return result class Container(object): def run(self, method, command, bg): command = 'container.exec ' + command shell(command, True, bg) return command def show(self, name=''): print shell('container.show ' + name) def config(self): config = { 'port' : '-p' ,'volumes' : '-v' ,'environment' : '-e' ,'link' : '--link' ,'volumes_from' : '--volumes-from' ,'command' : '' ,'entrypoint' : '--entrypoint' ,'network' : '--net=' ,'host' : '--add-host' ,'root' : '--privileged=' ,'memory' : '--memory=' ,'expose' : '--expose', } return config def drop(self): shell('container.drop', bg=True) def stop(self, name): shell('container.stop ' + name, bg=True) def logs(self, name): shell('container.logs ' + name, True) def inspect(self, name): shell('container.inspect ' + name, True) def restart(self, name): shell('container.restart ' + name, bg=True) def delete(self, name=''): if name != '': print 'rm ' + name + ', please wait...' if self.check(name) == 1: shell('container.rm ' + name, bg=True) else: shell('container.rm', bg=True) def check(self, name): result = int(popen('docker ps -a | grep '+name+' | wc -l')) if result != 0: return 1 else: return 0 def network(self, config): if 'network' in config: result = int(popen('docker network ls | grep ' + config['network'] + ' | wc -l')) if result == 0: shell('container.network ' + config['network'], True) def save(self, File, tar, name, backup): if File.exists(tar) == True: now = time.strftime('%Y%m%d%H%M%S',time.localtime(time.time())) old = replace('.tar', '.' + now + '.tar', tar) File.rename(tar, old) popen('docker commit -p ' + name + ' ' + backup) popen('docker save ' + backup + ' > ' + tar, True) def load(self, tar, name): popen('docker load < ' + tar, True) self.delete(name) class Image(object): def push(self, me, hub): if self.check(me) == 1: command = 'image.push ' + me + ' ' + hub print command shell(command, bg=True) def show(self): print shell('image.show') def drop(self): shell('image.drop', bg=True) def delete(self): shell('image.rm', bg=True) def build(self, core, path, name): file = path + 'conf/docker/' + name + '/' name = core['dockerme'] + '/' + name shell('image.build ' + name + ' ' + file, True) def check(self, name): result = int(popen('docker images | grep '+name+' | wc -l')) if result != 0: return 1 else: return 0 def install(self, config, library, key): pull = 'docker pull'; for item in config: if config[item]['image'] == key: command = pull + ' ' + library + config[item]['image'] popen(command, True) print 'finished' class Alias(object): def delete(self, path, config, name): return result = self.get(path, config, name) for key in result: action = self.action(name, key) popen('rm -rf ' + action[1], bg=True) popen('rm -rf ' + action[2], bg=True) def add(self, path, config, name, content, File, type): result = self.get(path, config, name) for key in result: action = self.action(name, key) content = '#!/usr/bin/env sh \nset -e\n' if type != 'call': content = content + self.define(name) + \ 'else\n' + \ 'docker exec -it ' + name + ' ' + action[0] + ' $@\n' + \ 'fi' else: content = content + ' $@' File.write(action[1], content) popen('ln -sf ' + action[1] + ' ' + action[2]) def define(self, name): conf = ['logs', 'inspect', 'restart', 'stop', 'rm', 'run', 'show'] result = '' container = name arr = name.split('-'); prefix = arr[0] del arr[0] name = '-'.join(arr) name = prefix + ' ' + name for key in conf: control = 'elif' shell = 'dever ' + key + ' ' + name + '\n' if key == 'logs': control = 'if' result = result + control + ' [ "$1" = "'+key+'" ];then\n' + shell return result def get(self, path, config, name): self.path = path + 'data/alias/' result = [] default = 'sh->' + name if 'alias' in config: config['alias'] = config['alias'] + ',' + default if ',' in config['alias']: result = config['alias'].split(','); else: result = [config['alias']] else: result = [default] return result def action(self, name, key): file = key if '->' in key: temp = key.split('->') key = temp[0] file = temp[1] link = '/usr/bin/' + file file = self.path + file return [key, file, link] class File(object): def write(self, file, content): handle = open(file, 'w') handle.write(content) handle.close() popen('chmod +x ' + file) def read(self, path, name): handle = open(path + name, 'r') content = handle.read() handle.close() return content def path(self): return os.path.split(os.path.realpath(__file__))[0] + '/' def exists(self, name): return os.path.exists(name) def rename(self, old, new): return os.rename(old, new) def remove(self, file): return os.remove(file) def mkdir(self, path): if self.exists(path) == False: os.mkdir(path) def shell(command, sub=False, bg=False): global PATH shell = PATH + 'shell/' + command.replace('.', '/', 1) return popen(shell, sub, bg) def popen(command, sub=False, bg=False): string = command if bg == True: command = command + ' 1>/dev/null 2>&1 &' if sub == False: process = os.popen(command) output = process.read() process.close() return output else: popen = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) output = '' print string while True: output = popen.stdout.readline() print output if popen.poll() is not None: break return output def install(soft): print 'install ' + soft + '...' if soft == 'docker': shell('install.docker', True) else: shell('install.package ' + soft, True) def isset(v): try : type(eval(v)) except : return 0 else : return 1 def check(soft): result = int(popen('which '+soft+' | wc -l')) if result != 0: return 1 else: install(soft) return 0 def replace(old, new, string): if old in string: string = string.replace(old, new) return string Dever()