4566522426259260f3cacd16e76d2b788cede34f
[plcapi.git] / PLC / Methods / GetDummyBoxMedium.py
1 #
2 # Marta Carbone - UniPi
3 # $Id$
4 #
5 # This class requires the rpm package containing
6 # the picobsd image to be installed
7 # on the Central Site system.
8 #
9
10 import base64
11 import os
12 import datetime
13
14 from PLC.Faults import *                          # faults library
15 from PLC.Method import Method                     # base class for methods
16 from PLC.Parameter import Parameter               # use the Parameter wrapper
17 from PLC.Auth import Auth                         # import the Auth parameter
18 from PLC.Nodes import *                           # nodes functions
19 from PLC.Methods.GetBootMedium import compute_key # key generation function
20 from PLC.Accessors.Accessors_dummynetbox import * # import dummynet accessors
21
22 WORK_DIR = "/var/tmp/DummynetBoxMedium"
23 BASE_IMAGE = "/usr/share/dummynet/picobsd"
24
25 class GetDummyBoxMedium(Method):
26     """
27     This method is used to fetch a boot image for the DummyNetBox
28     with its configuration file embedded.
29
30     Every time this method is called, a new key is generated in the configuration file,
31     SO THIS OPERATION WILL INVALIDATE ANY PREVIOUSLY DUMMYNETBOX BOOT MEDIUM.
32
33     This method updates the key fields in the database.
34     It returns a base64-encoded boot image.
35     """
36     # I added the session role, because this method should be called from the web
37     roles = ['admin', 'pi', 'tech', 'session']
38
39     accepts = [
40         Auth(),
41         Parameter(int, "The dummybox_id"),
42         Parameter(str, "The image type (bin or iso)")
43     ]
44
45     returns = Parameter(str, "DummynetBox boot medium")
46
47     # XXX add checks for picobsd.bin existence
48
49     # Generate a new configuration file in the working directory
50     # input parameters follows:
51     # self is used to access instance data,
52     # dummybox is the dummybox_id,
53     # new_key is the new generated key,
54     # configfile is the output file of the configuration.
55     def generate_conf_file(self, dummybox, new_key, configfile):
56         
57         # Generate the dummynet box configuration file
58         today = datetime.date.today()
59         file = ""
60         file += "# This is the dummynetbox configuration file\n"
61         file += "# and was generated on %s\n" % str(today)
62         
63         host_domain = dummybox['hostname']
64         host_domain = host_domain.split('.', 1)
65         file += 'HOST_NAME="%s"\n' % host_domain[0]
66         file += 'DOMAIN_NAME="%s"\n' % host_domain[1]
67         
68         file += 'IP_ADDRESS="%s"\n' % dummybox['ip']
69         file += 'IP_NETMASK="%s"\n' % dummybox['netmask']
70         file += 'IP_GATEWAY="%s"\n' % dummybox['gateway']
71         file += 'IP_DNS1="%s"\n' % dummybox['dns1']
72         file += 'IP_DNS2="%s"\n' % dummybox['dns2']
73         file += 'DUMMYBOX_ID="%s"\n' % dummybox['node_id']
74         file += 'DUMMYBOX_KEY="%s"\n' % new_key
75         
76         file += 'CS_IP="%s"\n' % self.api.config.PLC_API_HOST
77
78         # write the configuration file
79         # WORK_DIR must be writable
80         FILE = open(configfile, "w")
81         FILE.write(file)
82         FILE.close()
83         
84         return
85         
86     # Here starts the execution of the call
87     def call(self, auth, dummybox_id, type):
88
89         # check for dummynet box existence and get dummyboxes information
90         dummybox_info = Nodes(self.api, {'node_id':dummybox_id, 'node_type':'dummynet'}, \
91                 ['hostname', 'interface_ids'])
92  
93         if dummybox_id != None and not dummybox_info:
94             raise PLCInvalidArgument, "No such DummyBox %s" % dummybox_id
95
96         # Get the dummynet box hostname
97         dummybox_hostname = dummybox_info[0]['hostname']
98         print "dummybox hostname %s" % dummybox_hostname
99
100         # get the node interface, if configured
101         interfaces = Interfaces(self.api, dummybox_info[0]['interface_ids'])
102         for i in interfaces:
103             if i['is_primary']:
104                 interface_info = i
105                 break
106
107         if not interface_info:
108             raise PLCInvalidArgument, "No primary network configured on %s" % dummybox_hostname
109
110         dummybox = interface_info
111         dummybox['hostname']=dummybox_hostname
112
113         # Select the base image, default to bin image
114         if type != 'iso':
115                 type="bin"
116         IMAGE_NAME = str(WORK_DIR) + "/dummybox_" + dummybox_hostname + str(type)
117         BASE_IMAGE = "/usr/share/dummynet/picobsd." + str(type)
118         configfile = WORK_DIR + '/dummybox.conf'
119         lockfile =  WORK_DIR + '/lockfile'
120
121         # Permission checks
122         assert self.caller is not None
123
124         # Start the generation of the image
125         # Generate a new key
126         new_key = compute_key()
127
128         # create working dir and lock file for concurrent runs
129         if not os.path.exists(WORK_DIR):
130             print "Creating working directory %s" % WORK_DIR
131             os.mkdir(WORK_DIR)
132
133         if os.path.exists(lockfile):
134             raise PLCInvalidArgument, "Lockfile %s exist, try again " % lockfile
135         else:
136             print "Executing "+"touch %s" % lockfile
137             os.system("touch %s" % lockfile)
138
139         # generate the configuration file
140         conf_file = self.generate_conf_file(dummybox, new_key, configfile)
141
142         # build the shell script to customize the dummynetbox image
143         # copy the raw file and find the configuration file position
144         shell_script = "(cp %s %s; export MATCH=`grep -abo START_USER_DATA %s | cut -d: -f1`; " \
145                            % (BASE_IMAGE, IMAGE_NAME, IMAGE_NAME)
146
147         # set permission file
148         shell_script += " chmod u+w %s; chmod u+w %s; " % (IMAGE_NAME, configfile)
149
150         # cat the configuration file in the raw image
151         shell_script += " cat %s | dd of=%s seek=$MATCH conv=notrunc bs=1)" \
152                            % (configfile, IMAGE_NAME)
153
154         # check returned values, 0 means OK, remove the lock file
155         os.system(shell_script)
156         os.system("rm %s" % (lockfile))
157
158         # if all goes fine store the key in the database
159         nodes = Nodes(self.api, dummybox_id)
160         if not nodes:
161             raise PLCInvalidArgument, "No such node %r"%node_id_or_hostname
162         nodes[0]['key'] = new_key
163         nodes.sync()
164
165         # return the file's content base64-encoded
166         result = file(IMAGE_NAME).read()
167         os.unlink(IMAGE_NAME)
168         return base64.b64encode(result)