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

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

Setup hosting for static resources in the InvirtWeb? and
InvirtUnauthWeb? classes, instead of in the fcgi configuration.

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