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

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

cut a leftover comment, fix a bit of spacing

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