source: fedd/fedd_allocate_project.py @ 11a08b0

axis_examplecompt_changesinfo-opsversion-1.30version-2.00version-3.01version-3.02
Last change on this file since 11a08b0 was 11a08b0, checked in by Ted Faber <faber@…>, 16 years ago

decent logging

  • Property mode set to 100644
File size: 7.0 KB
Line 
1#!/usr/local/bin/python
2
3import os,sys
4
5from BaseHTTPServer import BaseHTTPRequestHandler
6from ZSI import *
7from M2Crypto import SSL
8from M2Crypto.m2xmlrpclib import SSL_Transport
9from M2Crypto.SSL.SSLServer import SSLServer
10import M2Crypto.httpslib
11import xmlrpclib
12
13import re
14import random
15import string
16import subprocess
17import tempfile
18
19from fedd_services import *
20from fedd_internal_services import *
21from fedd_util import *
22import parse_detail
23from service_error import *
24import logging
25
26
27class nullHandler(logging.Handler):
28    def emit(self, record): pass
29
30fl = logging.getLogger("fedd.allocate.local")
31fl.addHandler(nullHandler())
32fl = logging.getLogger("fedd.allocate.remote")
33fl.addHandler(nullHandler())
34
35class fedd_allocate_project_local:
36    def __init__(self, dp=False, url=None, certs=None):
37        """
38        Initializer.  Parses a configuration if one is given.
39        """
40
41        self.dynamic_projects = dp
42        self.wap = '/usr/testbed/sbin/wap'
43        self.newproj = '/usr/testbed/sbin/newproj'
44        self.mkproj = '/usr/testbed/sbin/mkproj'
45        self.grantnodetype = '/usr/testbed/sbin/grantnodetype'
46        self.log = logging.getLogger("fedd.allocate.local")
47
48    def random_string(self, s, n=3):
49        """Append n random ASCII characters to s and return the string"""
50        rv = s
51        for i in range(0,n):
52            rv += random.choice(string.ascii_letters)
53        return rv
54
55    def write_attr_xml(self, file, root, lines):
56        """
57        Write an emulab config file for a dynamic project.
58
59        Format is <root><attribute name=lines[0]>lines[1]</attribute></root>
60        """
61        # Convert a pair to an attribute line
62        out_attr = lambda a,v : \
63                '<attribute name="%s"><value>%s</value></attribute>' % (a, v)
64
65        f = os.fdopen(file, "w")
66        f.write("<%s>\n" % root)
67        f.write("\n".join([out_attr(*l) for l in lines]))
68        f.write("</%s>\n" % root)
69        f.close()
70
71
72    def dynamic_project(self, req, fedid=None):
73        """
74        Create a dynamic project with ssh access
75
76        Req includes the project and resources as a dictionary
77        """
78        # tempfiles for the parameter files
79        uf, userfile = tempfile.mkstemp(prefix="usr", suffix=".xml",
80                dir="/tmp")
81        pf, projfile = tempfile.mkstemp(prefix="proj", suffix=".xml",
82                dir="/tmp")
83
84        if req.has_key('AllocateProjectRequestBody') and \
85                req['AllocateProjectRequestBody'].has_key('project'):
86            proj = req['AllocateProjectRequestBody']['project']
87        else:
88            raise service_error(service_error.req, 
89                    "Badly formed allocation request")
90        # Take the first user and ssh key
91        name = proj.get('name', None) or self.random_string("proj",4)
92        user = proj.get('user', None)
93        if user != None:
94            user = user[0]      # User is a list, take the first entry
95            if not user.has_key("userID"):
96                uname = self.random_string("user", 3)
97            else:
98                uid = proj['userID']
99                # XXX: fedid
100                uname = uid.get('localname', None) or \
101                        uid.get('kerberosUsername', None) or \
102                        uid.get('uri', None)
103                if uname == None:
104                    raise fedd_proj.service_error(fedd_proj.service_error.req, 
105                            "No ID for user");
106
107            access = user.get('access', None)
108            if access != None:
109                ssh = access[0].get('sshPubkey', None)
110                if ssh == None:
111                    raise fedd_proj.service_error(fedd_proj.service_error.req, 
112                            "No ssh key for user");
113        else:
114            raise fedd_proj.service_error(fedd_proj.service_error.req, 
115                    "No access information for project");
116
117        # uname, name and ssh are set
118        user_fields = [
119                ("name", "Federation User %s" % uname),
120                ("email", "%s-fed@isi.deterlab.net" % uname),
121                ("password", self.random_string("", 8)),
122                ("login", uname),
123                ("address", "4676 Admiralty"),
124                ("city", "Marina del Rey"),
125                ("state", "CA"),
126                ("zip", "90292"),
127                ("country", "USA"),
128                ("phone", "310-448-9190"),
129                ("title", "None"),
130                ("affiliation", "USC/ISI"),
131                ("pubkey", ssh)
132        ]
133
134        proj_fields = [
135                ("name", name),
136                ("short description", "dynamic federated project"),
137                ("URL", "http://www.isi.edu/~faber"),
138                ("funders", "USC/USU"),
139                ("long description", "Federation access control"),
140                ("public", "1"),
141                ("num_pcs", "100"),
142                ("linkedtous", "1"),
143                ("newuser_xml", userfile)
144        ]
145       
146
147        # Write out the files
148        self.write_attr_xml(uf, "user", user_fields)
149        self.write_attr_xml(pf, "project", proj_fields)
150
151        # Generate the commands (only grantnodetype's are dynamic)
152        cmds = [
153                (self.wap, self.newproj, projfile),
154                (self.wap, self.mkproj, name)
155                ]
156
157        # Add commands to grant access to any resources in the request.
158        for nt in [ h for r in req.get('resources', []) \
159                if r.has_key('node') and r['node'].has_key('hardware')\
160                    for h in r['node']['hardware'] ] :
161            cmds.append((self.wap, self.grantnodetype, '-p', name, nt))
162
163        # Create the projects
164        rc = 0
165        for cmd in cmds:
166            self.log.debug("[dynamic_project]: %s" % ' '.join(cmd))
167            if self.dynamic_projects:
168                try:
169                    rc = subprocess.call(cmd)
170                except OSerror, e:
171                    raise fedd_proj.service_error(\
172                            fedd_proj.service_error.internal,
173                            "Dynamic project subprocess creation error "+ \
174                                    "[%s] (%s)" %  (cmd[1], e.strerror))
175
176            if rc != 0: 
177                raise fedd_proj.service_error(\
178                        fedd_proj.service_error.internal,
179                        "Dynamic project subprocess error " +\
180                                "[%s] (%d)" % (cmd[1], rc))
181        # Clean up tempfiles
182        os.unlink(userfile)
183        os.unlink(projfile)
184        rv = {\
185            'project': {\
186                'name': { 'localname': name }, 
187                'user' : [ {\
188                    'userID': { 'localname' : uname },
189                    'access': [ { 'sshPubkey' : ssh } ],
190                } ]\
191            }\
192        }
193        return rv
194
195
196class fedd_allocate_project_remote:
197    def __init__(self, dp=False, url=None, certs=None):
198        """
199        Initializer.  Parses a configuration if one is given.
200        """
201
202        self.dynamic_projects = dp
203        self.url = url
204
205        if certs != None and isinstance(certs, type(tuple())):
206            self.cert_file, self.trusted_certs, self.cert_pwd = certs
207        else:
208            self.cert_file, self.trusted_certs, self.cert_pwd = \
209                    (None, None, None)
210
211    def dynamic_project(self, req, fedid=None):
212        """
213        Send req on to a remote project instantiator.
214
215        Req is just the projectAllocType object.  This function re-wraps it.
216        It also rethrows any faults.
217        """
218        # No retry loop here.  Proxy servers must correctly authenticate
219        # themselves without help
220        try:
221            ctx = fedd_ssl_context(self.cert_file, self.trusted_certs,
222                    password=self.cert_pwd)
223        except SSL.SSLError:
224            raise service_error(service_error.server_config, 
225                    "Server certificates misconfigured")
226
227        loc = feddInternalServiceLocator();
228        port = loc.getfeddInternalPortType(self.url,
229                transport=M2Crypto.httpslib.HTTPSConnection, 
230                transdict={ 'ssl_context' : ctx })
231
232        if req.has_key('AllocateProjectRequestBody'):
233            req = req['AllocateProjectRequestBody']
234        else:
235            raise service_error(service_error.req, "Bad formated request");
236
237        # Reconstruct the full request message
238        msg = AllocateProjectRequestMessage()
239        msg.set_element_AllocateProjectRequestBody(
240                pack_soap(msg, "AllocateProjectRequestBody", req))
241        try:
242            resp = port.AllocateProject(msg)
243        except ZSI.ParseException, e:
244            raise service_error(service_error.proxy,
245                    "Bad format message (XMLRPC??): %s" % str(e))
246        r = unpack_soap(resp)
247
248        if r.has_key('AllocateProjectResponseBody'):
249            return r['AllocateProjectResponseBody']
250        else:
251            raise service_error(service_error.proxy, "Bad proxy response")
252
Note: See TracBrowser for help on using the repository browser.