github.com/spg/deis@v1.7.3/controller/scheduler/swarm.py (about) 1 import re 2 import time 3 4 from django.conf import settings 5 from docker import Client 6 7 from .states import JobState 8 9 10 MATCH = re.compile( 11 r'(?P<app>[a-z0-9-]+)_?(?P<version>v[0-9]+)?\.?(?P<c_type>[a-z-_]+)?.(?P<c_num>[0-9]+)') 12 13 14 class SwarmClient(object): 15 16 def __init__(self, target, auth, options, pkey): 17 self.target = settings.SWARM_HOST 18 # single global connection 19 self.registry = settings.REGISTRY_HOST + ':' + settings.REGISTRY_PORT 20 self.docker_cli = Client("tcp://{}:2395".format(self.target), 21 timeout=1200, version='1.17') 22 23 def create(self, name, image, command='', template=None, **kwargs): 24 """Create a container""" 25 cimage = self.registry + '/' + image 26 affinity = "affinity:container!=~/{}*/".format(re.split(r'_v\d.', name)[0]) 27 l = locals().copy() 28 l.update(re.match(MATCH, name).groupdict()) 29 mem = kwargs.get('memory', {}).get(l['c_type']) 30 if mem: 31 mem = mem.lower() 32 if mem[-2:-1].isalpha() and mem[-1].isalpha(): 33 mem = mem[:-1] 34 cpu = kwargs.get('cpu', {}).get(l['c_type']) 35 self.docker_cli.create_container(image=cimage, name=name, 36 command=command.encode('utf-8'), mem_limit=mem, 37 cpu_shares=cpu, 38 environment=[affinity], 39 host_config={'PublishAllPorts': True}) 40 41 def start(self, name): 42 """ 43 Start a container 44 """ 45 self.docker_cli.start(name) 46 47 def stop(self, name): 48 """ 49 Stop a container 50 """ 51 self.docker_cli.stop(name) 52 53 def destroy(self, name): 54 """ 55 Destroy a container 56 """ 57 self.stop(name) 58 self.docker_cli.remove_container(name) 59 60 def run(self, name, image, entrypoint, command): 61 """ 62 Run a one-off command 63 """ 64 cimage = self.registry + '/' + image 65 # use affinity for nodes that already have the image 66 affinity = "affinity:image==~{}".format(cimage) 67 self.docker_cli.create_container(image=cimage, name=name, 68 command=command.encode('utf-8'), 69 environment=[affinity], 70 entrypoint=[entrypoint]) 71 time.sleep(2) 72 self.start(name) 73 rc = 0 74 while (True): 75 if self._get_container_state(name) == JobState.created: 76 break 77 time.sleep(1) 78 try: 79 output = self.docker_cli.logs(name) 80 return rc, output 81 except: 82 rc = 1 83 return rc, output 84 85 def _get_container_state(self, name): 86 try: 87 if self.docker_cli.inspect_container(name)['State']['Running']: 88 return JobState.up 89 else: 90 return JobState.created 91 except: 92 return JobState.destroyed 93 94 def state(self, name): 95 try: 96 for _ in xrange(30): 97 return self._get_container_state(name) 98 time.sleep(1) 99 # FIXME (smothiki): should be able to send JobState.crashed 100 except KeyError: 101 return JobState.error 102 except RuntimeError: 103 return JobState.destroyed 104 105 def attach(self, name): 106 """ 107 Attach to a job's stdin, stdout and stderr 108 """ 109 raise NotImplementedError 110 111 def _get_hostname(self, application_name): 112 hostname = settings.UNIT_HOSTNAME 113 if hostname == 'default': 114 return '' 115 elif hostname == 'application': 116 # replace underscore with dots, since underscore is not valid in DNS hostnames 117 dns_name = application_name.replace('_', '.') 118 return dns_name 119 elif hostname == 'server': 120 raise NotImplementedError 121 else: 122 raise RuntimeError('Unsupported hostname: ' + hostname) 123 124 def _get_portbindings(self, image): 125 dictports = self.docker_cli.inspect_image(image)['ContainerConfig']['ExposedPorts'] 126 for port in dictports: 127 dictports[port] = None 128 return dictports 129 130 def _get_ports(self, image): 131 dictports = self.docker_cli.inspect_image(image)['ContainerConfig']['ExposedPorts'] 132 return [int(port.split('/')[0]) for port in dictports] 133 134 SchedulerClient = SwarmClient