<s> from __future__ import print_function <EOL> import errno <EOL> import random <EOL> import re <EOL> import sys <EOL> import time <EOL> import docker <EOL> from . errors import BlockadeError , AlreadyInitializedError , BlockadeContainerConflictError , InsufficientPermissionsError <EOL> from . net import NetworkState , BlockadeNetwork <EOL> from . state import BlockadeState , BlockadeStateFactory <EOL> DEFAULT_KILL_TIMEOUT = <NUM_LIT:3> <EOL> class Blockade ( object ) : <EOL> def __init__ ( self , config , state_factory = None , network = None , <EOL> docker_client = None ) : <EOL> self . config = config <EOL> self . state_factory = state_factory or BlockadeStateFactory ( ) <EOL> self . network = network or BlockadeNetwork ( config ) <EOL> self . docker_client = docker_client or docker . Client ( ) <EOL> def create ( self , blockade_id = None , verbose = False , force = False ) : <EOL> container_state = { } <EOL> if blockade_id : <EOL> if re . match ( r"<STR_LIT>" , blockade_id ) is None : <EOL> raise BlockadeError ( "<STR_LIT>" <EOL> "<STR_LIT>" ) <EOL> else : <EOL> blockade_id = self . state_factory . get_blockade_id ( ) <EOL> num_containers = len ( self . config . sorted_containers ) <EOL> if self . state_factory . exists ( ) : <EOL> raise AlreadyInitializedError ( '<STR_LIT>' <EOL> '<STR_LIT>' ) <EOL> def vprint ( msg ) : <EOL> if verbose : <EOL> sys . stdout . write ( msg ) <EOL> sys . stdout . flush ( ) <EOL> for idx , container in enumerate ( self . config . sorted_containers ) : <EOL> name = container . name <EOL> vprint ( "<STR_LIT>" % ( idx + <NUM_LIT:1> , num_containers , name ) ) <EOL> if container . start_delay > <NUM_LIT:0> : <EOL> vprint ( '<STR_LIT>' % ( container . start_delay ) ) <EOL> time . sleep ( container . start_delay ) <EOL> container_id = self . _start_container ( blockade_id , container , force ) <EOL> device = self . _init_container ( container_id , name ) <EOL> container_state [ name ] = { '<STR_LIT>' : device , '<STR_LIT:id>' : container_id } <EOL> vprint ( '<STR_LIT:\r>' ) <EOL> state = self . state_factory . initialize ( container_state , blockade_id ) <EOL> container_descriptions = [ ] <EOL> for container in self . config . sorted_containers : <EOL> description = self . _get_container_description ( state , container . name ) <EOL> container_descriptions . append ( description ) <EOL> return container_descriptions <EOL> def _init_container ( self , container_id , container_name ) : <EOL> device = None <EOL> try : <EOL> device = self . network . get_container_device ( self . docker_client , container_id ) <EOL> except OSError as err : <EOL> if err . errno in ( errno . EACCES , errno . EPERM ) : <EOL> msg = "<STR_LIT>" % ( container_name , container_id ) <EOL> raise InsufficientPermissionsError ( msg ) <EOL> raise <EOL> return device <EOL> def _start_container ( self , blockade_id , container , force = False ) : <EOL> container_name = container . container_name or docker_container_name ( blockade_id , container . name ) <EOL> volumes = list ( container . volumes . values ( ) ) or None <EOL> links = dict ( ( docker_container_name ( blockade_id , link ) , alias ) <EOL> for link , alias in container . links . items ( ) ) <EOL> port_bindings = dict ( ( v , k ) for k , v in container . publish_ports . items ( ) ) <EOL> host_config = self . docker_client . create_host_config ( <EOL> binds = container . volumes , dns = container . dns , <EOL> port_bindings = port_bindings , links = links ) <EOL> def create_container ( ) : <EOL> response = self . docker_client . create_container ( <EOL> container . image , <EOL> command = container . command , <EOL> name = container_name , <EOL> ports = container . expose_ports , <EOL> volumes = volumes , <EOL> hostname = container . hostname , <EOL> environment = container . environment , <EOL> host_config = host_config , <EOL> labels = { "<STR_LIT>" : blockade_id } ) <EOL> return response [ '<STR_LIT>' ] <EOL> try : <EOL> container_id = create_container ( ) <EOL> except docker . errors . APIError as err : <EOL> if err . response . status_code == <NUM_LIT> and err . is_client_error ( ) : <EOL> if force and self . __try_remove_container ( container_name ) : <EOL> container_id = create_container ( ) <EOL> else : <EOL> raise BlockadeContainerConflictError ( err ) <EOL> else : <EOL> raise <EOL> self . docker_client . start ( container_id ) <EOL> return container_id <EOL> def __try_remove_container ( self , name ) : <EOL> try : <EOL> self . docker_client . remove_container ( name , force = True ) <EOL> return True <EOL> except Exception : <EOL> return False <EOL> def _get_container_description ( self , state , name , network_state = True , <EOL> ip_partitions = None ) : <EOL> state_container = state . containers [ name ] <EOL> container_id = state_container [ '<STR_LIT:id>' ] <EOL> try : <EOL> container = self . docker_client . inspect_container ( container_id ) <EOL> except docker . errors . APIError as err : <EOL> if err . response . status_code == <NUM_LIT> : <EOL> return Container ( name , container_id , ContainerState . MISSING ) <EOL> else : <EOL> raise <EOL> state_dict = container . get ( '<STR_LIT>' ) <EOL> if state_dict and state_dict . get ( '<STR_LIT>' ) : <EOL> container_state = ContainerState . UP <EOL> else : <EOL> container_state = ContainerState . DOWN <EOL> extras = { } <EOL> network = container . get ( '<STR_LIT>' ) <EOL> ip = None <EOL> if network : <EOL> ip = network . get ( '<STR_LIT>' ) <EOL> if ip : <EOL> extras [ '<STR_LIT>' ] = ip <EOL> if ( network_state and name in state . containers <EOL> and container_state == ContainerState . UP ) : <EOL> device = state_container [ '<STR_LIT>' ] <EOL> extras [ '<STR_LIT>' ] = device <EOL> extras [ '<STR_LIT>' ] = self . network . network_state ( device ) <EOL> if ip_partitions and ip : <EOL> extras [ '<STR_LIT>' ] = ip_partitions . get ( ip ) <EOL> else : <EOL> extras [ '<STR_LIT>' ] = NetworkState . UNKNOWN <EOL> extras [ '<STR_LIT>' ] = None <EOL> cfg_container = self . config . containers . get ( name ) <EOL> extras [ '<STR_LIT>' ] = cfg_container . neutral if cfg_container else False <EOL> extras [ '<STR_LIT>' ] = cfg_container . holy if cfg_container else False <EOL> return Container ( name , container_id , container_state , ** extras ) <EOL> def destroy ( self , force = False ) : <EOL> state = self . state_factory . load ( ) <EOL> containers = self . _get_docker_containers ( state ) <EOL> for container in list ( containers . values ( ) ) : <EOL> container_id = container [ '<STR_LIT>' ] <EOL> self . docker_client . stop ( container_id , timeout = DEFAULT_KILL_TIMEOUT ) <EOL> self . docker_client . remove_container ( container_id ) <EOL> self . network . restore ( state . blockade_id ) <EOL> self . state_factory . destroy ( ) <EOL> def _get_docker_containers ( self , state ) : <EOL> containers = { } <EOL> filters = { "<STR_LIT:label>" : [ "<STR_LIT>" + state . blockade_id ] } <EOL> prefix = state . blockade_id + "<STR_LIT:_>" <EOL> for container in self . docker_client . containers ( all = True , filters = filters ) : <EOL> for name in container [ '<STR_LIT>' ] : <EOL> name = name [ <NUM_LIT:1> : ] if name [ <NUM_LIT:0> ] == '<STR_LIT:/>' else name <EOL> name = name [ len ( prefix ) : ] if name . startswith ( prefix ) else name <EOL> if name in state . containers : <EOL> containers [ name ] = container <EOL> break <EOL> return containers <EOL> def _get_all_containers ( self , state ) : <EOL> containers = [ ] <EOL> ip_partitions = self . network . get_ip_partitions ( state . blockade_id ) <EOL> docker_containers = self . _get_docker_containers ( state ) <EOL> for name in docker_containers . keys ( ) : <EOL> container = self . _get_container_description ( state , name , ip_partitions = ip_partitions ) <EOL> containers . append ( container ) <EOL> return containers <EOL> def status ( self ) : <EOL> state = self . state_factory . load ( ) <EOL> return self . _get_all_containers ( state ) <EOL> def _get_running_containers ( self , container_names = None , state = None ) : <EOL> state = state or self . state_factory . load ( ) <EOL> containers = self . _get_all_containers ( state ) <EOL> running = dict ( ( c . name , c ) for c in containers <EOL> if c . state == ContainerState . UP ) <EOL> if container_names is None : <EOL> return list ( running . values ( ) ) <EOL> found = [ ] <EOL> for name in container_names : <EOL> container = running . get ( name ) <EOL> if not container : <EOL> raise BlockadeError ( "<STR_LIT>" <EOL> % ( name , ) ) <EOL> found . append ( container ) <EOL> return found <EOL> def _get_running_container ( self , container_name , state = None ) : <EOL> return self . _get_running_containers ( ( container_name , ) , state ) [ <NUM_LIT:0> ] <EOL> def __with_running_container_device ( self , container_names , state , func ) : <EOL> containers = self . _get_running_containers ( container_names , state ) <EOL> for container in containers : <EOL> device = container . device <EOL> func ( device ) <EOL> def flaky ( self , container_names , state ) : <EOL> self . __with_running_container_device ( container_names , state , self . network . flaky ) <EOL> def slow ( self , container_names , state ) : <EOL> self . __with_running_container_device ( container_names , state , self . network . slow ) <EOL> def duplicate ( self , container_names , state ) : <EOL> self . __with_running_container_device ( container_names , state , self . network . duplicate ) <EOL> def fast ( self , container_names , state ) : <EOL> self . __with_running_container_device ( container_names , state , self . network . fast ) <EOL> def restart ( self , container_names , state ) : <EOL> containers = self . _get_running_containers ( container_names , state ) <EOL> for container in containers : <EOL> self . _stop ( container ) <EOL> state = self . _start ( container . name , state ) <EOL> def stop ( self , container_names , state ) : <EOL> containers = self . _get_running_containers ( container_names , state ) <EOL> for container in containers : <EOL> self . _stop ( container ) <EOL> def _stop ( self , container ) : <EOL> self . docker_client . stop ( container . container_id , timeout = DEFAULT_KILL_TIMEOUT ) <EOL> def start ( self , container_names , state ) : <EOL> for container in container_names : <EOL> state = self . _start ( container , state ) <EOL> def _start ( self , container , state ) : <EOL> container_id = state . container_id ( container ) <EOL> if container_id is None : <EOL> return <EOL> self . docker_client . start ( container_id ) <EOL> device = self . _init_container ( container_id , container ) <EOL> updated_containers = state . containers <EOL> updated_containers [ container ] = { '<STR_LIT:id>' : container_id , '<STR_LIT>' : device } <EOL> self . state_factory . update ( state . blockade_id , updated_containers ) <EOL> return BlockadeState ( state . blockade_id , updated_containers ) <EOL> def random_partition ( self ) : <EOL> state = self . state_factory . load ( ) <EOL> containers = [ c . name for c in self . _get_running_containers ( state = state ) <EOL> if not c . holy ] <EOL> if not containers : <EOL> return [ ] <EOL> num_containers = len ( containers ) <EOL> num_partitions = random . randint ( <NUM_LIT:1> , num_containers ) <EOL> if num_partitions <= <NUM_LIT:1> : <EOL> self . join ( ) <EOL> return [ ] <EOL> else : <EOL> pick = lambda : containers . pop ( random . randint ( <NUM_LIT:0> , len ( containers ) - <NUM_LIT:1> ) ) <EOL> partitions = [ [ pick ( ) ] for _ in xrange ( num_partitions ) ] <EOL> for _ in xrange ( len ( containers ) ) : <EOL> random_partition = random . randint ( <NUM_LIT:0> , num_partitions - <NUM_LIT:1> ) <EOL> partitions [ random_partition ] . append ( pick ( ) ) <EOL> self . partition ( partitions , state ) <EOL> return partitions <EOL> def partition ( self , partitions , state = None ) : <EOL> state = state or self . state_factory . load ( ) <EOL> containers = self . _get_running_containers ( state = state ) <EOL> container_dict = dict ( ( c . name , c ) for c in containers ) <EOL> partitions = expand_partitions ( containers , partitions ) <EOL> container_partitions = [ ] <EOL> for partition in partitions : <EOL> container_partitions . append ( [ container_dict [ c ] for c in partition ] ) <EOL> self . network . partition_containers ( state . blockade_id , <EOL> container_partitions ) <EOL> def join ( self ) : <EOL> state = self . state_factory . load ( ) <EOL> self . network . restore ( state . blockade_id ) <EOL> def logs ( self , container_name ) : <EOL> container = self . _get_running_container ( container_name ) <EOL> return self . docker_client . logs ( container . container_id ) <EOL> class Container ( object ) : <EOL> ip_address = None <EOL> device = None <EOL> network_state = NetworkState . NORMAL <EOL> partition = None <EOL> def __init__ ( self , name , container_id , state , ** kwargs ) : <EOL> self . name = name <EOL> self . container_id = container_id <EOL> self . state = state <EOL> self . holy = False <EOL> self . neutral = False <EOL> for k , v in kwargs . items ( ) : <EOL> setattr ( self , k , v ) <EOL> def to_dict ( self ) : <EOL> return dict ( name = self . name , <EOL> container_id = self . container_id , <EOL> state = self . state , <EOL> ip_address = self . ip_address , <EOL> device = self . device , <EOL> network_state = self . network_state , <EOL> partition = self . partition ) <EOL> class ContainerState ( object ) : <EOL> '''<STR_LIT>''' <EOL> UP = "<STR_LIT>" <EOL> DOWN = "<STR_LIT>" <EOL> MISSING = "<STR_LIT>" <EOL> def docker_container_name ( blockade_id , name ) : <EOL> return '<STR_LIT:_>' . join ( ( blockade_id , name ) ) <EOL> def expand_partitions ( containers , partitions ) : <EOL> '''<STR_LIT>''' <EOL> all_names = frozenset ( c . name for c in containers if not c . holy ) <EOL> holy_names = frozenset ( c . name for c in containers if c . holy ) <EOL> neutral_names = frozenset ( c . name for c in containers if c . neutral ) <EOL> partitions = [ frozenset ( p ) for p in partitions ] <EOL> unknown = set ( ) <EOL> holy = set ( ) <EOL> union = set ( ) <EOL> for partition in partitions : <EOL> unknown . update ( partition - all_names - holy_names ) <EOL> holy . update ( partition - all_names ) <EOL> union . update ( partition ) <EOL> if unknown : <EOL> raise BlockadeError ( '<STR_LIT>' % <EOL> list ( unknown ) ) <EOL> if holy : <EOL> raise BlockadeError ( '<STR_LIT>' % <EOL> list ( holy ) ) <EOL> leftover = all_names . difference ( union ) <EOL> if leftover : <EOL> partitions . append ( leftover ) <EOL> if not neutral_names . issubset ( leftover ) : <EOL> partitions . append ( neutral_names ) <EOL> return partitions </s>
