Changeset 5ecb9a3 for fedd/federation


Ignore:
Timestamp:
Dec 6, 2010 1:56:04 PM (14 years ago)
Author:
Ted Faber <faber@…>
Branches:
axis_example, compt_changes, info-ops, master
Children:
cf0ff4f
Parents:
d31a171
Message:

Checkpoint along the path to #10

Several create_experiment subtasks have been brokern out and the
get_access and get_legacy access signatures have been unified.

Part of #10

Location:
fedd/federation
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • fedd/federation/experiment_control.py

    rd31a171 r5ecb9a3  
    203203                        "creating local one.")
    204204                auth = authorizer()
     205            self.get_access = self.legacy_get_access
    205206        elif self.auth_type == 'abac':
    206207            self.auth = abac_authorizer(load=self.auth_dir)
     
    14511452        return rv
    14521453
    1453     def create_experiment(self, req, fid):
    1454         """
    1455         The external interface to experiment creation called from the
    1456         dispatcher.
    1457 
    1458         Creates a working directory, splits the incoming description using the
    1459         splitter script and parses out the various subsections using the
    1460         classes above.  Once each sub-experiment is created, use pooled threads
    1461         to instantiate them and start it all up.
    1462         """
    1463 
    1464         req = req.get('CreateRequestBody', None)
    1465         if not req:
    1466             raise service_error(service_error.req,
    1467                     "Bad request format (no CreateRequestBody)")
    1468 
     1454    @staticmethod
     1455    def get_create_key(req):
     1456        """
     1457        Parse the experiment identifiers out of the request (the request body
     1458        tag has been removed).  Specifically this pulls either the fedid or the
     1459        localname out of the experimentID field.  A fedid is preferred.  If
     1460        neither is present or the request does not contain the fields,
     1461        service_errors are raised.
     1462        """
    14691463        # Get the experiment access
    14701464        exp = req.get('experimentID', None)
     
    14721466            if exp.has_key('fedid'):
    14731467                key = exp['fedid']
    1474                 expid = key
    1475                 eid = None
    14761468            elif exp.has_key('localname'):
    14771469                key = exp['localname']
    1478                 eid = key
    1479                 expid = None
    14801470            else:
    14811471                raise service_error(service_error.req, "Unknown lookup type")
     
    14831473            raise service_error(service_error.req, "No request?")
    14841474
    1485         # Import information from the requester
    1486         if self.auth.import_credentials(data_list=req.get('credential', [])):
    1487             self.auth.save()
    1488 
    1489         self.check_experiment_access(fid, key)
    1490 
    1491         # Install the testbed map entries supplied with the request into a copy
    1492         # of the testbed map.
    1493         tbmap = dict(self.tbmap)
    1494         for m in req.get('testbedmap', []):
    1495             if 'testbed' in m and 'uri' in m:
    1496                 tbmap[m['testbed']] = m['uri']
    1497 
    1498         try:
    1499             tmpdir = tempfile.mkdtemp(prefix="split-")
    1500             os.mkdir(tmpdir+"/keys")
    1501         except EnvironmentError:
    1502             raise service_error(service_error.internal, "Cannot create tmp dir")
    1503 
    1504         gw_pubkey_base = "fed.%s.pub" % self.ssh_type
    1505         gw_secretkey_base = "fed.%s" % self.ssh_type
    1506         gw_pubkey = tmpdir + "/keys/" + gw_pubkey_base
    1507         gw_secretkey = tmpdir + "/keys/" + gw_secretkey_base
    1508         tclfile = tmpdir + "/experiment.tcl"
    1509         tbparams = { }
    1510         try:
    1511             access_user = self.accessdb[fid]
    1512         except KeyError:
    1513             raise service_error(service_error.internal,
    1514                     "Access map and authorizer out of sync in " + \
    1515                             "create_experiment for fedid %s"  % fid)
    1516 
    1517         pid = "dummy"
    1518         gid = "dummy"
    1519 
    1520         # The tcl parser needs to read a file so put the content into that file
    1521         descr=req.get('experimentdescription', None)
    1522         if descr:
    1523             file_content=descr.get('ns2description', None)
    1524             if file_content:
    1525                 try:
    1526                     f = open(tclfile, 'w')
    1527                     f.write(file_content)
    1528                     f.close()
    1529                 except EnvironmentError:
    1530                     raise service_error(service_error.internal,
    1531                             "Cannot write temp experiment description")
    1532             else:
    1533                 raise service_error(service_error.req,
    1534                         "Only ns2descriptions supported")
    1535         else:
    1536             raise service_error(service_error.req, "No experiment description")
    1537 
     1475        return key
     1476
     1477    def get_experiment_ids_and_start(self, key, tmpdir):
     1478        """
     1479        Get the experiment name, id and access certificate from the state, and
     1480        set the experiment state to 'starting'.  returns a triple (fedid,
     1481        localname, access_cert_file). The access_cert_file is a copy of the
     1482        contents of the access certificate, created in the tempdir with
     1483        restricted permissions.  If things are confused, raise an exception.
     1484        """
     1485
     1486        expid = eid = None
    15381487        self.state_lock.acquire()
    15391488        if self.state.has_key(key):
     
    15511500        self.state_lock.release()
    15521501
    1553         if not (eid and expid):
    1554             raise service_error(service_error.internal,
    1555                     "Cannot find local experiment info!?")
    1556 
    15571502        # make a protected copy of the access certificate so the experiment
    15581503        # controller can act as the experiment principal.
    1559         if expcert and self.auth_type != 'legacy':
     1504        if expcert:
    15601505            expcert_file = self.make_temp_certfile(expcert, tmpdir)
    15611506            if not expcert_file:
     
    15651510            expcert_file = None
    15661511
     1512        return (eid, expid, expcert_file)
     1513
     1514    def get_topology(self, req, tmpdir):
     1515        """
     1516        Get the ns2 content and put it into a file for parsing.  Call the local
     1517        or remote parser and return the topdl.Topology.  Errors result in
     1518        exceptions.  req is the request and tmpdir is a work directory.
     1519        """
     1520
     1521        # The tcl parser needs to read a file so put the content into that file
     1522        descr=req.get('experimentdescription', None)
     1523        if descr:
     1524            file_content=descr.get('ns2description', None)
     1525        else:
     1526            raise service_error(service_error.req, "No experiment description")
     1527
     1528
     1529        if self.splitter_url:
     1530            self.log.debug("Calling remote topdl translator at %s" % \
     1531                    self.splitter_url)
     1532            top = self.remote_ns2topdl(self.splitter_url, file_content)
     1533        else:
     1534            tclfile = os.path.join(tmpdir, "experiment.tcl")
     1535            if file_content:
     1536                try:
     1537                    f = open(tclfile, 'w')
     1538                    f.write(file_content)
     1539                    f.close()
     1540                except EnvironmentError:
     1541                    raise service_error(service_error.internal,
     1542                            "Cannot write temp experiment description")
     1543            else:
     1544                raise service_error(service_error.req,
     1545                        "Only ns2descriptions supported")
     1546            pid = "dummy"
     1547            gid = "dummy"
     1548            eid = "dummy"
     1549
     1550            tclcmd = [self.tclsh, self.tcl_splitter, '-t', '-x',
     1551                str(self.muxmax), '-m', 'dummy']
     1552
     1553            tclcmd.extend([pid, gid, eid, tclfile])
     1554
     1555            self.log.debug("running local splitter %s", " ".join(tclcmd))
     1556            # This is just fantastic.  As a side effect the parser copies
     1557            # tb_compat.tcl into the current directory, so that directory
     1558            # must be writable by the fedd user.  Doing this in the
     1559            # temporary subdir ensures this is the case.
     1560            tclparser = Popen(tclcmd, stdout=PIPE, close_fds=True,
     1561                    cwd=tmpdir)
     1562            split_data = tclparser.stdout
     1563
     1564            top = topdl.topology_from_xml(file=split_data, top="experiment")
     1565            os.remove(tclfile)
     1566
     1567        return top
     1568
     1569    def get_testbed_services(self, req):
     1570        """
     1571        Parse the services section of the request into into two dicts mapping
     1572        testbed to lists of federated_service objects.  The first lists all
     1573        exporters of services, and the second all exporters of services that
     1574        need control portals int the experiment.
     1575        """
     1576        masters = { }
     1577        pmasters = { }
     1578        for s in req.get('service', []):
     1579            # If this is a service request with the importall field
     1580            # set, fill it out.
     1581
     1582            if s.get('importall', False):
     1583                s['import'] = [ tb for tb in testbeds \
     1584                        if tb not in s.get('export',[])]
     1585                del s['importall']
     1586
     1587            # Add the service to masters
     1588            for tb in s.get('export', []):
     1589                if s.get('name', None):
     1590
     1591                    params = { }
     1592                    for a in s.get('fedAttr', []):
     1593                        params[a.get('attribute', '')] = a.get('value','')
     1594
     1595                    fser = federated_service(name=s['name'],
     1596                            exporter=tb, importers=s.get('import',[]),
     1597                            params=params)
     1598                    if fser.name == 'hide_hosts' \
     1599                            and 'hosts' not in fser.params:
     1600                        fser.params['hosts'] = \
     1601                                ",".join(tb_hosts.get(fser.exporter, []))
     1602                    if tb in masters: masters[tb].append(fser)
     1603                    else: masters[tb] = [fser]
     1604
     1605                    if fser.portal:
     1606                        if tb not in pmasters: pmasters[tb] = [ fser ]
     1607                        else: pmasters[tb].append(fser)
     1608                else:
     1609                    self.log.error('Testbed service does not have name " + \
     1610                            "and importers')
     1611        return masters, pmasters
     1612
     1613
     1614    def create_experiment(self, req, fid):
     1615        """
     1616        The external interface to experiment creation called from the
     1617        dispatcher.
     1618
     1619        Creates a working directory, splits the incoming description using the
     1620        splitter script and parses out the various subsections using the
     1621        classes above.  Once each sub-experiment is created, use pooled threads
     1622        to instantiate them and start it all up.
     1623        """
     1624
     1625        req = req.get('CreateRequestBody', None)
     1626        if req:
     1627            key = self.get_create_key(req)
     1628        else:
     1629            raise service_error(service_error.req,
     1630                    "Bad request format (no CreateRequestBody)")
     1631
     1632        # Import information from the requester
     1633        if self.auth.import_credentials(data_list=req.get('credential', [])):
     1634            self.auth.save()
     1635
     1636        # Make sure that the caller can talk to us
     1637        self.check_experiment_access(fid, key)
     1638
     1639        # Install the testbed map entries supplied with the request into a copy
     1640        # of the testbed map.
     1641        tbmap = dict(self.tbmap)
     1642        for m in req.get('testbedmap', []):
     1643            if 'testbed' in m and 'uri' in m:
     1644                tbmap[m['testbed']] = m['uri']
     1645
     1646        # a place to work
     1647        try:
     1648            tmpdir = tempfile.mkdtemp(prefix="split-")
     1649            os.mkdir(tmpdir+"/keys")
     1650        except EnvironmentError:
     1651            raise service_error(service_error.internal, "Cannot create tmp dir")
     1652
     1653        gw_pubkey_base = "fed.%s.pub" % self.ssh_type
     1654        gw_secretkey_base = "fed.%s" % self.ssh_type
     1655        gw_pubkey = tmpdir + "/keys/" + gw_pubkey_base
     1656        gw_secretkey = tmpdir + "/keys/" + gw_secretkey_base
     1657        tbparams = { }
     1658
     1659        eid, expid, expcert_file = \
     1660                self.get_experiment_ids_and_start(key, tmpdir)
     1661
     1662        # This catches exceptions to clear the placeholder if necessary
    15671663        try:
    1568             # This catches exceptions to clear the placeholder if necessary
     1664            if not (eid and expid):
     1665                raise service_error(service_error.internal,
     1666                        "Cannot find local experiment info!?")
    15691667            try:
    15701668                self.generate_ssh_keys(gw_secretkey, self.ssh_type)
     
    15731671                        "Bad key type (%s)" % self.ssh_type)
    15741672
    1575             # Copy the service request
    1576             tb_services = [ s for s in req.get('service',[]) ]
    1577             # Translate to topdl
    1578             if self.splitter_url:
    1579                 self.log.debug("Calling remote topdl translator at %s" % \
    1580                         self.splitter_url)
    1581                 top = self.remote_ns2topdl(self.splitter_url, file_content)
    1582             else:
    1583                 tclcmd = [self.tclsh, self.tcl_splitter, '-t', '-x',
    1584                     str(self.muxmax), '-m', 'dummy']
    1585 
    1586                 tclcmd.extend([pid, gid, eid, tclfile])
    1587 
    1588                 self.log.debug("running local splitter %s", " ".join(tclcmd))
    1589                 # This is just fantastic.  As a side effect the parser copies
    1590                 # tb_compat.tcl into the current directory, so that directory
    1591                 # must be writable by the fedd user.  Doing this in the
    1592                 # temporary subdir ensures this is the case.
    1593                 tclparser = Popen(tclcmd, stdout=PIPE, close_fds=True,
    1594                         cwd=tmpdir)
    1595                 split_data = tclparser.stdout
    1596 
    1597                 top = topdl.topology_from_xml(file=split_data, top="experiment")
    1598 
     1673            top = self.get_topology(req, tmpdir)
     1674
     1675            # Assign the IPs
    15991676            hosts, ip_allocator = self.allocate_ips_to_topo(top)
    16001677            # Find the testbeds to look up
    1601             testbeds = set([ a.value for e in top.elements \
    1602                     for a in e.attribute \
    1603                         if a.attribute == 'testbed'])
    1604 
    16051678            tb_hosts = { }
    1606             for tb in testbeds:
    1607                 tb_hosts[tb] = [ e.name for e in top.elements \
    1608                         if isinstance(e, topdl.Computer) and \
    1609                             e.get_attribute('testbed') and \
    1610                             e.get_attribute('testbed') == tb]
    1611 
    1612             masters = { }           # testbeds exporting services
    1613             pmasters = { }          # Testbeds exporting services that
    1614                                     # need portals
    1615             for s in tb_services:
    1616                 # If this is a service request with the importall field
    1617                 # set, fill it out.
    1618 
    1619                 if s.get('importall', False):
    1620                     s['import'] = [ tb for tb in testbeds \
    1621                             if tb not in s.get('export',[])]
    1622                     del s['importall']
    1623 
    1624                 # Add the service to masters
    1625                 for tb in s.get('export', []):
    1626                     if s.get('name', None):
    1627                         if tb not in masters:
    1628                             masters[tb] = [ ]
    1629 
    1630                         params = { }
    1631                         if 'fedAttr' in s:
    1632                             for a in s['fedAttr']:
    1633                                 params[a.get('attribute', '')] = \
    1634                                         a.get('value','')
    1635 
    1636                         fser = federated_service(name=s['name'],
    1637                                 exporter=tb, importers=s.get('import',[]),
    1638                                 params=params)
    1639                         if fser.name == 'hide_hosts' \
    1640                                 and 'hosts' not in fser.params:
    1641                             fser.params['hosts'] = \
    1642                                     ",".join(tb_hosts.get(fser.exporter, []))
    1643                         masters[tb].append(fser)
    1644 
    1645                         if fser.portal:
    1646                             if tb not in pmasters: pmasters[tb] = [ fser ]
    1647                             else: pmasters[tb].append(fser)
    1648                     else:
    1649                         self.log.error('Testbed service does not have name " + \
    1650                                 "and importers')
    1651 
    1652 
     1679            testbeds = [ ]
     1680            for e in top.elements:
     1681                if isinstance(e, topdl.Computer):
     1682                    tb = e.get_attribute('testbed') or 'default'
     1683                    if tb in tb_hosts: tb_hosts[tb].append(e.name)
     1684                    else:
     1685                        tb_hosts[tb] = [ e.name ]
     1686                        testbeds.append(tb)
     1687
     1688            masters, pmasters = self.get_testbed_services(req)
    16531689            allocated = { }         # Testbeds we can access
    16541690            topo ={ }               # Sub topologies
    16551691            connInfo = { }          # Connection information
    16561692
    1657             if self.auth_type == 'legacy':
    1658                 self.get_legcay_access_to_testbeds(testbeds, access_user,
    1659                         allocated, tbparams, masters, tbmap)
    1660             elif self.auth_type == 'abac':
    1661                 self.get_access_to_testbeds(testbeds, fid, allocated,
    1662                         tbparams, masters, tbmap, expid, expcert_file)
    1663             else:
    1664                 raise service_error(service_error.internal,
    1665                         "Unknown auth_type %s" % self.auth_type)
     1693            self.get_access_to_testbeds(testbeds, fid, allocated,
     1694                    tbparams, masters, tbmap, expid, expcert_file)
    16661695
    16671696            self.split_topology(top, topo, testbeds)
  • fedd/federation/experiment_control_legacy.py

    rd31a171 r5ecb9a3  
    4848    """
    4949
    50     def get_legacy_access(self, tb, tbparam, access_user, masters, tbmap):
     50    def get_legacy_access(self, tb, tbparam, fid, masters, tbmap, expid=None,
     51            expcert=None):
    5152        """
    5253        Get access to testbed through fedd and set the parameters for that tb
     
    6768                raise service_error(service_error.req,
    6869                        "More than one project export is not supported")
     70
     71        try:
     72            access_user = self.accessdb[fid]
     73        except KeyError:
     74            raise service_error(service_error.internal,
     75                    "Access map and authorizer out of sync in " + \
     76                            "create_experiment for fedid %s"  % fid)
    6977
    7078        uri = tbmap.get(testbed_base(tb), None)
     
    187195
    188196
    189     def get_legacy_access_to_testbeds(self, testbeds, access_user, allocated,
    190             tbparams, masters, tbmap):
    191         """
    192         Request access to the various testbeds required for this instantiation
    193         (passed in as testbeds).  User, access_user, expoert_project and master
    194         are used to construct the correct requests.  Per-testbed parameters are
    195         returned in tbparams.
    196         """
    197         for tb in testbeds:
    198             self.get_access(tb, tbparams, access_user, masters, tbmap)
    199             allocated[tb] = 1
    200 
Note: See TracChangeset for help on using the changeset viewer.