source: package_branches/invirt-web/cherrypy-rebased/code/main.py @ 2705

Last change on this file since 2705 was 2705, checked in by broder, 14 years ago

tighten a bit of code

  • Property svn:executable set to *
File size: 25.5 KB
RevLine 
[113]1#!/usr/bin/python
[205]2"""Main CGI script for web interface"""
[113]3
[205]4import base64
5import cPickle
[113]6import cgi
[205]7import datetime
8import hmac
[770]9import random
[205]10import sha
11import sys
[118]12import time
[447]13import urllib
[2186]14import socket
[2663]15import cherrypy
[2693]16from cherrypy import _cperror
[205]17from StringIO import StringIO
18
19def printError():
20    """Revert stderr to stdout, and print the contents of stderr"""
21    if isinstance(sys.stderr, StringIO):
22        print revertStandardError()
23
24if __name__ == '__main__':
25    import atexit
26    atexit.register(printError)
27
[209]28import validation
[446]29import cache_acls
[1612]30from webcommon import State
[209]31import controls
[632]32from getafsgroups import getAfsGroupMembers
[865]33from invirt import database
[1001]34from invirt.database import Machine, CDROM, session, connect, MachineAccess, Type, Autoinstall
[863]35from invirt.config import structs as config
[1612]36from invirt.common import InvalidInput, CodeError
[113]37
[2693]38from view import View, revertStandardError
[2664]39
[2692]40class InvirtUnauthWeb(View):
41    @cherrypy.expose
42    @cherrypy.tools.mako(filename="/unauth.mako")
43    def index(self):
44        return {'simple': True}
45
[2664]46class InvirtWeb(View):
47    def __init__(self):
48        super(self.__class__,self).__init__()
49        connect()
[2665]50        self._cp_config['tools.require_login.on'] = True
[2693]51        self._cp_config['tools.catch_stderr.on'] = True
[2674]52        self._cp_config['tools.mako.imports'] = ['from invirt.config import structs as config',
53                                                 'from invirt import database']
[2693]54        self._cp_config['request.error_response'] = self.handle_error
[2664]55
[2693]56    @cherrypy.expose
57    @cherrypy.tools.mako(filename="/invalid.mako")
58    def invalidInput(self):
59        """Print an error page when an InvalidInput exception occurs"""
60        err = cherrypy.request.prev.params["err"]
61        emsg = cherrypy.request.prev.params["emsg"]
62        d = dict(err_field=err.err_field,
63                 err_value=str(err.err_value), stderr=emsg,
64                 errorMessage=str(err))
65        return d
66
67    @cherrypy.expose
68    @cherrypy.tools.mako(filename="/error.mako")
69    def error(self):
70        """Print an error page when an exception occurs"""
71        op = cherrypy.request.prev.path_info
72        username = cherrypy.request.login
73        err = cherrypy.request.prev.params["err"]
74        emsg = cherrypy.request.prev.params["emsg"]
75        traceback = cherrypy.request.prev.params["traceback"]
[2704]76        d = dict(op=op, user=username, fields=cherrypy.request.prev.params,
[2693]77                 errorMessage=str(err), stderr=emsg, traceback=traceback)
[2694]78        error_raw = cherrypy.request.lookup.get_template("/error_raw.mako")
[2693]79        details = error_raw.render(**d)
80        exclude = config.web.errormail_exclude
81        if username not in exclude and '*' not in exclude:
82            send_error_mail('xvm error on %s for %s: %s' % (op, cherrypy.request.login, err),
83                            details)
84        d['details'] = details
85        return d
86
[2690]87    def __getattr__(self, name):
88        if name in ("admin", "overlord"):
89            if not cherrypy.request.login in getAfsGroupMembers(config.adminacl, config.authz[0].cell):
90                raise InvalidInput('username', cherrypy.request.login,
91                                   'Not in admin group %s.' % config.adminacl)
92            cherrypy.request.state = State(cherrypy.request.login, isadmin=True)
93            return self
94        else:
95            return super(InvirtWeb, self).__getattr__(name)
[2674]96
[2693]97    def handle_error(self):
98        err = sys.exc_info()[1]
99        if isinstance(err, InvalidInput):
100            cherrypy.request.params['err'] = err
[2705]101            cherrypy.request.params['emsg'] = revertStandardError()
[2693]102            raise cherrypy.InternalRedirect('/invalidInput')
103        if not cherrypy.request.prev or 'err' not in cherrypy.request.prev.params:
104            cherrypy.request.params['err'] = err
[2705]105            cherrypy.request.params['emsg'] = revertStandardError()
[2693]106            cherrypy.request.params['traceback'] = _cperror.format_exc()
107            raise cherrypy.InternalRedirect('/error')
108        # fall back to cherrypy default error page
109        cherrypy.HTTPError(500).set_response()
110
[2664]111    @cherrypy.expose
[2665]112    @cherrypy.tools.mako(filename="/list.mako")
[2684]113    def list(self, result=None):
[2665]114        """Handler for list requests."""
115        checkpoint.checkpoint('Getting list dict')
[2669]116        d = getListDict(cherrypy.request.login, cherrypy.request.state)
[2684]117        if result is not None:
118            d['result'] = result
[2665]119        checkpoint.checkpoint('Got list dict')
[2668]120        return d
[2665]121    index=list
122
123    @cherrypy.expose
[2676]124    @cherrypy.tools.mako(filename="/help.mako")
125    def help(self, subject=None, simple=False):
126        """Handler for help messages."""
127
128        help_mapping = {
129            'Autoinstalls': """
130The autoinstaller builds a minimal Debian or Ubuntu system to run as a
131ParaVM.  You can access the resulting system by logging into the <a
132href="help?simple=true&subject=ParaVM+Console">serial console server</a>
133with your Kerberos tickets; there is no root password so sshd will
134refuse login.</p>
135
136<p>Under the covers, the autoinstaller uses our own patched version of
137xen-create-image, which is a tool based on debootstrap.  If you log
138into the serial console while the install is running, you can watch
139it.
140""",
141            'ParaVM Console': """
142ParaVM machines do not support local console access over VNC.  To
143access the serial console of these machines, you can SSH with Kerberos
144to %s, using the name of the machine as your
145username.""" % config.console.hostname,
146            'HVM/ParaVM': """
147HVM machines use the virtualization features of the processor, while
148ParaVM machines rely on a modified kernel to communicate directly with
149the hypervisor.  HVMs support boot CDs of any operating system, and
150the VNC console applet.  The three-minute autoinstaller produces
151ParaVMs.  ParaVMs typically are more efficient, and always support the
152<a href="help?subject=ParaVM+Console">console server</a>.</p>
153
154<p>More details are <a
155href="https://xvm.scripts.mit.edu/wiki/Paravirtualization">on the
156wiki</a>, including steps to prepare an HVM guest to boot as a ParaVM
157(which you can skip by using the autoinstaller to begin with.)</p>
158
159<p>We recommend using a ParaVM when possible and an HVM when necessary.
160""",
161            'CPU Weight': """
162Don't ask us!  We're as mystified as you are.""",
163            'Owner': """
164The owner field is used to determine <a
165href="help?subject=Quotas">quotas</a>.  It must be the name of a
166locker that you are an AFS administrator of.  In particular, you or an
167AFS group you are a member of must have AFS rlidwka bits on the
168locker.  You can check who administers the LOCKER locker using the
169commands 'attach LOCKER; fs la /mit/LOCKER' on Athena.)  See also <a
170href="help?subject=Administrator">administrator</a>.""",
171            'Administrator': """
172The administrator field determines who can access the console and
173power on and off the machine.  This can be either a user or a moira
174group.""",
175            'Quotas': """
176Quotas are determined on a per-locker basis.  Each locker may have a
177maximum of 512 mebibytes of active ram, 50 gibibytes of disk, and 4
178active machines.""",
179            'Console': """
180<strong>Framebuffer:</strong> At a Linux boot prompt in your VM, try
181setting <tt>fb=false</tt> to disable the framebuffer.  If you don't,
182your machine will run just fine, but the applet's display of the
183console will suffer artifacts.
184""",
185            'Windows': """
186<strong>Windows Vista:</strong> The Vista image is licensed for all MIT students and will automatically activate off the network; see <a href="/static/msca-email.txt">the licensing confirmation e-mail</a> for details. The installer requires 512 MiB RAM and at least 7.5 GiB disk space (15 GiB or more recommended).<br>
[2686]187<strong>Windows XP:</strong> This is the volume license CD image. You will need your own volume license key to complete the install. We do not have these available for the general MIT community; ask your department if they have one, or visit <a href="http://msca.mit.edu/">http://msca.mit.edu/</a> if you are staff/faculty to request one.
[2676]188"""
189            }
190
191        if not subject:
192            subject = sorted(help_mapping.keys())
193        if not isinstance(subject, list):
194            subject = [subject]
195
[2677]196        return dict(simple=simple,
[2676]197                    subjects=subject,
198                    mapping=help_mapping)
199    help._cp_config['tools.require_login.on'] = False
200
[2685]201    def parseCreate(self, fields):
[2687]202        kws = dict([(kw, fields.get(kw)) for kw in 'name description owner memory disksize vmtype cdrom autoinstall'.split() if fields.get(kw)])
[2685]203        validate = validation.Validate(cherrypy.request.login, cherrypy.request.state, strict=True, **kws)
204        return dict(contact=cherrypy.request.login, name=validate.name, description=validate.description, memory=validate.memory,
205                    disksize=validate.disksize, owner=validate.owner, machine_type=getattr(validate, 'vmtype', Defaults.type),
206                    cdrom=getattr(validate, 'cdrom', None),
207                    autoinstall=getattr(validate, 'autoinstall', None))
208
[2676]209    @cherrypy.expose
[2685]210    @cherrypy.tools.mako(filename="/list.mako")
211    @cherrypy.tools.require_POST()
212    def create(self, **fields):
213        """Handler for create requests."""
214        try:
215            parsed_fields = self.parseCreate(fields)
216            machine = controls.createVm(cherrypy.request.login, cherrypy.request.state, **parsed_fields)
217        except InvalidInput, err:
218            pass
219        else:
220            err = None
221        cherrypy.request.state.clear() #Changed global state
222        d = getListDict(cherrypy.request.login, cherrypy.request.state)
223        d['err'] = err
224        if err:
225            for field in fields.keys():
226                setattr(d['defaults'], field, fields.get(field))
227        else:
228            d['new_machine'] = parsed_fields['name']
229        return d
230
231    @cherrypy.expose
[2665]232    @cherrypy.tools.mako(filename="/helloworld.mako")
[2675]233    def helloworld(self, **kwargs):
234        return {'request': cherrypy.request, 'kwargs': kwargs}
[2665]235    helloworld._cp_config['tools.require_login.on'] = False
[2664]236
[2689]237    @cherrypy.expose
238    def errortest(self):
239        """Throw an error, to test the error-tracing mechanisms."""
[2693]240        print >>sys.stderr, "look ma, it's a stderr"
[2689]241        raise RuntimeError("test of the emergency broadcast system")
242
[2678]243    class MachineView(View):
244        # This is hairy. Fix when CherryPy 3.2 is out. (rename to
245        # _cp_dispatch, and parse the argument as a list instead of
246        # string
247
248        def __getattr__(self, name):
249            try:
250                machine_id = int(name)
251                cherrypy.request.params['machine_id'] = machine_id
252                return self
253            except ValueError:
254                return None
255
256        @cherrypy.expose
257        @cherrypy.tools.mako(filename="/info.mako")
258        def info(self, machine_id):
259            """Handler for info on a single VM."""
260            machine = validation.Validate(cherrypy.request.login, cherrypy.request.state, machine_id=machine_id).machine
261            d = infoDict(cherrypy.request.login, cherrypy.request.state, machine)
262            checkpoint.checkpoint('Got infodict')
263            return d
264        index = info
265
[2680]266        @cherrypy.expose
[2691]267        @cherrypy.tools.mako(filename="/info.mako")
268        @cherrypy.tools.require_POST()
269        def modify(self, machine_id, **fields):
270            """Handler for modifying attributes of a machine."""
271            try:
272                modify_dict = modifyDict(cherrypy.request.login, cherrypy.request.state, machine_id, fields)
273            except InvalidInput, err:
274                result = None
275                machine = validation.Validate(cherrypy.request.login, cherrypy.request.state, machine_id=machine_id).machine
276            else:
277                machine = modify_dict['machine']
278                result = 'Success!'
279                err = None
280            info_dict = infoDict(cherrypy.request.login, cherrypy.request.state, machine)
281            info_dict['err'] = err
282            if err:
283                for field in fields.keys():
284                    setattr(info_dict['defaults'], field, fields.get(field))
285            info_dict['result'] = result
286            return info_dict
287
288        @cherrypy.expose
[2680]289        @cherrypy.tools.mako(filename="/vnc.mako")
290        def vnc(self, machine_id):
291            """VNC applet page.
292
293            Note that due to same-domain restrictions, the applet connects to
294            the webserver, which needs to forward those requests to the xen
295            server.  The Xen server runs another proxy that (1) authenticates
296            and (2) finds the correct port for the VM.
297
298            You might want iptables like:
299
300            -t nat -A PREROUTING -s ! 18.181.0.60 -i eth1 -p tcp -m tcp \
301            --dport 10003 -j DNAT --to-destination 18.181.0.60:10003
302            -t nat -A POSTROUTING -d 18.181.0.60 -o eth1 -p tcp -m tcp \
303            --dport 10003 -j SNAT --to-source 18.187.7.142
304            -A FORWARD -d 18.181.0.60 -i eth1 -o eth1 -p tcp -m tcp \
305            --dport 10003 -j ACCEPT
306
307            Remember to enable iptables!
308            echo 1 > /proc/sys/net/ipv4/ip_forward
309            """
310            machine = validation.Validate(cherrypy.request.login, cherrypy.request.state, machine_id=machine_id).machine
311
312            token = controls.vnctoken(machine)
313            host = controls.listHost(machine)
314            if host:
315                port = 10003 + [h.hostname for h in config.hosts].index(host)
316            else:
317                port = 5900 # dummy
318
319            status = controls.statusInfo(machine)
320            has_vnc = hasVnc(status)
321
322            d = dict(on=status,
323                     has_vnc=has_vnc,
324                     machine=machine,
325                     hostname=cherrypy.request.local.name,
326                     port=port,
327                     authtoken=token)
328            return d
[2684]329        @cherrypy.expose
330        @cherrypy.tools.mako(filename="/command.mako")
[2685]331        @cherrypy.tools.require_POST()
[2684]332        def command(self, command_name, machine_id, **kwargs):
333            """Handler for running commands like boot and delete on a VM."""
334            back = kwargs.get('back', None)
335            try:
336                d = controls.commandResult(cherrypy.request.login, cherrypy.request.state, command_name, machine_id, kwargs)
337                if d['command'] == 'Delete VM':
338                    back = 'list'
339            except InvalidInput, err:
340                if not back:
341                    raise
342                print >> sys.stderr, err
[2693]343                result = str(err)
[2684]344            else:
345                result = 'Success!'
346                if not back:
347                    return d
348            if back == 'list':
349                cherrypy.request.state.clear() #Changed global state
350                raise cherrypy.InternalRedirect('/list?result=%s' % urllib.quote(result))
351            elif back == 'info':
352                raise cherrypy.HTTPRedirect(cherrypy.request.base + '/machine/%d/' % machine_id, status=303)
353            else:
354                raise InvalidInput('back', back, 'Not a known back page.')
[2680]355
[2678]356    machine = MachineView()
357
[235]358class Checkpoint:
359    def __init__(self):
360        self.start_time = time.time()
361        self.checkpoints = []
362
363    def checkpoint(self, s):
364        self.checkpoints.append((s, time.time()))
365
366    def __str__(self):
367        return ('Timing info:\n%s\n' %
368                '\n'.join(['%s: %s' % (d, t - self.start_time) for
369                           (d, t) in self.checkpoints]))
370
371checkpoint = Checkpoint()
372
[205]373class Defaults:
374    """Class to store default values for fields."""
375    memory = 256
376    disk = 4.0
377    cdrom = ''
[443]378    autoinstall = ''
[205]379    name = ''
[609]380    description = ''
[2691]381    administrator = ''
[515]382    type = 'linux-hvm'
383
[205]384    def __init__(self, max_memory=None, max_disk=None, **kws):
385        if max_memory is not None:
386            self.memory = min(self.memory, max_memory)
387        if max_disk is not None:
[1964]388            self.disk = min(self.disk, max_disk)
[205]389        for key in kws:
390            setattr(self, key, kws[key])
391
[119]392def hasVnc(status):
[133]393    """Does the machine with a given status list support VNC?"""
[119]394    if status is None:
395        return False
396    for l in status:
397        if l[0] == 'device' and l[1][0] == 'vfb':
398            d = dict(l[1][1:])
399            return 'location' in d
400    return False
401
[134]402
[572]403def getListDict(username, state):
[438]404    """Gets the list of local variables used by list.tmpl."""
[535]405    checkpoint.checkpoint('Starting')
[572]406    machines = state.machines
[235]407    checkpoint.checkpoint('Got my machines')
[133]408    on = {}
[119]409    has_vnc = {}
[2687]410    installing = {}
[572]411    xmlist = state.xmlist
[235]412    checkpoint.checkpoint('Got uptimes')
[136]413    for m in machines:
[535]414        if m not in xmlist:
[144]415            has_vnc[m] = 'Off'
[535]416            m.uptime = None
[136]417        else:
[535]418            m.uptime = xmlist[m]['uptime']
419            if xmlist[m]['console']:
420                has_vnc[m] = True
421            elif m.type.hvm:
422                has_vnc[m] = "WTF?"
423            else:
[2679]424                has_vnc[m] = "ParaVM"
[2687]425            if xmlist[m].get('autoinstall'):
426                installing[m] = True
427            else:
428                installing[m] = False
[572]429    max_memory = validation.maxMemory(username, state)
430    max_disk = validation.maxDisk(username)
[235]431    checkpoint.checkpoint('Got max mem/disk')
[205]432    defaults = Defaults(max_memory=max_memory,
433                        max_disk=max_disk,
[1739]434                        owner=username)
[235]435    checkpoint.checkpoint('Got defaults')
[424]436    def sortkey(machine):
[572]437        return (machine.owner != username, machine.owner, machine.name)
[424]438    machines = sorted(machines, key=sortkey)
[572]439    d = dict(user=username,
440             cant_add_vm=validation.cantAddVm(username, state),
[205]441             max_memory=max_memory,
[144]442             max_disk=max_disk,
[205]443             defaults=defaults,
[113]444             machines=machines,
[540]445             has_vnc=has_vnc,
[2687]446             installing=installing)
[205]447    return d
[113]448
[252]449def getHostname(nic):
[438]450    """Find the hostname associated with a NIC.
451
452    XXX this should be merged with the similar logic in DNS and DHCP.
453    """
[1976]454    if nic.hostname:
455        hostname = nic.hostname
[252]456    elif nic.machine:
[1976]457        hostname = nic.machine.name
[252]458    else:
459        return None
[1976]460    if '.' in hostname:
461        return hostname
462    else:
463        return hostname + '.' + config.dns.domains[0]
[252]464
[133]465def getNicInfo(data_dict, machine):
[145]466    """Helper function for info, get data on nics for a machine.
467
468    Modifies data_dict to include the relevant data, and returns a list
469    of (key, name) pairs to display "name: data_dict[key]" to the user.
470    """
[133]471    data_dict['num_nics'] = len(machine.nics)
[227]472    nic_fields_template = [('nic%s_hostname', 'NIC %s Hostname'),
[133]473                           ('nic%s_mac', 'NIC %s MAC Addr'),
474                           ('nic%s_ip', 'NIC %s IP'),
475                           ]
476    nic_fields = []
477    for i in range(len(machine.nics)):
478        nic_fields.extend([(x % i, y % i) for x, y in nic_fields_template])
[1976]479        data_dict['nic%s_hostname' % i] = getHostname(machine.nics[i])
[133]480        data_dict['nic%s_mac' % i] = machine.nics[i].mac_addr
481        data_dict['nic%s_ip' % i] = machine.nics[i].ip
482    if len(machine.nics) == 1:
483        nic_fields = [(x, y.replace('NIC 0 ', '')) for x, y in nic_fields]
484    return nic_fields
485
486def getDiskInfo(data_dict, machine):
[145]487    """Helper function for info, get data on disks for a machine.
488
489    Modifies data_dict to include the relevant data, and returns a list
490    of (key, name) pairs to display "name: data_dict[key]" to the user.
491    """
[133]492    data_dict['num_disks'] = len(machine.disks)
493    disk_fields_template = [('%s_size', '%s size')]
494    disk_fields = []
495    for disk in machine.disks:
496        name = disk.guest_device_name
[438]497        disk_fields.extend([(x % name, y % name) for x, y in
[205]498                            disk_fields_template])
[211]499        data_dict['%s_size' % name] = "%0.1f GiB" % (disk.size / 1024.)
[133]500    return disk_fields
501
[2691]502def modifyDict(username, state, machine_id, fields):
[438]503    """Modify a machine as specified by CGI arguments.
504
[2691]505    Return a dict containing the machine that was modified.
[438]506    """
[177]507    olddisk = {}
[1013]508    session.begin()
[161]509    try:
[2691]510        kws = dict([(kw, fields.get(kw)) for kw in 'owner admin contact name description memory vmtype disksize'.split() if fields.get(kw)])
511        kws['machine_id'] = machine_id
[572]512        validate = validation.Validate(username, state, **kws)
513        machine = validate.machine
[161]514        oldname = machine.name
[153]515
[572]516        if hasattr(validate, 'memory'):
517            machine.memory = validate.memory
[438]518
[572]519        if hasattr(validate, 'vmtype'):
520            machine.type = validate.vmtype
[440]521
[572]522        if hasattr(validate, 'disksize'):
523            disksize = validate.disksize
[177]524            disk = machine.disks[0]
525            if disk.size != disksize:
526                olddisk[disk.guest_device_name] = disksize
527                disk.size = disksize
[1013]528                session.save_or_update(disk)
[438]529
[446]530        update_acl = False
[572]531        if hasattr(validate, 'owner') and validate.owner != machine.owner:
532            machine.owner = validate.owner
[446]533            update_acl = True
[572]534        if hasattr(validate, 'name'):
[586]535            machine.name = validate.name
[1977]536            for n in machine.nics:
537                if n.hostname == oldname:
538                    n.hostname = validate.name
[609]539        if hasattr(validate, 'description'):
540            machine.description = validate.description
[572]541        if hasattr(validate, 'admin') and validate.admin != machine.administrator:
542            machine.administrator = validate.admin
[446]543            update_acl = True
[572]544        if hasattr(validate, 'contact'):
545            machine.contact = validate.contact
[438]546
[1013]547        session.save_or_update(machine)
[446]548        if update_acl:
549            cache_acls.refreshMachine(machine)
[1013]550        session.commit()
[161]551    except:
[1013]552        session.rollback()
[163]553        raise
[177]554    for diskname in olddisk:
[209]555        controls.resizeDisk(oldname, diskname, str(olddisk[diskname]))
[572]556    if hasattr(validate, 'name'):
557        controls.renameMachine(machine, oldname, validate.name)
[2691]558    return dict(machine=machine)
[438]559
[579]560def infoDict(username, state, machine):
[438]561    """Get the variables used by info.tmpl."""
[209]562    status = controls.statusInfo(machine)
[235]563    checkpoint.checkpoint('Getting status info')
[133]564    has_vnc = hasVnc(status)
565    if status is None:
566        main_status = dict(name=machine.name,
567                           memory=str(machine.memory))
[205]568        uptime = None
569        cputime = None
[133]570    else:
571        main_status = dict(status[1:])
[662]572        main_status['host'] = controls.listHost(machine)
[167]573        start_time = float(main_status.get('start_time', 0))
574        uptime = datetime.timedelta(seconds=int(time.time()-start_time))
575        cpu_time_float = float(main_status.get('cpu_time', 0))
576        cputime = datetime.timedelta(seconds=int(cpu_time_float))
[235]577    checkpoint.checkpoint('Status')
[133]578    display_fields = [('name', 'Name'),
[609]579                      ('description', 'Description'),
[133]580                      ('owner', 'Owner'),
[187]581                      ('administrator', 'Administrator'),
[133]582                      ('contact', 'Contact'),
[136]583                      ('type', 'Type'),
[133]584                      'NIC_INFO',
585                      ('uptime', 'uptime'),
586                      ('cputime', 'CPU usage'),
[662]587                      ('host', 'Hosted on'),
[133]588                      ('memory', 'RAM'),
589                      'DISK_INFO',
590                      ('state', 'state (xen format)'),
591                      ]
592    fields = []
593    machine_info = {}
[147]594    machine_info['name'] = machine.name
[609]595    machine_info['description'] = machine.description
[136]596    machine_info['type'] = machine.type.hvm and 'HVM' or 'ParaVM'
[133]597    machine_info['owner'] = machine.owner
[187]598    machine_info['administrator'] = machine.administrator
[133]599    machine_info['contact'] = machine.contact
600
601    nic_fields = getNicInfo(machine_info, machine)
602    nic_point = display_fields.index('NIC_INFO')
[438]603    display_fields = (display_fields[:nic_point] + nic_fields +
[205]604                      display_fields[nic_point+1:])
[133]605
606    disk_fields = getDiskInfo(machine_info, machine)
607    disk_point = display_fields.index('DISK_INFO')
[438]608    display_fields = (display_fields[:disk_point] + disk_fields +
[205]609                      display_fields[disk_point+1:])
[438]610
[211]611    main_status['memory'] += ' MiB'
[133]612    for field, disp in display_fields:
[167]613        if field in ('uptime', 'cputime') and locals()[field] is not None:
[133]614            fields.append((disp, locals()[field]))
[147]615        elif field in machine_info:
616            fields.append((disp, machine_info[field]))
[133]617        elif field in main_status:
618            fields.append((disp, main_status[field]))
619        else:
620            pass
621            #fields.append((disp, None))
[235]622
623    checkpoint.checkpoint('Got fields')
624
625
[572]626    max_mem = validation.maxMemory(machine.owner, state, machine, False)
[235]627    checkpoint.checkpoint('Got mem')
[566]628    max_disk = validation.maxDisk(machine.owner, machine)
[209]629    defaults = Defaults()
[609]630    for name in 'machine_id name description administrator owner memory contact'.split():
[2691]631        if getattr(machine, name):
632            setattr(defaults, name, getattr(machine, name))
[516]633    defaults.type = machine.type.type_id
[205]634    defaults.disk = "%0.2f" % (machine.disks[0].size/1024.)
[235]635    checkpoint.checkpoint('Got defaults')
[572]636    d = dict(user=username,
[133]637             on=status is not None,
638             machine=machine,
[205]639             defaults=defaults,
[133]640             has_vnc=has_vnc,
641             uptime=str(uptime),
642             ram=machine.memory,
[144]643             max_mem=max_mem,
644             max_disk=max_disk,
[133]645             fields = fields)
[205]646    return d
[113]647
[598]648def send_error_mail(subject, body):
649    import subprocess
[205]650
[863]651    to = config.web.errormail
[598]652    mail = """To: %s
[863]653From: root@%s
[598]654Subject: %s
655
656%s
[863]657""" % (to, config.web.hostname, subject, body)
[1718]658    p = subprocess.Popen(['/usr/sbin/sendmail', '-f', to, to],
659                         stdin=subprocess.PIPE)
[598]660    p.stdin.write(mail)
661    p.stdin.close()
662    p.wait()
663
[2693]664random.seed()
Note: See TracBrowser for help on using the repository browser.