source: fedd/fedd_allocate_project.py @ ef36c1e

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

split out project creation

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