source: fedd/fedd_allocate_project.py @ fca8740

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

split out internal interfaces

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