Exemplo n.º 1
0
    def _collate_container_params(self, 
                                  containertemplate, 
                                  commonparams, 
                                  containerelem, 
                                  overlays):

        containerparams = [ ('docker.utsname', self.docker_name) ]

        if containertemplate:
            containerparams.extend(containertemplate.params)

        for k,v in commonparams:
            containerparams.append((k,v))

        for paramelem in containerelem.findall('./parameters/parameter'):
            if(str(paramelem.attrib['name']) == 'docker.utsname'):
                # the docker_name is set by the container element atribute
                print >>sys.stderr, \
                    'Found docker.utsname in containertemplate. Ignoring'
                continue

            containerparams.append((str(paramelem.attrib['name']),
                                    str(paramelem.attrib['value'])))

        for i,parampair in enumerate(containerparams):
            k,v = parampair

            try:
                containerparams[i] = (k,format_string(v, overlays))
            except TemplateError as ne:
                raise DOCKERError(str(ne))

        return containerparams
Exemplo n.º 2
0
def stopdockers(dockerplan):
    dockerplanfiledoc = dockerplan

    if not type(dockerplan) == DOCKERPlanFileDoc:
        # assume file name
        dockerplanfiledoc = DOCKERPlanFileDoc(dockerplan)

    try:
        DOCKERManagerImpl().stop(dockerplanfiledoc)
    except Exception as e:
        raise DOCKERError(e.message)
Exemplo n.º 3
0
def startdockers(dockerplan, writehosts=False, forcedockerroot=False, dryrun=False):
    dockerplanfiledoc = dockerplan

    if not type(dockerplan) == DOCKERPlanFileDoc:
        # assume file name
        dockerplanfiledoc = DOCKERPlanFileDoc(dockerplan)

    try:
        DOCKERManagerImpl().start(dockerplanfiledoc,
                               writehosts=writehosts,
                               forcedockerroot=forcedockerroot,
                               dryrun=dryrun)
    except Exception as e:
        raise DOCKERError(e.message)
Exemplo n.º 4
0
    def __init__(self, dockerplanfile):
        etce.xmldoc.XMLDoc.__init__(self, 'dockerplanfile.xsd')

        if not os.path.isfile(dockerplanfile):
            raise DOCKERError('Cannot find dockerplanfile "%s". Quitting.' % dockerplanfile)
        
        self._dockerplanfile = dockerplanfile

        # just xml parse first
        self._hostnames, \
        self._kernelparameters, \
        self._vxlantunnels, \
        self._bridges, \
        self._containers, \
        self._rootdirectories = self._parseplan(dockerplanfile)
def startfield(args):
    this_hostname = Platform().hostname()

    config = ConfigDictionary()

    workdir = config.get('etce', 'WORK_DIRECTORY')

    workdir = os.getenv('WORKDIR', workdir)

    if not os.path.exists(workdir):
        raise DOCKERError('ETCE WORK_DIRECTORY "%s" not found. ' \
                       'Please create it before starting.' % workdir)

    if args.dockerplanfile:
        dockerplanfile = args.dockerplanfile
    else:
        dockerplanfile = os.path.join(workdir, 'dockerplan.xml')

    plandoc = DOCKERPlanFileDoc(dockerplanfile)

    # lockfile
    lockfilename = \
        os.path.join(workdir, 'etce.docker.lock')

    if os.path.isfile(lockfilename):
        err = 'Detected an active docker field with root at: %s. ' \
              'Run "etce-docker stop" first.' % \
              plandoc.docker_root_directory(this_hostname)
        raise DOCKERError(err)

    cidr = os.getenv('CIDR', '10.99.0.0/16')
    containers = []
    for hostname, _ in plandoc.hostnames():
        for container in plandoc.containers(hostname):
            for bridgename, interfaceparams in container.interfaces.items():
                if IPAddress(interfaceparams['ipv4']) in IPNetwork(cidr):
                    containers.append(
                        (container.docker_name, interfaceparams['ipv4']))
                    break

        ipexist = []
    for _, ip in containers:
        ipexist.append(ip)
    my_ip = ''
    for ip in IPNetwork(cidr)[1:]:
        if not str(ip) in ipexist:
            my_ip = str(ip)
            break

    my_ip = my_ip + '/' + cidr.split('/')[1]

    # write to /etc/hosts in container/machine controller all external ip
    writehosts(plandoc, containers)

    hostfile = \
        os.path.join(workdir, 'hosts')

    if not args.dryrun:
        shutil.copy(dockerplanfile, lockfilename)
        shutil.copy('/etc/hosts', hostfile)

    startdockers(plandoc, args.writehosts, args.forcedockerroot, args.dryrun)

    other_hosts = []

    for hostname, ip in plandoc.hostnames():
        if hostname != (this_hostname and 'localhost'):
            other_hosts.append(hostname)

    # start containers on other hosts, if any
    if other_hosts:
        client = None
        try:
            client = ClientBuilder().build(\
                        other_hosts,
                        user=args.user,
                        port=args.port,
                        password=args.password)

            # push the file and execute
            client.put(dockerplanfile, '.', other_hosts, doclobber=True)

            # push the file
            client.put('/etc/hosts', '.', other_hosts, doclobber=True)

            # on the destination node the netplan file gets pushed to the
            # ETCE WORK_DIRECTORY
            command = 'dockermanager startdockers %s writehosts=%s forcedockerroot=%s' \
                      % (os.path.basename(dockerplanfile),
                         args.writehosts,
                         args.forcedockerroot)

            ret = client.execute(command, other_hosts)

            for k in ret:
                print '[%s] return: %s' % (k, ret[k].retval['result'])

        finally:
            if client:
                client.close()

                # A valid ETCE Test Directory.
                TESTDIRECTORY = os.path.join(workdir, 'pub-tdmact')

                # The output directory to place the built Test Directory.
                TESTROOT = os.path.join(
                    workdir, TESTDIRECTORY + '_' + etce.utils.timestamp())

                os.system('etce-test publish %s %s --verbose' %
                          (TESTDIRECTORY, TESTROOT))

                # A user tag to prepend to the name of each test result directory.
                TESTPREFIX = 'tdmact'
                # Run scenario order steps
                #if not args.collect:
                os.system(
                    'etce-test run --user root --policy autoadd -v --kill before --nocollect %s %s %s'
                    % (TESTPREFIX, HOSTFILE, TESTROOT))
def stopfield(args):
    workdir = ConfigDictionary().get('etce', 'WORK_DIRECTORY')

    workdir = os.getenv('WORKDIR', workdir)

    lockfilename = os.path.join(workdir, 'etce.docker.lock')

    if not os.path.exists(lockfilename) or not os.path.isfile(lockfilename):
        raise DOCKERError('Lockfile "%s" not found. Quitting.' % lockfilename)

    if args.dockerplanfile:
        dockerplanfile = args.dockerplanfile
    else:
        dockerplanfile = os.path.join(workdir, 'dockerplan.xml')

    plandoc = DOCKERPlanFileDoc(dockerplanfile)

    this_hostname = Platform().hostname()

    other_hosts = []

    for hostname, ip in plandoc.hostnames():
        if hostname != (this_hostname and 'localhost'):
            other_hosts.append(hostname)

    # stop containers on other hosts, if any
    try:
        if other_hosts:
            if args.collect:
                client_nodes = None
                try:
                    print
                    'Collecting results.'

                    time = 'collect_on_%s' % etce.timeutils.getstrtimenow(
                    ).split('.')[0]

                    localtestresultsdir = os.path.join(workdir, 'data', time)

                    field = Field(os.path.join(workdir, 'HOSTFILE'))

                    # root nodes host the filesystem for all of the virtual nodes attached
                    filesystemnodes = list(field.roots())

                    testdir = 'data'

                    client_nodes = ClientBuilder().build(
                        filesystemnodes,
                        user=args.user,
                        port=args.port,
                        password=args.password,
                        policy=args.policy)
                    try:

                        client_nodes.collect(testdir, localtestresultsdir,
                                             filesystemnodes)
                    except:
                        pass

                finally:
                    if client_nodes:
                        client_nodes.close()

            client = None
            try:
                client = ClientBuilder().build(other_hosts,
                                               user=args.user,
                                               port=args.port,
                                               password=args.password)

                # push the file and execute
                client.put(lockfilename, '.', other_hosts, doclobber=True)
                # on the destination node the netplan file gets pushed to the
                # ETCE WORK_DIRECTORY
                command = 'dockermanager stopdockers %s' % os.path.basename(
                    dockerplanfile)

                ret = client.execute(command, other_hosts)

                for k in ret:
                    print '[%s] return: %s' % (k, ret[k].retval['result'])
            finally:
                if client:
                    client.close()

    finally:
        #       os.system('ip link del vxlan1')
        stopdockers(plandoc)
        os.system('rm -f %s' % lockfilename)
Exemplo n.º 7
0
    def start(self, plandoc, writehosts, forcedockerroot=False, dryrun=False):
        hostname = socket.gethostname().split('.')[0].lower()
        dockerrootdir = plandoc.docker_root_directory(hostname)
        containers = plandoc.containers(hostname)

        if not containers:
            print 'No containers assigned to "%s". Skipping.' % hostname
            return
        
        if not dockerrootdir[0] == '/':
            print 'root_directory "%s" for hostname "%s" is not an absolute path. Quitting.' % \
                (dockerrootdir, hostname)
            return

        directory_level = len(dockerrootdir.split('/')) - 1
        if not directory_level >= 3:
            print 'root_directory "%s" for hostname "%s" is less than 3 levels deep. Quitting.' % \
                (dockerrootdir, hostname)
            return

        allowed_roots = ('tmp', 'opt', 'home', 'var', 'mnt')
        if not dockerrootdir.split('/')[1] in allowed_roots:
            print 'root_directory "%s" for hostname "%s" is not located in one of {%s} ' \
                'directory trees. Quitting.' % \
                (dockerrootdir, hostname, ', '.join(allowed_roots))
            return

        if dockerrootdir is None or len(containers) == 0:
            print 'No containers assigned to host %s. Quitting.' % hostname
            return

        # delete and remake the node root
        if os.path.exists(dockerrootdir):
            if forcedockerroot:
                print 'Force removal of "%s" docker root directory.' \
                    % dockerrootdir
                shutil.rmtree(dockerrootdir)
            else:
                raise DOCKERError('%s docker root directory already exists, Quitting.' % dockerrootdir)

        os.makedirs(dockerrootdir)

        # set kernelparameters
        kernelparameters = plandoc.kernelparameters(hostname)
        if len(kernelparameters) > 0:
            print 'Setting kernel parameters:'

            for kernelparamname,kernelparamval in kernelparameters.items():
                os.system('sysctl %s=%s' % (kernelparamname,kernelparamval))

        #vxlan tunnel
        if not dryrun:
            for _,vxlantunnel in plandoc.vxlantunnels(hostname).items():
                if not self._platform.isdeviceup('vxlan1'):
                    self._platform.runcommand('ip link add %s ' \
                                              'type vxlan id %s ' \
                                              'group 239.1.1.1 ' \
                                              'dev %s' % \
                                              (vxlantunnel.name,
                                               vxlantunnel.id,
                                               vxlantunnel.device))
                    self._platform.networkinterfaceup(vxlantunnel.name)

        # bring up bridge
        if not dryrun:
            for _,bridge in plandoc.bridges(hostname).items():
                if not bridge.persistent:
                    print 'Bringing up bridge: %s' % bridge.devicename

                    self._platform.dockerbridgeup(bridge.devicename,
                                                  bridge.subnet,
                                                  bridge.iprange,
                                                  bridge.gateway,
                                                  bridge.mtu,
                                                  bridge.addifs,
                                                  enablemulticastsnooping=True)
                    '''
                    if not bridge.ipv4 is None:
                        self._platform.adddeviceaddress(bridge.devicename,
                                                        bridge.ipv4)

                    if not bridge.ipv6 is None:
                        self._platform.adddeviceaddress(bridge.devicename,
                                                        bridge.ipv6)
                    '''

                    time.sleep(0.1)
                        
                elif not self._platform.isdeviceup(bridge.devicename):
                    raise RuntimeError('Bridge %s marked persistent is not up. Quitting.')

        # write hosts file
        if not dryrun:
            if writehosts:
                self._writehosts(containers)

        # create container files
        for container in containers:
            docker_directory = container.docker_directory

            self._makedirs(docker_directory)

            # make the config
            with open(os.path.join(docker_directory, 'config'), 'w') as configf:
                configf.write(str(container))

            # make init script
            filename,initscripttext = container.initscript

            if initscripttext:
                scriptfile = os.path.join(docker_directory, filename)

                with open(scriptfile, 'w') as sf:
                    sf.write(initscripttext)

                    os.chmod(scriptfile, 
                             stat.S_IRWXU | stat.S_IRGRP | \
                             stat.S_IXGRP | stat.S_IROTH | \
                             stat.S_IXOTH)

        if dryrun:
            print 'dryrun'
        else:
            self._startnodes(containers)
Exemplo n.º 8
0
    def _parseplan(self, dockerplanfile): 
        dockerplanelem = self.parse(dockerplanfile)

        kernelparameters = {}

        vxlantunnels = {}

        containertemplates = {}

        rootdirectories = {}

        dockerplanelems = \
            dockerplanelem.findall('./containertemplates/containertemplate')

        for containertemplateelem in dockerplanelems:
            containertemplate_name = containertemplateelem.attrib['name']

            containertemplate_parent_name = \
                containertemplateelem.attrib.get('parent', None)

            containertemplate_parent = None

            if containertemplate_parent_name:
                if not containertemplate_parent_name in containertemplates:
                    errmsg = 'parent "%s" of containertemplate "%s" not ' \
                             'previously listed. Quitting.' % \
                             (containertemplate_parent_name,
                              containertemplate_name)
                    raise DOCKERError(errmsg)

                containertemplate_parent = \
                    containertemplates[containertemplate_parent_name]

            containertemplates[containertemplate_name] = \
                ContainerTemplate(containertemplateelem, 
                                  containertemplate_parent)
                                            
        hostelems = dockerplanelem.findall('./hosts/host')

        bridges = {}

        containers = {}

        hostnames = []

        for hostelem in hostelems:
            hostname = hostelem.attrib.get('hostname').lower()
            ip = hostelem.attrib.get('ip')

            hostnames.append((hostname, ip))
            
            # 'localhost' is permitted as a catchall hostname to mean the
            # local machine only when one host is specified in the file
            if hostname == 'localhost':
                if len(hostelems) > 1:
                    error = '"localhost" hostname only permitted when one ' \
                            'host is specified. Quitting'
                    raise DOCKERError(error)

            # kernel params
            kernelparameters[hostname] = {}

            for paramelem in hostelem.findall('./kernelparameters/parameter'):
                kernelparameters[hostname][paramelem.attrib['name']] = \
                    paramelem.attrib['value']

            vxlantunnels[hostname] = {}
            for vxlantunnelelem in hostelem.findall('./vxlantunnels/vxlantunnel'):
                vxlantunnel = VXLanTunnel(vxlantunnelelem)
                vxlantunnels[hostname][vxlantunnel.name] = vxlantunnel

            # bridges (explicit)
            bridges[hostname] = {}

            for bridgeelem in hostelem.findall('./bridges/bridge'):
                bridge = Bridge(bridgeelem)

                bridges[hostname][bridge.name] = bridge

            containers[hostname] = []

            params = []

            containerselem = hostelem.findall('./containers')[0]

            root_directory = \
                os.path.join(ConfigDictionary().get('etce', 'WORK_DIRECTORY'), 'dockerroot')
            
            rootdirectories[hostname] = root_directory

            # ensure no repeated docker_name
            alldockerids = set([])

            # Create containers from container elems
            for containerelem in hostelem.findall('./containers/container'):
                templatename = containerelem.attrib.get('template', None)

                template = containertemplates.get(templatename, None)

                dockerids = etce.utils.nodestr_to_nodelist(
                    str(containerelem.attrib['docker_indices']))

                # fetch the overlays, use etce file values as default
                overlays = ConfigDictionary().asdict()['overlays']

                for overlayelem in containerelem.findall('./overlays/overlay'):
                    oname = overlayelem.attrib['name']

                    ovalue = overlayelem.attrib['value']

                    overlays[oname] = etce.utils.configstrtoval(ovalue)

                # fetch the overlaylists
                overlaylists = {}

                for overlaylistelem in containerelem.findall('./overlays/overlaylist'):
                    oname = overlaylistelem.attrib['name']

                    separator = overlaylistelem.attrib.get('separator',',')

                    ovalues = overlaylistelem.attrib['values'].split(separator)

                    overlaylists[oname] = ovalues

                # treat all values for each name as an int if possible,
                # else all strings
                for oname,ovals in overlaylists.items():
                    converted_vals = []
                    try:
                        converted_vals = [ etce.utils.configstrtoval(oval)
                                           for oval in ovals ]

                        overlaylists[oname] = converted_vals
                    except ValueError:
                        # leave as strings
                        pass

                # Why must a default value be supplied here when
                # schema declares this attribute with a default value?
                for i,dockerid in enumerate(dockerids):
                    # start with overlays
                    dockeroverlays = copy.copy(overlays)

                    # then add list items for this node
                    for oname,ovals in overlaylists.items():
                        dockeroverlays[oname] = ovals[i]

                    # then dockerindex, docker_name and docker_directory (cannot be overwritten)
                    dockeroverlays.update(
                        {'docker_index':dockerid})

                    dockeroverlays.update(
                        {'docker_name':format_string(containerelem.attrib['docker_name'], dockeroverlays)})

                    dockeroverlays.update(
                        {'docker_directory':os.path.join(root_directory, dockeroverlays['docker_name'])})

                    containerdockerids = [str(dockeroverlays['docker_name'])]

                    repeatedids = alldockerids.intersection(containerdockerids)

                    if len(repeatedids) > 0:
                        error = 'Duplicate docker_name {%s} found in DOCKER Plan File "%s" are not permitted. Quitting.' % \
                                (','.join([str(nid) for nid in list(repeatedids)]),
                                 dockerplanfile)

                        raise DOCKERError(error)

                    alldockerids.update(containerdockerids)

                    containers[hostname].append(Container(containerelem,
                                                          dockeroverlays,
                                                          params,
                                                          template,
                                                          bridges[hostname],
                                                          hostname))

            # Roll over containers to get names of implicit bridges added
            # from the container interface bridge names and augment
            # the bridges list
            for container in containers[hostname]:
                for iname,iparams in container.interfaces.items():
                    if not iname in bridges[hostname]:
                        bridges[hostname][iname] = BridgeImplicit(iname)
            
        return hostnames,kernelparameters,vxlantunnels,bridges,containers,rootdirectories
Exemplo n.º 9
0
    def _process_interfaces(self, containertemplate, containerelem, overlays):
        interfaces = defaultdict(lambda: {})

        bridge_entry_ipv4 = {}

        bridge_entry_ipv6 = {}

        try:
            if containertemplate:
                for bridgename,paramdict in containertemplate.interfaces.items():
                    bridgename = format_string(bridgename, overlays)
                    for iname,ival in paramdict.items():
                        interfaces[bridgename][format_string(iname, overlays)] = \
                            format_string(ival, overlays)

                for bridgename, entryname in \
                    containertemplate.hosts_entries_ipv4.items():
                    bridgename = format_string(bridgename, overlays)
                    bridge_entry_ipv4[bridgename] = format_string(entryname, overlays)

                for bridgename, entryname in \
                    containertemplate.hosts_entries_ipv6.items():
                    bridgename = format_string(bridgename, overlays)
                    bridge_entry_ipv6[bridgename] = format_string(entryname, overlays)

            # overwrite with local values from container
            for interfaceelem in containerelem.findall('./interfaces/interface'):
                bridgename = format_string(str(interfaceelem.attrib['bridge']), overlays)

                interfaceparams = interfaces[bridgename]

                for iparamelem in interfaceelem.findall('./parameter'):
                    iname = format_string(str(iparamelem.attrib['name']), overlays)

                    ival = format_string(str(iparamelem.attrib['value']), overlays)

                    interfaceparams[iname] =  ival

                entry_name_ipv4 = \
                    interfaceelem.attrib.get(
                        'hosts_entry_ipv4', 
                        bridge_entry_ipv4.get(bridgename, None))

                if entry_name_ipv4:
                    bridge_entry_ipv4[bridgename] = \
                        format_string(entry_name_ipv4, overlays)

                entry_name_ipv6 = \
                    interfaceelem.attrib.get(
                        'hosts_entry_ipv6', 
                        bridge_entry_ipv6.get(bridgename, None))

                if entry_name_ipv6:
                    bridge_entry_ipv6[bridgename] = \
                        format_string(entry_name_ipv6, overlays)

        except TemplateError as ne:
            raise DOCKERError(str(ne))


        hosts_entries_ipv4 = []

        for bridgename,entry_name_ipv4 in bridge_entry_ipv4.items():
            if not 'ipv4' in interfaces[bridgename]:
                error = 'Found hosts_entry_ipv4 attribute for ' \
                        'bridge "%s" for container "%s" but ' \
                        'no corresponding "ipv4" ' \
                        'value for the interface. Quitting.' \
                        % (bridgename, self.docker_name)
                raise DOCKERError(error)
                    
            addr = interfaces[bridgename]['ipv4']

            hosts_entries_ipv4.append((entry_name_ipv4,  addr.split('/')[0]))

        hosts_entries_ipv6 = []

        for bridgename,entry_name_ipv6 in bridge_entry_ipv6.items():
            if not 'ipv6' in interfaces[bridgename]:
                error = 'Found hosts_entry_ipv6 attribute for ' \
                        'bridge "%s" for container "%s" but ' \
                        'no corresponding "ipv6" ' \
                        'value for the interface. Quitting.' \
                        % (bridgename, self.docker_name)
                raise DOCKERError(error)
                
            addr = interfaces[bridgename]['ipv6']

            hosts_entries_ipv6.append((entry_name_ipv6,  addr))

        return interfaces,hosts_entries_ipv4,hosts_entries_ipv6