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

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

fix a syntax error from my r2526

  • Property svn:executable set to *
File size: 26.2 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):
[2707]202        kws = dict([(kw, fields[kw]) for kw in
[2706]203         'name description owner memory disksize vmtype cdrom autoinstall'.split()
[2707]204                    if fields[kw]])
[2706]205        validate = validation.Validate(cherrypy.request.login,
206                                       cherrypy.request.state,
207                                       strict=True, **kws)
208        return dict(contact=cherrypy.request.login, name=validate.name,
209                    description=validate.description, memory=validate.memory,
210                    disksize=validate.disksize, owner=validate.owner,
211                    machine_type=getattr(validate, 'vmtype', Defaults.type),
[2685]212                    cdrom=getattr(validate, 'cdrom', None),
213                    autoinstall=getattr(validate, 'autoinstall', None))
214
[2676]215    @cherrypy.expose
[2685]216    @cherrypy.tools.mako(filename="/list.mako")
217    @cherrypy.tools.require_POST()
218    def create(self, **fields):
219        """Handler for create requests."""
220        try:
221            parsed_fields = self.parseCreate(fields)
[2706]222            machine = controls.createVm(cherrypy.request.login,
223                                        cherrypy.request.state, **parsed_fields)
[2685]224        except InvalidInput, err:
225            pass
226        else:
227            err = None
228        cherrypy.request.state.clear() #Changed global state
229        d = getListDict(cherrypy.request.login, cherrypy.request.state)
230        d['err'] = err
231        if err:
[2707]232            for field, value in fields.items():
[2711]233                setattr(d['defaults'], field, value)
[2685]234        else:
235            d['new_machine'] = parsed_fields['name']
236        return d
237
238    @cherrypy.expose
[2665]239    @cherrypy.tools.mako(filename="/helloworld.mako")
[2675]240    def helloworld(self, **kwargs):
241        return {'request': cherrypy.request, 'kwargs': kwargs}
[2665]242    helloworld._cp_config['tools.require_login.on'] = False
[2664]243
[2689]244    @cherrypy.expose
245    def errortest(self):
246        """Throw an error, to test the error-tracing mechanisms."""
[2693]247        print >>sys.stderr, "look ma, it's a stderr"
[2689]248        raise RuntimeError("test of the emergency broadcast system")
249
[2678]250    class MachineView(View):
251        # This is hairy. Fix when CherryPy 3.2 is out. (rename to
252        # _cp_dispatch, and parse the argument as a list instead of
253        # string
254
255        def __getattr__(self, name):
256            try:
[2708]257                cherrypy.request.params['machine_id'] = int(name)
[2678]258                return self
259            except ValueError:
260                return None
261
262        @cherrypy.expose
263        @cherrypy.tools.mako(filename="/info.mako")
264        def info(self, machine_id):
265            """Handler for info on a single VM."""
[2706]266            machine = validation.Validate(cherrypy.request.login,
267                                          cherrypy.request.state,
268                                          machine_id=machine_id).machine
[2678]269            d = infoDict(cherrypy.request.login, cherrypy.request.state, machine)
270            checkpoint.checkpoint('Got infodict')
271            return d
272        index = info
273
[2680]274        @cherrypy.expose
[2691]275        @cherrypy.tools.mako(filename="/info.mako")
276        @cherrypy.tools.require_POST()
277        def modify(self, machine_id, **fields):
278            """Handler for modifying attributes of a machine."""
279            try:
[2706]280                modify_dict = modifyDict(cherrypy.request.login,
281                                         cherrypy.request.state,
282                                         machine_id, fields)
[2691]283            except InvalidInput, err:
284                result = None
[2706]285                machine = validation.Validate(cherrypy.request.login,
286                                              cherrypy.request.state,
287                                              machine_id=machine_id).machine
[2691]288            else:
289                machine = modify_dict['machine']
290                result = 'Success!'
291                err = None
[2706]292            info_dict = infoDict(cherrypy.request.login,
293                                 cherrypy.request.state, machine)
[2691]294            info_dict['err'] = err
295            if err:
[2707]296                for field, value in fields.items():
297                    setattr(info_dict['defaults'], field, value)
[2691]298            info_dict['result'] = result
299            return info_dict
300
301        @cherrypy.expose
[2680]302        @cherrypy.tools.mako(filename="/vnc.mako")
303        def vnc(self, machine_id):
304            """VNC applet page.
305
306            Note that due to same-domain restrictions, the applet connects to
307            the webserver, which needs to forward those requests to the xen
308            server.  The Xen server runs another proxy that (1) authenticates
309            and (2) finds the correct port for the VM.
310
311            You might want iptables like:
312
313            -t nat -A PREROUTING -s ! 18.181.0.60 -i eth1 -p tcp -m tcp \
314            --dport 10003 -j DNAT --to-destination 18.181.0.60:10003
315            -t nat -A POSTROUTING -d 18.181.0.60 -o eth1 -p tcp -m tcp \
316            --dport 10003 -j SNAT --to-source 18.187.7.142
317            -A FORWARD -d 18.181.0.60 -i eth1 -o eth1 -p tcp -m tcp \
318            --dport 10003 -j ACCEPT
319
320            Remember to enable iptables!
321            echo 1 > /proc/sys/net/ipv4/ip_forward
322            """
[2706]323            machine = validation.Validate(cherrypy.request.login,
324                                          cherrypy.request.state,
325                                          machine_id=machine_id).machine
[2680]326            token = controls.vnctoken(machine)
327            host = controls.listHost(machine)
328            if host:
329                port = 10003 + [h.hostname for h in config.hosts].index(host)
330            else:
331                port = 5900 # dummy
332
333            status = controls.statusInfo(machine)
334            has_vnc = hasVnc(status)
335
336            d = dict(on=status,
337                     has_vnc=has_vnc,
338                     machine=machine,
339                     hostname=cherrypy.request.local.name,
340                     port=port,
341                     authtoken=token)
342            return d
[2709]343
[2684]344        @cherrypy.expose
345        @cherrypy.tools.mako(filename="/command.mako")
[2685]346        @cherrypy.tools.require_POST()
[2684]347        def command(self, command_name, machine_id, **kwargs):
348            """Handler for running commands like boot and delete on a VM."""
[2709]349            back = kwargs.get('back')
[2684]350            try:
[2706]351                d = controls.commandResult(cherrypy.request.login,
352                                           cherrypy.request.state,
353                                           command_name, machine_id, kwargs)
[2684]354                if d['command'] == 'Delete VM':
355                    back = 'list'
356            except InvalidInput, err:
357                if not back:
358                    raise
359                print >> sys.stderr, err
[2693]360                result = str(err)
[2684]361            else:
362                result = 'Success!'
363                if not back:
364                    return d
365            if back == 'list':
366                cherrypy.request.state.clear() #Changed global state
[2706]367                raise cherrypy.InternalRedirect('/list?result=%s'
368                                                % urllib.quote(result))
[2684]369            elif back == 'info':
[2706]370                raise cherrypy.HTTPRedirect(cherrypy.request.base
371                                            + '/machine/%d/' % machine_id,
372                                            status=303)
[2684]373            else:
374                raise InvalidInput('back', back, 'Not a known back page.')
[2680]375
[2678]376    machine = MachineView()
377
[235]378class Checkpoint:
379    def __init__(self):
380        self.start_time = time.time()
381        self.checkpoints = []
382
383    def checkpoint(self, s):
384        self.checkpoints.append((s, time.time()))
385
386    def __str__(self):
387        return ('Timing info:\n%s\n' %
388                '\n'.join(['%s: %s' % (d, t - self.start_time) for
389                           (d, t) in self.checkpoints]))
390
391checkpoint = Checkpoint()
392
[205]393class Defaults:
394    """Class to store default values for fields."""
395    memory = 256
396    disk = 4.0
397    cdrom = ''
[443]398    autoinstall = ''
[205]399    name = ''
[609]400    description = ''
[2691]401    administrator = ''
[515]402    type = 'linux-hvm'
403
[205]404    def __init__(self, max_memory=None, max_disk=None, **kws):
405        if max_memory is not None:
406            self.memory = min(self.memory, max_memory)
407        if max_disk is not None:
[1964]408            self.disk = min(self.disk, max_disk)
[205]409        for key in kws:
410            setattr(self, key, kws[key])
411
[119]412def hasVnc(status):
[133]413    """Does the machine with a given status list support VNC?"""
[119]414    if status is None:
415        return False
416    for l in status:
417        if l[0] == 'device' and l[1][0] == 'vfb':
418            d = dict(l[1][1:])
419            return 'location' in d
420    return False
421
[134]422
[572]423def getListDict(username, state):
[438]424    """Gets the list of local variables used by list.tmpl."""
[535]425    checkpoint.checkpoint('Starting')
[572]426    machines = state.machines
[235]427    checkpoint.checkpoint('Got my machines')
[133]428    on = {}
[119]429    has_vnc = {}
[2687]430    installing = {}
[572]431    xmlist = state.xmlist
[235]432    checkpoint.checkpoint('Got uptimes')
[136]433    for m in machines:
[535]434        if m not in xmlist:
[144]435            has_vnc[m] = 'Off'
[535]436            m.uptime = None
[136]437        else:
[535]438            m.uptime = xmlist[m]['uptime']
439            if xmlist[m]['console']:
440                has_vnc[m] = True
441            elif m.type.hvm:
442                has_vnc[m] = "WTF?"
443            else:
[2679]444                has_vnc[m] = "ParaVM"
[2687]445            if xmlist[m].get('autoinstall'):
446                installing[m] = True
447            else:
448                installing[m] = False
[572]449    max_memory = validation.maxMemory(username, state)
450    max_disk = validation.maxDisk(username)
[235]451    checkpoint.checkpoint('Got max mem/disk')
[205]452    defaults = Defaults(max_memory=max_memory,
453                        max_disk=max_disk,
[1739]454                        owner=username)
[235]455    checkpoint.checkpoint('Got defaults')
[424]456    def sortkey(machine):
[572]457        return (machine.owner != username, machine.owner, machine.name)
[424]458    machines = sorted(machines, key=sortkey)
[572]459    d = dict(user=username,
460             cant_add_vm=validation.cantAddVm(username, state),
[205]461             max_memory=max_memory,
[144]462             max_disk=max_disk,
[205]463             defaults=defaults,
[113]464             machines=machines,
[540]465             has_vnc=has_vnc,
[2687]466             installing=installing)
[205]467    return d
[113]468
[252]469def getHostname(nic):
[438]470    """Find the hostname associated with a NIC.
471
472    XXX this should be merged with the similar logic in DNS and DHCP.
473    """
[1976]474    if nic.hostname:
475        hostname = nic.hostname
[252]476    elif nic.machine:
[1976]477        hostname = nic.machine.name
[252]478    else:
479        return None
[1976]480    if '.' in hostname:
481        return hostname
482    else:
483        return hostname + '.' + config.dns.domains[0]
[252]484
[133]485def getNicInfo(data_dict, machine):
[145]486    """Helper function for info, get data on nics for a machine.
487
488    Modifies data_dict to include the relevant data, and returns a list
489    of (key, name) pairs to display "name: data_dict[key]" to the user.
490    """
[133]491    data_dict['num_nics'] = len(machine.nics)
[227]492    nic_fields_template = [('nic%s_hostname', 'NIC %s Hostname'),
[133]493                           ('nic%s_mac', 'NIC %s MAC Addr'),
494                           ('nic%s_ip', 'NIC %s IP'),
495                           ]
496    nic_fields = []
497    for i in range(len(machine.nics)):
498        nic_fields.extend([(x % i, y % i) for x, y in nic_fields_template])
[1976]499        data_dict['nic%s_hostname' % i] = getHostname(machine.nics[i])
[133]500        data_dict['nic%s_mac' % i] = machine.nics[i].mac_addr
501        data_dict['nic%s_ip' % i] = machine.nics[i].ip
502    if len(machine.nics) == 1:
503        nic_fields = [(x, y.replace('NIC 0 ', '')) for x, y in nic_fields]
504    return nic_fields
505
506def getDiskInfo(data_dict, machine):
[145]507    """Helper function for info, get data on disks for a machine.
508
509    Modifies data_dict to include the relevant data, and returns a list
510    of (key, name) pairs to display "name: data_dict[key]" to the user.
511    """
[133]512    data_dict['num_disks'] = len(machine.disks)
513    disk_fields_template = [('%s_size', '%s size')]
514    disk_fields = []
515    for disk in machine.disks:
516        name = disk.guest_device_name
[438]517        disk_fields.extend([(x % name, y % name) for x, y in
[205]518                            disk_fields_template])
[211]519        data_dict['%s_size' % name] = "%0.1f GiB" % (disk.size / 1024.)
[133]520    return disk_fields
521
[2691]522def modifyDict(username, state, machine_id, fields):
[438]523    """Modify a machine as specified by CGI arguments.
524
[2691]525    Return a dict containing the machine that was modified.
[438]526    """
[177]527    olddisk = {}
[1013]528    session.begin()
[161]529    try:
[2707]530        kws = dict([(kw, fields[kw]) for kw in
[2706]531         'owner admin contact name description memory vmtype disksize'.split()
[2707]532                    if fields[kw]])
[2691]533        kws['machine_id'] = machine_id
[572]534        validate = validation.Validate(username, state, **kws)
535        machine = validate.machine
[161]536        oldname = machine.name
[153]537
[572]538        if hasattr(validate, 'memory'):
539            machine.memory = validate.memory
[438]540
[572]541        if hasattr(validate, 'vmtype'):
542            machine.type = validate.vmtype
[440]543
[572]544        if hasattr(validate, 'disksize'):
545            disksize = validate.disksize
[177]546            disk = machine.disks[0]
547            if disk.size != disksize:
548                olddisk[disk.guest_device_name] = disksize
549                disk.size = disksize
[1013]550                session.save_or_update(disk)
[438]551
[446]552        update_acl = False
[572]553        if hasattr(validate, 'owner') and validate.owner != machine.owner:
554            machine.owner = validate.owner
[446]555            update_acl = True
[572]556        if hasattr(validate, 'name'):
[586]557            machine.name = validate.name
[1977]558            for n in machine.nics:
559                if n.hostname == oldname:
560                    n.hostname = validate.name
[609]561        if hasattr(validate, 'description'):
562            machine.description = validate.description
[572]563        if hasattr(validate, 'admin') and validate.admin != machine.administrator:
564            machine.administrator = validate.admin
[446]565            update_acl = True
[572]566        if hasattr(validate, 'contact'):
567            machine.contact = validate.contact
[438]568
[1013]569        session.save_or_update(machine)
[446]570        if update_acl:
571            cache_acls.refreshMachine(machine)
[1013]572        session.commit()
[161]573    except:
[1013]574        session.rollback()
[163]575        raise
[177]576    for diskname in olddisk:
[209]577        controls.resizeDisk(oldname, diskname, str(olddisk[diskname]))
[572]578    if hasattr(validate, 'name'):
579        controls.renameMachine(machine, oldname, validate.name)
[2691]580    return dict(machine=machine)
[438]581
[579]582def infoDict(username, state, machine):
[438]583    """Get the variables used by info.tmpl."""
[209]584    status = controls.statusInfo(machine)
[235]585    checkpoint.checkpoint('Getting status info')
[133]586    has_vnc = hasVnc(status)
587    if status is None:
588        main_status = dict(name=machine.name,
589                           memory=str(machine.memory))
[205]590        uptime = None
591        cputime = None
[133]592    else:
593        main_status = dict(status[1:])
[662]594        main_status['host'] = controls.listHost(machine)
[167]595        start_time = float(main_status.get('start_time', 0))
596        uptime = datetime.timedelta(seconds=int(time.time()-start_time))
597        cpu_time_float = float(main_status.get('cpu_time', 0))
598        cputime = datetime.timedelta(seconds=int(cpu_time_float))
[235]599    checkpoint.checkpoint('Status')
[133]600    display_fields = [('name', 'Name'),
[609]601                      ('description', 'Description'),
[133]602                      ('owner', 'Owner'),
[187]603                      ('administrator', 'Administrator'),
[133]604                      ('contact', 'Contact'),
[136]605                      ('type', 'Type'),
[133]606                      'NIC_INFO',
607                      ('uptime', 'uptime'),
608                      ('cputime', 'CPU usage'),
[662]609                      ('host', 'Hosted on'),
[133]610                      ('memory', 'RAM'),
611                      'DISK_INFO',
612                      ('state', 'state (xen format)'),
613                      ]
614    fields = []
615    machine_info = {}
[147]616    machine_info['name'] = machine.name
[609]617    machine_info['description'] = machine.description
[136]618    machine_info['type'] = machine.type.hvm and 'HVM' or 'ParaVM'
[133]619    machine_info['owner'] = machine.owner
[187]620    machine_info['administrator'] = machine.administrator
[133]621    machine_info['contact'] = machine.contact
622
623    nic_fields = getNicInfo(machine_info, machine)
624    nic_point = display_fields.index('NIC_INFO')
[438]625    display_fields = (display_fields[:nic_point] + nic_fields +
[205]626                      display_fields[nic_point+1:])
[133]627
628    disk_fields = getDiskInfo(machine_info, machine)
629    disk_point = display_fields.index('DISK_INFO')
[438]630    display_fields = (display_fields[:disk_point] + disk_fields +
[205]631                      display_fields[disk_point+1:])
[438]632
[211]633    main_status['memory'] += ' MiB'
[133]634    for field, disp in display_fields:
[167]635        if field in ('uptime', 'cputime') and locals()[field] is not None:
[133]636            fields.append((disp, locals()[field]))
[147]637        elif field in machine_info:
638            fields.append((disp, machine_info[field]))
[133]639        elif field in main_status:
640            fields.append((disp, main_status[field]))
641        else:
642            pass
643            #fields.append((disp, None))
[235]644
645    checkpoint.checkpoint('Got fields')
646
647
[572]648    max_mem = validation.maxMemory(machine.owner, state, machine, False)
[235]649    checkpoint.checkpoint('Got mem')
[566]650    max_disk = validation.maxDisk(machine.owner, machine)
[209]651    defaults = Defaults()
[609]652    for name in 'machine_id name description administrator owner memory contact'.split():
[2691]653        if getattr(machine, name):
654            setattr(defaults, name, getattr(machine, name))
[516]655    defaults.type = machine.type.type_id
[205]656    defaults.disk = "%0.2f" % (machine.disks[0].size/1024.)
[235]657    checkpoint.checkpoint('Got defaults')
[572]658    d = dict(user=username,
[133]659             on=status is not None,
660             machine=machine,
[205]661             defaults=defaults,
[133]662             has_vnc=has_vnc,
663             uptime=str(uptime),
664             ram=machine.memory,
[144]665             max_mem=max_mem,
666             max_disk=max_disk,
[133]667             fields = fields)
[205]668    return d
[113]669
[598]670def send_error_mail(subject, body):
671    import subprocess
[205]672
[863]673    to = config.web.errormail
[598]674    mail = """To: %s
[863]675From: root@%s
[598]676Subject: %s
677
678%s
[863]679""" % (to, config.web.hostname, subject, body)
[1718]680    p = subprocess.Popen(['/usr/sbin/sendmail', '-f', to, to],
681                         stdin=subprocess.PIPE)
[598]682    p.stdin.write(mail)
683    p.stdin.close()
684    p.wait()
685
[2710]686random.seed() #sigh
Note: See TracBrowser for help on using the repository browser.