source: fedd/federation/emulab_segment.py

Last change on this file was 26821ac, checked in by Ted Faber <faber@…>, 6 years ago

Hooks for shared NAT

  • Property mode set to 100755
File size: 7.5 KB
Line 
1#!/usr/local/bin/python
2
3import sys, os
4import re
5
6import tempfile
7import subprocess
8import logging 
9import time
10import signal
11
12import util
13
14from ssh_emulab_segment import ssh_emulab_segment
15from xmlrpc_emulab_segment import xmlrpc_emulab_segment
16
17
18class start_segment(ssh_emulab_segment, xmlrpc_emulab_segment):
19    def __init__(self, log=None, keyfile=None, debug=False, boss=None,
20            ops=None, cert=None):
21        ssh_emulab_segment.__init__(self, log=log, keyfile=keyfile, debug=debug)
22        xmlrpc_emulab_segment.__init__(self, boss=boss, ops=ops, cert=cert)
23
24    def set_up_experiment_filespace(self, user, host, pid, eid, tmpdir):
25        """
26        Send all the software and configuration files into the experiment's
27        file space.  To reduce the number of ssh connections, we script many
28        changes and execute the script.
29        """
30        # Configuration directories on the remote machine
31        proj_dir = "/proj/%s/exp/%s/tmp" % (pid, eid)
32        softdir_root = "/proj/%s/software/" % pid
33        softdir = "%s/%s" % (softdir_root, eid)
34        # Local software dir
35        lsoftdir = "%s/software" % tmpdir
36
37        # Open up a temporary file to contain a script for setting up the
38        # filespace for the new experiment.
39        self.log.info("[start_segment]: creating script file")
40        try:
41            sf, scriptname = tempfile.mkstemp()
42            scriptfile = os.fdopen(sf, 'w')
43        except EnvironmentError:
44            return False
45
46        scriptbase = os.path.basename(scriptname)
47
48        # Script the filesystem changes
49       
50        # Set umask to 007.  Since we are operating
51        # within /proj, we want to permit all proj users
52        # read and write.  This is especially necessary
53        # since creating directory hierarchy (mkdir -p).
54        print >>scriptfile, "umask 007"
55        print >>scriptfile, "/bin/rm -rf %s" % proj_dir
56        # Clear and create the software directory
57        print >>scriptfile, "/bin/rm -rf %s/*" % softdir
58        print >>scriptfile, 'mkdir %s' % proj_dir
59        print >>scriptfile, 'chmod 0770 %s' % proj_dir
60        if os.path.isdir(lsoftdir):
61            print >>scriptfile, 'mkdir %s' % softdir_root
62            print >>scriptfile, 'chmod 0770 %s' % softdir_root
63            print >>scriptfile, 'mkdir %s' % softdir
64            print >>scriptfile, 'chmod 0770 %s' % softdir
65        print >>scriptfile, "rm -f %s" % scriptbase
66        scriptfile.close()
67
68        # Move the script to the remote machine
69        # XXX: could collide tempfile names on the remote host
70        if self.scp_file(scriptname, user, host, scriptbase):
71            os.remove(scriptname)
72        else:
73            return False
74
75        # Execute the script (and the script's last line deletes it)
76        if not self.ssh_cmd(user, host, "sh -x %s" % scriptbase):
77            return False
78
79        for f in os.listdir(tmpdir):
80            if not os.path.isdir("%s/%s" % (tmpdir, f)):
81                if not self.scp_file("%s/%s" % (tmpdir, f), user, host,
82                        "%s/%s" % (proj_dir, f)):
83                    return False
84        if os.path.isdir(lsoftdir):
85            for f in os.listdir(lsoftdir):
86                if not os.path.isdir("%s/%s" % (lsoftdir, f)):
87                    if not self.scp_file("%s/%s" % (lsoftdir, f), 
88                            user, host, "%s/%s" % (softdir, f)):
89                        return False
90        return True
91
92
93    def __call__(self, parent, eid, pid, user, tclfile, tmpdir, timeout=0, 
94            gid=None):
95        """
96        Start a sub-experiment on a federant.
97
98        Get the current state, and terminate the experiment if it exists. The
99        group membership of the experiment is difficult to determine or change,
100        so start with a clean slate.  Create a new one and ship data
101        and configs and start the experiment.  There are small ordering
102        differences based on the initial state of the sub-experiment.
103        """
104
105        state = self.get_state(pid, eid)
106
107        if state != 'none':
108            self.terminate_exp(pid, eid)
109
110        if not self.make_null_experiment(pid, eid, tmpdir, gid):
111            return False
112
113        if not self.set_up_experiment_filespace(user, self.ops, 
114                pid, eid, tmpdir):
115            return False
116
117        # Put the file into a string to pass to emulab.
118        try:
119            tcl = "".join([ l for l in open(tclfile,"r")])
120        except EnvironmentError, e:
121            self.log.error("Can't read %s: %s" % (tclfile, e))
122            return False
123       
124        # Stage the new configuration
125        if not self.modify_exp(pid, eid, tcl):
126            self.log.error("modify failed")
127            return False
128
129        if not self.swap_exp(pid, eid, 'in'):
130            self.log.error("swap in failed")
131            return False
132        # Everything has gone OK.
133        self.get_mapping(pid,eid)
134        return True
135
136class stop_segment(ssh_emulab_segment, xmlrpc_emulab_segment):
137    def __init__(self, log=None, keyfile=None, debug=False, boss=None, 
138            ops=None, cert=None):
139        ssh_emulab_segment.__init__(self, log=log, keyfile=keyfile, debug=debug)
140        xmlrpc_emulab_segment.__init__(self, boss=boss, ops=ops, cert=cert)
141
142    def __call__(self, parent, user, pid, eid, gid=None, terminate=False):
143        """
144        Stop a sub experiment by calling swapexp on the federant
145        """
146
147        self.log.info("[stop_segment]: Stopping %s" % eid)
148        rv = False
149        try:
150            # Clean out tar files: we've gone over quota in the past
151            self.ssh_cmd(user, self.ops, 
152                    "rm -rf /proj/%s/software/%s" % (pid, eid))
153            rv = self.swap_exp(pid, eid, 'out')
154            if terminate:
155                rv = self.terminate_exp(pid, eid)
156        except self.ssh_cmd_timeout:
157            rv = False
158        return rv
159
160class info_segment(ssh_emulab_segment, xmlrpc_emulab_segment):
161    def __init__(self, log=None, keyfile=None, debug=False, boss=None, 
162            ops=None, cert=None):
163        ssh_emulab_segment.__init__(self, log=log, keyfile=keyfile, debug=debug)
164        xmlrpc_emulab_segment.__init__(self, boss=boss, ops=ops, cert=cert)
165
166    def __call__(self, parent, user, pid, eid):
167        self.log.info("[info_segment]: Getting info from %s" % eid)
168        self.get_mapping(pid,eid)
169        return True
170
171class operation_segment(ssh_emulab_segment, xmlrpc_emulab_segment):
172    def __init__(self, log=None, keyfile=None, debug=False, boss=None, 
173            ops=None, cert=None):
174        ssh_emulab_segment.__init__(self, log=log, keyfile=keyfile, debug=debug)
175        xmlrpc_emulab_segment.__init__(self, boss=boss, ops=ops, cert=cert)
176
177    def __call__(self, parent, op, targets, param, top):
178        for l, p in targets.items():
179            self.log.info("[operation_segment]: Calling op %s on %s(%s)" % \
180                    (op, l,p))
181            self.do_operation(op, l, p, param, top)
182        return True
183
184class exports_segment(ssh_emulab_segment, xmlrpc_emulab_segment):
185    '''
186    Class to export parameters from this segment.  For a standard segment these
187    are calculated, so there is no testbed interaction.  For shared-NAT DETER
188    installations, this is more involved.  This class is a hook for that more
189    involved setup.
190    '''
191    def __init__(self, log=None, keyfile=None, debug=False, boss=None,
192            ops=None, cert=None):
193        ssh_emulab_segment.__init__(self, log=log, keyfile=keyfile, debug=debug)
194        xmlrpc_emulab_segment.__init__(self, boss=boss, ops=ops, cert=cert)
195
196    def __call__(self, parent, eid, pid, user, connInfo, tmpdir, timeout=0,
197            gid=None):
198        '''
199        Install the parameter values into each output parameter in each
200        connInfo entry.  This class understands peer and ssh_port output
201        variables.
202        '''
203
204        for c in connInfo:
205            for p in c.get('parameter', []):
206                # Only set output parameters
207                if  p.get('type', '') != 'output':
208                    continue
209                name = p.get('name', '')
210                # Debugging output
211                k = p.get('key', None)
212                if name == 'peer':
213                    if parent.nat_portal:
214                        value = parent.nat_portal
215                    elif k and k.index('/') != -1:
216                        value = "%s.%s.%s%s" % \
217                            (k[k.index('/')+1:], eid, pid, parent.domain)
218                    else:
219                        self.log.error("Bad export request: %s" % p)
220                        continue
221                    p['value'] = value
222                    self.log.debug("Assigning %s to %s" % (k, value))
223                elif name == 'ssh_port':
224                    value = parent.ssh_port
225                    p['value'] = value
226                    self.log.debug("Assigning %s to %s" % (k, value))
227                else:
228                    self.log.error("Unknown export parameter: %s" % \
229                            p.get('name'))
230                    continue
231        return True
Note: See TracBrowser for help on using the repository browser.