bitkeeper revision 1.1236.43.6 (4243f3f3rPSrXq5qC_rXYAkeTrQ7dQ)

manual merge
This commit is contained in:
iap10@freefall.cl.cam.ac.uk committed 2005-03-25 11:20:19 +00:00
commit e9d4be73ca
14 files changed
+332 -91

No files matched your search

+24
View File
@@ -477,6 +477,7 @@ static PyTypeObject xu_notifier_type = {
*/
#define TYPE(_x,_y) (((_x)<<8)|(_y))
#define P2C(_struct, _field, _ctype) \
do { \
PyObject *obj; \
@@ -497,6 +498,29 @@ static PyTypeObject xu_notifier_type = {
} \
xum->msg.length = sizeof(_struct); \
} while ( 0 )
/** Set a char[] field in a struct from a Python string.
* Can't do this in P2C because of the typing.
*/
#define P2CSTRING(_struct, _field) \
do { \
PyObject *obj; \
if ( (obj = PyDict_GetItemString(payload, #_field)) != NULL ) \
{ \
if ( PyString_Check(obj) ) \
{ \
_struct * _cobj = (_struct *)&xum->msg.msg[0]; \
int _field_n = sizeof(_cobj->_field); \
memset(_cobj->_field, 0, _field_n); \
strncpy(_cobj->_field, \
PyString_AsString(obj), \
_field_n - 1); \
dict_items_parsed++; \
} \
} \
xum->msg.length = sizeof(_struct); \
} while ( 0 )
#define C2P(_struct, _field, _pytype, _ctype) \
do { \
PyObject *obj = Py ## _pytype ## _From ## _ctype \
+13
View File
@@ -37,6 +37,19 @@ def network(op, script=None, bridge=None, antispoof=None):
args = ' '.join(args)
os.system(script + ' ' + args)
def set_vif_name(vif_old, vif_new):
if vif_old == vif_new:
vif = view_new
if os.system("ip link show %s" % vif_old) == 0:
os.system("ip link set %s down" % vif_old)
os.system("ip link set %s name %s" % (vif_old, vif_new))
os.system("ip link set %s up" % vif_new)
if os.system("ip link show %s" % vif_new) == 0:
vif = vif_new
else:
vif = vif_old
return vif
def vifctl(op, vif=None, script=None, domain=None, mac=None, bridge=None, ipaddr=[]):
"""Call a vif control script.
Xend calls this when bringing vifs up or down.
+91 -67
View File
@@ -6,12 +6,14 @@ import errno
import sys
import socket
import time
import types
from twisted.internet import reactor
from twisted.internet import defer
#defer.Deferred.debug = 1
from twisted.internet.protocol import Protocol
from twisted.internet.protocol import ClientFactory
from twisted.python.failure import Failure
import sxp
import XendDB
@@ -45,11 +47,9 @@ class Xfrd(Protocol):
sxp.show(req, out=self.transport)
def loseConnection(self):
print 'Xfrd>loseConnection>'
self.transport.loseConnection()
def connectionLost(self, reason):
print 'Xfrd>connectionLost>', reason
self.xinfo.connectionLost(reason)
def dataReceived(self, data):
@@ -70,17 +70,15 @@ class XfrdClientFactory(ClientFactory):
self.xinfo = xinfo
def startedConnecting(self, connector):
print 'Started to connect', 'self=', self, 'connector=', connector
pass
def buildProtocol(self, addr):
print 'buildProtocol>', addr
return Xfrd(self.xinfo)
def clientConnectionLost(self, connector, reason):
print 'clientConnectionLost>', 'connector=', connector, 'reason=', reason
pass
def clientConnectionFailed(self, connector, reason):
print 'clientConnectionFailed>', 'connector=', connector, 'reason=', reason
self.xinfo.error(reason)
class XfrdInfo:
@@ -90,7 +88,7 @@ class XfrdInfo:
"""Suspend timeout (seconds).
We set a timeout because suspending a domain can hang."""
timeout = 30
timeout = 10
def __init__(self):
from xen.xend import XendDomain
@@ -98,6 +96,9 @@ class XfrdInfo:
self.deferred = defer.Deferred()
self.suspended = {}
self.paused = {}
self.state = 'init'
# List of errors encountered.
self.errors = []
def vmconfig(self):
dominfo = self.xd.domain_get(self.src_dom)
@@ -107,12 +108,38 @@ class XfrdInfo:
val = None
return val
def add_error(self, err):
"""Add an error to the error list.
Returns the error added (which may have been unwrapped if it
was a Twisted Failure).
"""
while isinstance(err, Failure):
err = err.value
if err not in self.errors:
self.errors.append(err)
return err
def error_summary(self, msg=None):
"""Get a XendError summarising the errors (if any).
"""
if msg is None:
msg = "errors"
if self.errors:
errmsg = msg + ': ' + ', '.join(map(str, self.errors))
else:
errmsg = msg
return XendError(errmsg)
def get_errors(self):
"""Get the list of errors.
"""
return self.errors
def error(self, err):
print 'Error>', err
self.state = 'error'
self.add_error(err)
if not self.deferred.called:
print 'Error> calling errback'
self.deferred.errback(err)
self.deferred.errback(self.error_summary())
def dispatch(self, xfrd, val):
@@ -139,28 +166,23 @@ class XfrdInfo:
cbok(val)
def unknown(self, xfrd, val):
print 'unknown>', val
xfrd.loseConnection()
return None
def xfr_err(self, xfrd, val):
# If we get an error with non-zero code the operation failed.
# An error with code zero indicates hello success.
print 'xfr_err>', val
v = sxp.child0(val)
print 'xfr_err>', type(v), v
err = int(sxp.child0(val))
if not err: return
self.error(err);
self.error("transfer daemon (xfrd) error: " + str(err))
xfrd.loseConnection()
return None
def xfr_progress(self, xfrd, val):
print 'xfr_progress>', val
return None
def xfr_vm_destroy(self, xfrd, val):
print 'xfr_vm_destroy>', val
try:
vmid = sxp.child0(val)
val = self.xd.domain_destroy(vmid)
@@ -168,28 +190,32 @@ class XfrdInfo:
del self.paused[vmid]
if vmid in self.suspended:
del self.suspended[vmid]
except:
except StandardError, err:
self.add_error("vm_destroy failed")
self.add_error(err)
val = errno.EINVAL
return ['xfr.err', val]
def xfr_vm_pause(self, xfrd, val):
print 'xfr_vm_pause>', val
try:
vmid = sxp.child0(val)
val = self.xd.domain_pause(vmid)
self.paused[vmid] = 1
except:
except StandardError, err:
self.add_error("vm_pause failed")
self.add_error(err)
val = errno.EINVAL
return ['xfr.err', val]
def xfr_vm_unpause(self, xfrd, val):
print 'xfr_vm_unpause>', val
try:
vmid = sxp.child0(val)
val = self.xd.domain_unpause(vmid)
if vmid in self.paused:
del self.paused[vmid]
except:
except StandardError, err:
self.add_error("vm_unpause failed")
self.add_error(err)
val = errno.EINVAL
return ['xfr.err', val]
@@ -199,7 +225,6 @@ class XfrdInfo:
Suspending can hang, so we set a timeout and fail if it
takes too long.
"""
print 'xfr_vm_suspend>', val
try:
vmid = sxp.child0(val)
d = defer.Deferred()
@@ -208,15 +233,15 @@ class XfrdInfo:
# the domain died. Set a timeout and error handler so the subscriptions
# will be cleaned up if suspending hangs or there is an error.
def onSuspended(e, v):
print 'xfr_vm_suspend>onSuspended>', e, v
if v[1] != vmid: return
subscribe(on=0)
d.callback(v)
if not d.called:
d.callback(v)
def onDied(e, v):
print 'xfr_vm_suspend>onDied>', e, v
if v[1] != vmid: return
d.errback(XendError('Domain died'))
if not d.called:
d.errback(XendError('Domain %s died while suspending' % vmid))
def subscribe(on=1):
if on:
@@ -227,24 +252,25 @@ class XfrdInfo:
action('xend.domain.died', onDied)
def cberr(err):
print 'xfr_vm_suspend>cberr>', err
subscribe(on=0)
self.add_error("suspend failed")
self.add_error(err)
return err
d.addErrback(cberr)
d.setTimeout(self.timeout)
subscribe()
val = self.xd.domain_shutdown(vmid, reason='suspend')
self.suspended[vmid] = 1
d.addErrback(cberr)
d.setTimeout(self.timeout)
return d
except Exception, err:
print 'xfr_vm_suspend> Exception', err
self.add_error("suspend failed")
self.add_error(err)
traceback.print_exc()
val = errno.EINVAL
return ['xfr.err', val]
def connectionLost(self, reason=None):
print 'XfrdInfo>connectionLost>', reason
for vmid in self.suspended:
try:
self.xd.domain_destroy(vmid)
@@ -279,7 +305,7 @@ class XendMigrateInfo(XfrdInfo):
['id', self.xid ],
['state', self.state ],
['live', self.live ],
['resource', self.resource] ]
['resource', self.resource ] ]
sxpr_src = ['src', ['host', self.src_host], ['domain', self.src_dom] ]
sxpr.append(sxpr_src)
sxpr_dst = ['dst', ['host', self.dst_host] ]
@@ -291,12 +317,12 @@ class XendMigrateInfo(XfrdInfo):
def request(self, xfrd):
vmconfig = self.vmconfig()
if not vmconfig:
self.error(XendError("vm config not found"))
xfrd.loseConnection()
return
log.info('Migrate BEGIN: ' + str(self.sxpr()))
log.info('Migrate BEGIN: %s' % str(self.sxpr()))
eserver.inject('xend.domain.migrate',
[ self.dominfo.name, self.dominfo.id,
"begin", self.sxpr() ])
[ self.dominfo.name, self.dominfo.id, "begin", self.sxpr() ])
xfrd.request(['xfr.migrate',
self.src_dom,
vmconfig,
@@ -305,19 +331,6 @@ class XendMigrateInfo(XfrdInfo):
self.live,
self.resource ])
## def xfr_vm_suspend(self, xfrd, val):
## def cbok(val):
## # Special case for localhost: destroy devices early.
## if self.dst_host in ["localhost", "127.0.0.1"]:
## self.dominfo.restart_cancel()
## self.dominfo.cleanup()
## self.dominfo.destroy_console()
## return val
## d = XfrdInfo.xfr_vm_suspend(self, xfrd, val)
## d.addCallback(cbok)
## return d
def xfr_migrate_ok(self, xfrd, val):
dom = int(sxp.child0(val))
self.state = 'ok'
@@ -327,17 +340,15 @@ class XendMigrateInfo(XfrdInfo):
self.deferred.callback(self)
def connectionLost(self, reason=None):
print 'XfrdMigrateInfo>connectionLost>', reason
XfrdInfo.connectionLost(self, reason)
if self.state =='ok':
log.info('Migrate OK: ' + str(self.sxpr()))
else:
self.state = 'error'
self.error(XendError("migrate failed"))
self.error("migrate failed")
log.info('Migrate ERROR: ' + str(self.sxpr()))
eserver.inject('xend.domain.migrate',
[ self.dominfo.name, self.dominfo.id,
self.state, self.sxpr() ])
[ self.dominfo.name, self.dominfo.id, self.state, self.sxpr() ])
class XendSaveInfo(XfrdInfo):
"""Representation of a save in-progress and its interaction with xfrd.
@@ -361,16 +372,15 @@ class XendSaveInfo(XfrdInfo):
return sxpr
def request(self, xfrd):
print '***request>', self.vmconfig()
vmconfig = self.vmconfig()
if not vmconfig:
self.error(XendError("vm config not found"))
xfrd.loseConnection()
return
print '***request> begin'
log.info('Save BEGIN: ' + str(self.sxpr()))
eserver.inject('xend.domain.save',
[self.dominfo.name, self.dominfo.id,
"begin", self.sxpr()])
[ self.dominfo.name, self.dominfo.id,
"begin", self.sxpr() ])
xfrd.request(['xfr.save', self.src_dom, vmconfig, self.file ])
def xfr_save_ok(self, xfrd, val):
@@ -380,13 +390,12 @@ class XendSaveInfo(XfrdInfo):
self.deferred.callback(self)
def connectionLost(self, reason=None):
print 'XfrdSaveInfo>connectionLost>', reason
XfrdInfo.connectionLost(self, reason)
if self.state =='ok':
log.info('Save OK: ' + str(self.sxpr()))
else:
self.state = 'error'
self.error(XendError("save failed"))
self.error("save failed")
log.info('Save ERROR: ' + str(self.sxpr()))
eserver.inject('xend.domain.save',
[ self.dominfo.name, self.dominfo.id,
@@ -409,8 +418,9 @@ class XendRestoreInfo(XfrdInfo):
return sxpr
def request(self, xfrd):
print '***request>', self.file
log.info('restore BEGIN: ' + str(self.sxpr()))
eserver.inject('xend.restore', [ 'begin', self.sxpr()])
xfrd.request(['xfr.restore', self.file ])
def xfr_restore_ok(self, xfrd, val):
@@ -419,8 +429,17 @@ class XendRestoreInfo(XfrdInfo):
self.state = 'ok'
if not self.deferred.called:
self.deferred.callback(dominfo)
def connectionLost(self, reason=None):
XfrdInfo.connectionLost(self, reason)
if self.state =='ok':
log.info('Restore OK: ' + self.file)
else:
self.state = 'error'
self.error("restore failed")
log.info('Restore ERROR: ' + str(self.sxpr()))
eserver.inject('xend.restore', [ self.state, self.sxpr()])
class XendMigrate:
"""External api for interaction with xfrd for migrate and save.
Singleton.
@@ -445,7 +464,6 @@ class XendMigrate:
self.db.saveall("", self.session_db)
def sync_session(self, xid):
print 'sync_session>', type(xid), xid, self.session_db[xid]
self.db.save(xid, self.session_db[xid])
def close(self):
@@ -458,7 +476,6 @@ class XendMigrate:
self.sync_session(xid)
def _delete_session(self, xid):
print '***_delete_session>', xid
if xid in self.session:
del self.session[xid]
if xid in self.session_db:
@@ -482,16 +499,23 @@ class XendMigrate:
@param info: session
@return: deferred
"""
def cbremove(val):
print '***cbremove>', val
dfr = defer.Deferred()
def cbok(val):
self._delete_session(info.xid)
if not dfr.called:
dfr.callback(val)
return val
def cberr(err):
self._delete_session(info.xid)
if not dfr.called:
dfr.errback(err)
return err
self._add_session(info)
info.deferred.addCallback(cbremove)
info.deferred.addErrback(cbremove)
info.deferred.addCallback(cbok)
info.deferred.addErrback(cberr)
xcf = XfrdClientFactory(info)
reactor.connectTCP('localhost', XFRD_PORT, xcf)
return info.deferred
return dfr
def migrate_begin(self, dominfo, host, port=XFRD_PORT, live=0, resource=0):
"""Begin to migrate a domain to another host.
-17
View File
@@ -29,13 +29,8 @@ class SrvDomain(SrvDir):
[['dom', 'int'],
['config', 'sxpr']])
deferred = fn(req.args, {'dom': self.dom.dom})
deferred.addErrback(self._op_configure_err, req)
return deferred
def _op_configure_err(self, err, req):
req.setResponseCode(http.BAD_REQUEST, "Error: "+ str(err))
return str(err)
def op_unpause(self, op, req):
val = self.xd.domain_unpause(self.dom.name)
return val
@@ -68,16 +63,11 @@ class SrvDomain(SrvDir):
['file', 'str']])
deferred = fn(req.args, {'dom': self.dom.id})
deferred.addCallback(self._op_save_cb, req)
deferred.addErrback(self._op_save_err, req)
return deferred
def _op_save_cb(self, val, req):
return 0
def _op_save_err(self, err, req):
req.setResponseCode(http.BAD_REQUEST, "Error: "+ str(err))
return str(err)
def op_migrate(self, op, req):
fn = FormFn(self.xd.domain_migrate,
[['dom', 'str'],
@@ -85,9 +75,7 @@ class SrvDomain(SrvDir):
['live', 'int'],
['resource', 'int']])
deferred = fn(req.args, {'dom': self.dom.id})
print 'op_migrate>', deferred
deferred.addCallback(self._op_migrate_cb, req)
deferred.addErrback(self._op_migrate_err, req)
return deferred
def _op_migrate_cb(self, info, req):
@@ -101,11 +89,6 @@ class SrvDomain(SrvDir):
print '_op_migrate_cb> url=', url
return url
def _op_migrate_err(self, err, req):
print '_op_migrate_err>', err, req
req.setResponseCode(http.BAD_REQUEST, "Error: "+ str(err))
return str(err)
def op_pincpu(self, op, req):
fn = FormFn(self.xd.domain_pincpu,
[['dom', 'str'],
+2
View File
@@ -181,6 +181,8 @@ class BlkifBackendInterface(controller.BackendInterface):
self.close()
d = defer.Deferred()
d.addCallback(cb_destroy)
if self.evtchn:
channel.eventChannelClose(self.evtchn)
self.send_be_disconnect(response=d)
def send_be_disconnect(self, response=None):
+25 -1
View File
@@ -11,7 +11,31 @@ VIRQ_CONSOLE = 3 # (DOM0) bytes received on emergency console.
VIRQ_DOM_EXC = 4 # (DOM0) Exceptional event for some domain.
def eventChannel(dom1, dom2):
return xc.evtchn_bind_interdomain(dom1=dom1, dom2=dom2)
"""Create an event channel between domains.
The returned dict contains dom1, dom2, port1 and port2 on success.
@return dict (empty on error)
"""
evtchn = xc.evtchn_bind_interdomain(dom1=dom1, dom2=dom2)
if evtchn:
evtchn['dom1'] = dom1
evtchn['dom2'] = dom2
return evtchn
def eventChannelClose(evtchn):
"""Close an event channel that was opened by eventChannel().
"""
def evtchn_close(dom, port):
if (dom is None) or (port is None): return
try:
xc.evtchn_close(dom=dom, port=port)
except Exception, ex:
pass
if not evtchn: return
evtchn_close(evtchn.get('dom1'), evtchn.get('port1'))
evtchn_close(evtchn.get('dom2'), evtchn.get('port2'))
class ChannelFactory:
"""Factory for creating channels.
+21 -3
View File
@@ -138,7 +138,13 @@ class NetDev(controller.SplitDev):
self.bridge = None
self.script = None
self.ipaddr = []
self.vifname = None
self.vifname = sxp.child_value(config, 'vifname')
if self.vifname is None:
self.vifname = self.default_vifname()
if len(self.vifname) > 15:
raise XendError('invalid vifname: too long: ' + self.vifname)
mac = self._get_config_mac(config)
if mac is None:
raise XendError("invalid mac")
@@ -201,7 +207,10 @@ class NetDev(controller.SplitDev):
val = ['vif',
['idx', self.idx],
['vif', vif],
['mac', mac]]
['mac', mac],
['vifname', self.vifname],
]
if self.be_mac:
val.append(['be_mac', self.get_be_mac()])
if self.bridge:
@@ -221,8 +230,11 @@ class NetDev(controller.SplitDev):
def get_vifname(self):
"""Get the virtual interface device name.
"""
return "vif%d.%d" % (self.controller.dom, self.vif)
return self.vifname
def default_vifname(self):
return "vif%d.%d" % (self.controller.dom, self.vif)
def get_mac(self):
"""Get the MAC address as a string.
"""
@@ -259,6 +271,8 @@ class NetDev(controller.SplitDev):
@param op: operation name (up, down)
@param vmname: vmname
"""
if op == 'up':
Vifctl.set_vif_name(self.default_vifname(), self.vifname)
Vifctl.vifctl(op, **self.vifctl_params(vmname=vmname))
vnet = XendVnet.instance().vnet_of_bridge(self.bridge)
if vnet:
@@ -287,7 +301,9 @@ class NetDev(controller.SplitDev):
{ 'domid' : self.controller.dom,
'netif_handle' : self.vif,
'be_mac' : self.be_mac or [0, 0, 0, 0, 0, 0],
'mac' : self.mac })
'mac' : self.mac,
#'vifname' : self.vifname
})
self.getBackendInterface().writeRequest(msg, response=d)
return d
@@ -308,6 +324,8 @@ class NetDev(controller.SplitDev):
if change:
self.reportStatus()
log.debug("Destroying vif domain=%d vif=%d", self.controller.dom, self.vif)
if self.evtchn:
channel.eventChannelClose(self.evtchn)
self.vifctl('down')
d = self.send_be_disconnect()
d.addCallback(cb_destroy)
+70 -2
View File
@@ -5,6 +5,7 @@
import random
import string
import sys
import socket
from xen.xend import sxp
from xen.xend import PrettyPrint
@@ -81,6 +82,14 @@ gopts.opt('console_autoconnect', short='c',
fn=set_true, default=0,
use="Connect to the console after the domain is created.")
gopts.var('vnc', val='no|yes',
fn=set_bool, default=None,
use="""Spawn a vncviewer listening for a vnc server in the domain.
The address of the vncviewer is passed to the domain on the kernel command
line using 'VNC_SERVER=<host>:<port>'. The port used by vnc is 5500 + DISPLAY.
A display value with a free port is chosen if possible.
""")
gopts.var('name', val='NAME',
fn=set_value, default=None,
use="Domain name. Must be unique.")
@@ -160,7 +169,7 @@ gopts.var('ipaddr', val="IPADDR",
fn=append_value, default=[],
use="Add an IP address to the domain.")
gopts.var('vif', val="mac=MAC,be_mac=MAC,bridge=BRIDGE,script=SCRIPT,backend=DOM",
gopts.var('vif', val="mac=MAC,be_mac=MAC,bridge=BRIDGE,script=SCRIPT,backend=DOM,vifname=NAME",
fn=append_value, default=[],
use="""Add a network interface with the given MAC address and bridge.
The vif is configured by calling the given configuration script.
@@ -170,6 +179,8 @@ gopts.var('vif', val="mac=MAC,be_mac=MAC,bridge=BRIDGE,script=SCRIPT,backend=DOM
If bridge is not specified the default bridge is used.
If script is not specified the default script is used.
If backend is not specified the default backend driver domain is used.
If vifname is not specified the backend virtual interface will have name vifD.N
where D is the domain id and N is the interface id.
This option may be repeated to add more than one vif.
Specifying vifs will increase the number of interfaces as needed.""")
@@ -321,6 +332,7 @@ def configure_vifs(config_devs, vals):
script = d.get('script')
backend = d.get('backend')
ip = d.get('ip')
vifname = d.get('vifname')
else:
mac = randomMAC()
be_mac = None
@@ -328,8 +340,11 @@ def configure_vifs(config_devs, vals):
script = None
backend = None
ip = None
vifname = None
config_vif = ['vif']
config_vif.append(['mac', mac])
if vifname:
config_vif.append(['vifname', vifname])
if be_mac:
config_vif.append(['be_mac', be_mac])
if bridge:
@@ -429,7 +444,7 @@ def preprocess_vifs(opts, vals):
(k, v) = b.strip().split('=', 1)
k = k.strip()
v = v.strip()
if k not in ['mac', 'be_mac', 'bridge', 'script', 'backend', 'ip']:
if k not in ['mac', 'be_mac', 'bridge', 'script', 'backend', 'ip', 'vifname']:
opts.err('Invalid vif specifier: ' + vif)
d[k] = v
vifs.append(d)
@@ -455,6 +470,58 @@ def preprocess_nfs(opts, vals):
opts.err('Must set nfs root and nfs server')
nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
vals.extra = nfs + ' ' + vals.extra
def get_host_addr():
host = socket.gethostname()
addr = socket.gethostbyname(host)
return addr
VNC_BASE_PORT = 5500
def choose_vnc_display():
"""Try to choose a free vnc display.
"""
def netstat_local_ports():
"""Run netstat to get a list of the local ports in use.
"""
l = os.popen("netstat -nat").readlines()
r = []
# Skip 2 lines of header.
for x in l[2:]:
# Local port is field 3.
y = x.split()[3]
# Field is addr:port, split off the port.
y = y.split(':')[1]
r.append(int(y))
return r
ports = netstat_local_ports()
for d in range(1, 100):
port = VNC_BASE_PORT + d
if port in ports: continue
return d
return None
def spawn_vnc(display):
os.system("vncviewer -listen %d &" % display)
return VNC_BASE_PORT + display
def preprocess_vnc(opts, vals):
"""If vnc was specified, spawn a vncviewer in listen mode
and pass its address to the domain on the kernel command line.
"""
if not vals.vnc: return
vnc_display = choose_vnc_display()
if not vnc_display:
opts.warn("No free vnc display")
return
print 'VNC=', vnc_display
vnc_port = spawn_vnc(vnc_display)
if vnc_port > 0:
vnc_host = get_host_addr()
vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
vals.extra = vnc + ' ' + vals.extra
def preprocess(opts, vals):
if not vals.kernel:
@@ -464,6 +531,7 @@ def preprocess(opts, vals):
preprocess_vifs(opts, vals)
preprocess_ip(opts, vals)
preprocess_nfs(opts, vals)
preprocess_vnc(opts, vals)
def make_domain(opts, config):
"""Create, build and start a domain.
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.