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

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

Use contact address from config on error page

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