def register_plugin_calls(*funcs): """Wrapper around XenAPIPlugin.dispatch which handles pickle serialization. """ wrapped_dict = {} for func in funcs: wrapped_dict[func.__name__] = _handle_serialization(func) XenAPIPlugin.dispatch(wrapped_dict)
elif mode == "nbd": cmdout = util.pread2(["rbd-nbd", "--nbds_max", NBDS_MAX, "map", "%s/%s" % (CEPH_POOL_NAME, vdi_name), "--name", CEPH_USER]).rstrip('\n') util.pread2(["ln", "-s", cmdout, dev_name]) return "mapped" def _unmap(session, arg_dict): mode = arg_dict['mode'] dev_name = arg_dict['dev_name'] CEPH_POOL_NAME = arg_dict['CEPH_POOL_NAME'] CEPH_USER = arg_dict['CEPH_USER'] NBDS_MAX = arg_dict['NBDS_MAX'] if arg_dict.has_key("snap_name"): vdi_name = arg_dict["snap_name"] else: vdi_name = arg_dict['vdi_name'] if mode == "kernel": util.pread2(["rbd", "unmap", dev_name, "--name", CEPH_USER]) elif mode == "fuse": pass elif mode == "nbd": nbddev = util.pread2(["realpath", dev_name]).rstrip('\n') util.pread2(["unlink", dev_name]) util.pread2(["rbd-nbd", "unmap", nbddev, "--name", CEPH_USER]) return "unmapped" if __name__ == "__main__": XenAPIPlugin.dispatch({"map": _map, "unmap": _unmap})
# a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # NOTE: XenServer still only supports Python 2.4 in it's dom0 userspace # which means the Nova xenapi plugins must use only Python 2.4 features import XenAPIPlugin def get_val(session, args): config_key = args['key'] config_file = open('/etc/xapi.conf') try: for line in config_file: split = line.split('=') if (len(split) == 2) and (split[0].strip() == config_key): return split[1].strip() return "" finally: config_file.close() if __name__ == '__main__': XenAPIPlugin.dispatch({"get_val": get_val})
exists_args = { "dom_id": arg_dict["dom_id"], "path": "name", } dom_id_is_present = xenstore.record_exists(exists_args) if not dom_id_is_present: reboot_detected = True break # No response within the timeout period; bail out # First, delete the request record arg_dict["path"] = "data/host/%s" % request_id xenstore.delete_record(self, arg_dict) if reboot_detected: raise RebootDetectedError(_("REBOOT: dom_id %s no longer " "present") % arg_dict["dom_id"]) else: raise TimeoutError(_("TIMEOUT: No response from agent within" " %s seconds.") % timeout) if __name__ == "__main__": XenAPIPlugin.dispatch( {"version": version, "key_init": key_init, "password": password, "resetnetwork": resetnetwork, "inject_file": inject_file, "agentupdate": agent_update})
return rewrite_etc_pamd_ssh(session, args) def after_subject_add(session, args): return rewrite_etc_pamd_ssh(session, args) def after_subject_remove(session, args): return rewrite_etc_pamd_ssh(session, args) def after_roles_update(session, args): return rewrite_etc_pamd_ssh(session, args) def before_extauth_disable(session, args): return revert_etc_pamd_ssh(session, args) # The dispatcher if __name__ == "__main__": dispatch_tbl = { "after-extauth-enable": after_extauth_enable, "after-xapi-initialize": after_xapi_initialize, "after-subject-add": after_subject_add, "after-subject-remove": after_subject_remove, "after-roles-update": after_roles_update, "before-extauth-disable": before_extauth_disable, } XenAPIPlugin.dispatch(dispatch_tbl)
# # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # NOTE: XenServer still only supports Python 2.4 in it's dom0 userspace # which means the Nova xenapi plugins must use only Python 2.4 features import XenAPIPlugin def get_val(session, args): config_key = args['key'] config_file = open('/etc/xapi.conf') try: for line in config_file: split = line.split('=') if (len(split) == 2) and (split[0].strip() == config_key): return split[1].strip() return "" finally: config_file.close() if __name__ == '__main__': XenAPIPlugin.dispatch({"get_val": get_val})
import XenAPIPlugin from xcpngutils import run_command, error_wrapped @error_wrapped def list_block_devices(session, args): result = run_command(["lsblk", "-P", "-b", "-o", "NAME,KNAME,PKNAME,SIZE,TYPE,RO,MOUNTPOINT"]) output_string = result["stdout"].decode("utf-8").strip() results = list() blockdevices = dict() for output in output_string.split("\n"): output_dict = dict(re.findall(r'(\S+)=(".*?"|\S+)', output)) output_dict = {key.lower(): output_dict[key].strip('"') for key in output_dict} kname = output_dict["kname"] pkname = output_dict["pkname"] if pkname != "": parent = blockdevices[pkname] if "children" not in parent: parent["children"] = list() parent["children"].append(output_dict) else: results.append(output_dict) blockdevices[kname] = output_dict return json.dumps({'blockdevices': results}) if __name__ == "__main__": XenAPIPlugin.dispatch({"list_block_devices": list_block_devices})
""" pipe = subprocess.PIPE proc = subprocess.Popen(cmd, shell=False, stdin=pipe, stdout=pipe, stderr=pipe, close_fds=True) (out, err) = proc.communicate(cmd_input) return proc.returncode, out, err def run_command(session, args): cmd = json.loads(args.get('cmd')) if cmd and cmd[0] not in ALLOWED_CMDS: msg = _("Dom0 execution of '%s' is not permitted") % cmd[0] raise PluginError(msg) returncode, out, err = _run_command( cmd, json.loads(args.get('cmd_input', 'null'))) if not err: err = "" if not out: out = "" # This runs in Dom0, will return to neutron-ovs-agent in compute node result = {'returncode': returncode, 'out': out, 'err': err} return json.dumps(result) if __name__ == "__main__": XenAPIPlugin.dispatch({"run_command": run_command})
# plugin entry point def main(session, args): xelogging.logToStderr() try: url = args['url'] except KeyError: xelogging.log("Missing argument 'url'") raise Exception('MISSING_URL') xelogging.log("Verifying repo...") succeeded = False if not test_repo(url): xelogging.log("%s is not a valid repo" % url) raise Exception('INVALID_URL') else: xelogging.log("Repo ok, preparing for upgrade.") succeeded = prepare_host_upgrade(url) if succeeded: xelogging.log("Preparation succeeded, ready for upgrade.") return "true" else: xelogging.log("There was an error in preparing the host for upgrade.") raise Exception('ERROR_PREPARING_HOST') if __name__ == '__main__': XenAPIPlugin.dispatch({"main": main, "testUrl": testUrl})
def is_open(session, args): try: return _is_open(session, args) except: util.logException("is_open") raise def refresh_lun_size_by_SCSIid(session, args): """Refresh the size of LUNs backing the SCSIid on the local node.""" util.SMlog("on-slave.refresh_lun_size_by_SCSIid(,%s)" % args) if scsiutil.refresh_lun_size_by_SCSIid(args['SCSIid']): util.SMlog("on-slave.refresh_lun_size_by_SCSIid with %s succeeded" % args) return "True" else: util.SMlog("on-slave.refresh_lun_size_by_SCSIid with %s failed" % args) return "False" if __name__ == "__main__": import XenAPIPlugin XenAPIPlugin.dispatch({ "multi": multi, "is_open": is_open, "refresh_lun_size_by_SCSIid": refresh_lun_size_by_SCSIid })
filename = "" logging.debug("Cached kernel/ramdisk image not found") return filename def _remove_file(filepath): try: os.remove(filepath) except OSError, exc: # noqa if exc.errno != errno.ENOENT: raise def remove_kernel_ramdisk(session, args): """Removes kernel and/or ramdisk from dom0's file system.""" kernel_file = optional(args, 'kernel-file') ramdisk_file = optional(args, 'ramdisk-file') if kernel_file: _remove_file(kernel_file) if ramdisk_file: _remove_file(ramdisk_file) return "ok" if __name__ == '__main__': XenAPIPlugin.dispatch({ 'copy_vdi': copy_vdi, 'create_kernel_ramdisk': create_kernel_ramdisk, 'remove_kernel_ramdisk': remove_kernel_ramdisk })
# returns empty string if no streaming is configured @error_wrapped def get_netdata_api_key(session, args): with OperationLocker(): try: with open("/etc/netdata/stream.conf", "r") as conf_file: content = conf_file.readlines() content = map(lambda line: line.split('#')[0].strip(), content) api_key_line = filter(lambda line: line.startswith('api key'), content) # Python 2&3 compatible code api_key_line = [x for x in api_key_line][0] api_key = api_key_line.split('=')[1].strip() return api_key except EnvironmentError as e: # if the file doesn't exist, the system is not configured for streaming, return empty string if e.errno == errno.ENOENT: return '' raise _LOGGER = configure_logging('netdata') if __name__ == "__main__": XenAPIPlugin.dispatch({ 'is_netdata_installed': is_netdata_installed, 'install_netdata': install_netdata, 'get_netdata_api_key': get_netdata_api_key })
if not config.has_section(section): raise Exception("Can't find section '%s' in config file" % section) # idempotence if proxies[section] == '_none_' and not config.has_option(section, 'proxy'): continue if config.has_option(section, 'proxy') and config.get(section, 'proxy') == proxies[section]: continue config.set(section, 'proxy', proxies[section]) url = config.get(section, 'baseurl') if proxies[section] == '_none_' and url.startswith(special_url_prefix): config.set(section, 'baseurl', https_url_prefix + url[len(special_url_prefix):]) elif proxies[section] != '_none_' and url.startswith(https_url_prefix): config.set(section, 'baseurl', special_url_prefix + url[len(https_url_prefix):]) else: raise Exception('Unexpected URL "%s" for proxy "%s" in section "%s"' % (url, proxies[section], section)) with open(CONFIGURATION_FILE, 'wb') as configfile: config.write(configfile) return '' _LOGGER = configure_logging('updater') if __name__ == "__main__": XenAPIPlugin.dispatch({ 'check_update': check_update, 'update': update, 'get_proxies': get_proxies, 'set_proxies': set_proxies })
#!/usr/bin/env python # Simple XenAPI plugin import XenAPIPlugin, time def main(session, args): if "sleep" in args: secs = int(args["sleep"]) time.sleep(secs) return "args were: %s" % (repr(args)) if __name__ == "__main__": XenAPIPlugin.dispatch({"main": main})
raise return file_like_object.read() def get_console_log(session, arg_dict): try: raw_dom_id = arg_dict['dom_id'] except KeyError: raise pluginlib_nova.PluginError("Missing dom_id") try: dom_id = int(raw_dom_id) except ValueError: raise pluginlib_nova.PluginError("Invalid dom_id") logfile = open(CONSOLE_LOG_FILE_PATTERN % dom_id, 'rb') try: try: log_content = _last_bytes(logfile) except IOError, e: # noqa msg = "Error reading console: %s" % e logging.debug(msg) raise pluginlib_nova.PluginError(msg) finally: logfile.close() return base64.b64encode(zlib.compress(log_content)) if __name__ == "__main__": XenAPIPlugin.dispatch({"get_console_log": get_console_log})
import sys echoRequest = parseRequest(args) data = echoRequest.data if echoRequest.path: writeToPath(data, echoRequest.path) if echoRequest.stdout: sys.stdout.write(data) if echoRequest.stderr: sys.stderr.write(data) if echoRequest.exitCode is not None: sys.exit(echoRequest.exitCode) return echoRequest.data def toXapiArgs(args): result = [] for k, v in args.iteritems(): if v is not None: result.append('args:%s="%s"' % (k, v)) return result if __name__ == "__main__": import XenAPIPlugin XenAPIPlugin.dispatch({ECHO_FN_NAME: echo})
#!/usr/bin/env python # -*- coding: utf-8 -*- import json import sys import XenAPIPlugin sys.path.append('.') from xcpngutils import configure_logging, run_command, error_wrapped @error_wrapped def get_hyperthreading(session, args): result = run_command(['xl', 'info', 'threads_per_core']) _LOGGER.info(result) lines = result['stdout'].splitlines() return json.dumps(int(lines[0]) > 1) _LOGGER = configure_logging('hyperthreading') if __name__ == "__main__": XenAPIPlugin.dispatch({ 'get_hyperthreading': get_hyperthreading })
'%(asctime)s - [%(process)d] - %(levelname)s - %(message)s', '%Y-%m-%d %H:%M:%S') handlers = [] log_level = logging.INFO if os.access(os.path.dirname(LOG_FILE), os.W_OK): fileh = logging.handlers.RotatingFileHandler(LOG_FILE, maxBytes=5 * 1024 * 1024, backupCount=5) handlers.append(fileh) if os.path.exists(ENABLE_DEV_LOGGING_FILE) or not handlers: handlers.append(logging.StreamHandler(sys.stdout)) log_level = logging.DEBUG # Configure and add all handlers for handler in handlers: handler.setLevel(log_level) handler.setFormatter(formatter) _LOGGER.addHandler(handler) signal.signal(signal.SIGPIPE, signal.SIG_DFL) _LOGGER = logging.getLogger('yum') configure_logging() sys.excepthook = handle_unhandled_exceptions if __name__ == "__main__": XenAPIPlugin.dispatch({'check_update': check_update, 'update': update})
# xe host-call-plugin host-uuid=<UUID> plugin=zfs.py fn=list_zfs_pools @error_wrapped def list_zfs_pools(session, args): try: command = ['zfs', 'get', '-H', 'all'] _LOGGER.info('executing command {}...'.format(command)) result = run_command(command) lines = result['stdout'].splitlines() res = {} def set_entry(pool, key, value): if pool in res: res.get(pool)[key] = value else: res[pool] = {key: value} for line in lines: split_line = line.split('\t') set_entry(split_line[0], split_line[1], split_line[2]) return json.dumps(res) except OSError as e: if e.errno == errno.ENOENT: return json.dumps({}) else: raise _LOGGER = configure_logging('zfs') if __name__ == "__main__": XenAPIPlugin.dispatch({'list_zfs_pools': list_zfs_pools})
CEPH_POOL_NAME = arg_dict['CEPH_POOL_NAME'] CEPH_USER = arg_dict['CEPH_USER'] NBDS_MAX = arg_dict['NBDS_MAX'] sharable = arg_dict['sharable'] dm = arg_dict['dm'] _vdi_name = arg_dict['_vdi_name'] dev = util.pread2(["realpath", _dev_name]).rstrip('\n') if dm != "none": util.pread2(["dmsetup", "suspend", _dm_name]) if mode == "kernel": util.pread2(["rbd", "unmap", dev, "--name", CEPH_USER]) elif mode == "fuse": pass elif mode == "nbd": util.pread2(["rbd", "nbd", "unmap", dev, "--name", CEPH_USER]) return "unmapped" if __name__ == "__main__": XenAPIPlugin.dispatch({ "map": _map, "unmap": _unmap, "_map": __map, "_unmap": __unmap, "merge": _merge })
def after_extauth_enable(session, args): return rewrite_etc_pamd_ssh(session, args) def after_xapi_initialize(session, args): return rewrite_etc_pamd_ssh(session, args) def after_subject_add(session, args): return rewrite_etc_pamd_ssh(session, args) def after_subject_remove(session, args): return rewrite_etc_pamd_ssh(session, args) def after_roles_update(session, args): return rewrite_etc_pamd_ssh(session, args) def before_extauth_disable(session, args): return revert_etc_pamd_ssh(session, args) # The dispatcher if __name__ == "__main__": dispatch_tbl = { "after-extauth-enable": after_extauth_enable, "after-xapi-initialize": after_xapi_initialize, "after-subject-add": after_subject_add, "after-subject-remove": after_subject_remove, "after-roles-update": after_roles_update, "before-extauth-disable":before_extauth_disable, } XenAPIPlugin.dispatch(dispatch_tbl)
syslog.syslog("tar.................") tmp = os.popen('tar -zxvf ' + toDirectory + '/' + filename + '.tgz -C ' + toDirectory + ';echo $?').readlines() tmplen = len(tmp) tmpstatus = tmp[tmplen - 1].strip('\n') if tmpstatus != '0': return "1002" syslog.syslog("uninstall.................") if os.path.exists(toDirectory + '/' + filename + '/uninstall_patch'): syslog.syslog("UnInstallation file found") else: syslog.syslog("UnInstallation file 'uninstall' No found") return "1005" tmp = os.popen('cd ' + toDirectory + '/' + filename + ' ;./uninstall_patch;echo $?').readlines() tmplen = len(tmp) tmpstatus = tmp[tmplen - 1].strip('\n') if tmpstatus != '0': return tmpstatus syslog.syslog("patch uninstall over.................") os.popen('rm -rf ' + toDirectory + '/' + filename) return "true" if __name__ == "__main__": XenAPIPlugin.dispatch({"main": main, "revert": revert})
# returns {"raid": {"State": "clean", (...)}, # "volumes": [["0", "8", "0", "0", "active sync", "/dev/sda"], (...)]} @error_wrapped def check_raid_pool(session, args): device = '/dev/md127' with OperationLocker(): result = run_command(['mdadm', '--detail', device]) lines = [line.strip() for line in result['stdout'].splitlines()] lines = [line for line in lines if len(line) > 0] # remove first line ('/dev/md127:') lines = lines[1:] # look for the line 'Number Major Minor RaidDevice State' footer_index = next(i for i, line in enumerate(lines) if line.startswith('Number')) # '1 8 16 1 active sync /dev/sdb' -> ["1", "8", "16", "1", "active sync", "/dev/sdb"] volumes = [[ field.strip() for field in line.split(' ') if len(field.strip()) > 0 ] for line in lines[footer_index + 1:]] # 'Version : 1.0' -> {"Version": "1.0"} lines = dict([[element.strip() for element in line.split(' : ', 1)] for line in lines[0:footer_index]]) return json.dumps({'raid': lines, 'volumes': volumes}) if __name__ == "__main__": XenAPIPlugin.dispatch({'check_raid_pool': check_raid_pool})
path = [] elif this_level == level: # child of same parent ret.append("%s/%s" % ("/".join(path), barename)) elif this_level > level: path.append(last_nm) ret.append("%s/%s" % ("/".join(path), barename)) level = this_level elif this_level < level: path = path[:this_level] ret.append("%s/%s" % ("/".join(path), barename)) level = this_level last_nm = barename return ret def _run_command(cmd): """Wrap utils.run_command to raise XenstoreError on failure """ try: return utils.run_command(cmd) except utils.SubprocessException, e: # noqa raise XenstoreError(e.cmdline, e.ret, e.err, e.out) if __name__ == "__main__": XenAPIPlugin.dispatch( {"read_record": read_record, "write_record": write_record, "list_records": list_records, "delete_record": delete_record})
]).rstrip('\n') util.pread2(["ln", "-s", cmdout, dev_name]) return "mapped" def _unmap(session, arg_dict): mode = arg_dict['mode'] dev_name = arg_dict['dev_name'] CEPH_POOL_NAME = arg_dict['CEPH_POOL_NAME'] CEPH_USER = arg_dict['CEPH_USER'] NBDS_MAX = arg_dict['NBDS_MAX'] if arg_dict.has_key("snap_name"): vdi_name = arg_dict["snap_name"] else: vdi_name = arg_dict['vdi_name'] if mode == "kernel": util.pread2(["rbd", "unmap", dev_name, "--name", CEPH_USER]) elif mode == "fuse": pass elif mode == "nbd": nbddev = util.pread2(["realpath", dev_name]).rstrip('\n') util.pread2(["unlink", dev_name]) util.pread2(["rbd-nbd", "unmap", nbddev, "--name", CEPH_USER]) return "unmapped" if __name__ == "__main__": XenAPIPlugin.dispatch({"map": _map, "unmap": _unmap})
elif this_level == level: # child of same parent ret.append("%s/%s" % ("/".join(path), barename)) elif this_level > level: path.append(last_nm) ret.append("%s/%s" % ("/".join(path), barename)) level = this_level elif this_level < level: path = path[:this_level] ret.append("%s/%s" % ("/".join(path), barename)) level = this_level last_nm = barename return ret def _run_command(cmd): """Wrap utils.run_command to raise XenstoreError on failure""" try: return utils.run_command(cmd) except utils.SubprocessException as e: # noqa raise XenstoreError(e.cmdline, e.ret, e.err, e.out) if __name__ == "__main__": XenAPIPlugin.dispatch({ "read_record": read_record, "write_record": write_record, "list_records": list_records, "delete_record": delete_record })
"dom_id": arg_dict["dom_id"], "path": "name", } dom_id_is_present = xenstore.record_exists(exists_args) if not dom_id_is_present: reboot_detected = True break # No response within the timeout period; bail out # First, delete the request record arg_dict["path"] = "data/host/%s" % request_id xenstore.delete_record(self, arg_dict) if reboot_detected: raise RebootDetectedError("REBOOT: dom_id %s no longer " "present" % arg_dict["dom_id"]) else: raise TimeoutError("TIMEOUT: No response from agent within" " %s seconds." % timeout) if __name__ == "__main__": XenAPIPlugin.dispatch({ "version": version, "key_init": key_init, "password": password, "resetnetwork": resetnetwork, "inject_file": inject_file, "agentupdate": agent_update })
logging.debug("Done. Filename: %s", filename) else: filename = "" logging.debug("Cached kernel/ramdisk image not found") return filename def _remove_file(filepath): try: os.remove(filepath) except OSError, exc: # noqa if exc.errno != errno.ENOENT: raise def remove_kernel_ramdisk(session, args): """Removes kernel and/or ramdisk from dom0's file system.""" kernel_file = optional(args, 'kernel-file') ramdisk_file = optional(args, 'ramdisk-file') if kernel_file: _remove_file(kernel_file) if ramdisk_file: _remove_file(ramdisk_file) return "ok" if __name__ == '__main__': XenAPIPlugin.dispatch({'copy_vdi': copy_vdi, 'create_kernel_ramdisk': create_kernel_ramdisk, 'remove_kernel_ramdisk': remove_kernel_ramdisk})
raise return file_like_object.read() def get_console_log(session, arg_dict): try: raw_dom_id = arg_dict['dom_id'] except KeyError: raise dom0_pluginlib.PluginError("Missing dom_id") try: dom_id = int(raw_dom_id) except ValueError: raise dom0_pluginlib.PluginError("Invalid dom_id") logfile = open(CONSOLE_LOG_FILE_PATTERN % dom_id, 'rb') try: try: log_content = _last_bytes(logfile) except IOError, e: # noqa msg = "Error reading console: %s" % e logging.debug(msg) raise dom0_pluginlib.PluginError(msg) finally: logfile.close() return base64.b64encode(zlib.compress(log_content)) if __name__ == "__main__": XenAPIPlugin.dispatch({"get_console_log": get_console_log})
def __unmap(session, arg_dict): mode = arg_dict['mode'] _dev_name = arg_dict['_dev_name'] _dm_name = arg_dict['_dm_name'] CEPH_USER = arg_dict['CEPH_USER'] dmmode = arg_dict['dmmode'] vdi_uuid = arg_dict["vdi_uuid"] dev = util.pread2(['realpath', _dev_name]).rstrip('\n') if dmmode != 'None': util.pread2(['dmsetup', 'suspend', _dm_name]) if mode == 'kernel': util.pread2(['rbd', 'unmap', dev, '--name', CEPH_USER]) elif mode == 'fuse': pass elif mode == 'nbd': nbd_unmap(_dev_name, vdi_uuid, CEPH_USER) return "unmapped" if __name__ == '__main__': XenAPIPlugin.dispatch({ 'map': _map, 'unmap': _unmap, '_map': __map, '_unmap': __unmap })
#!/usr/bin/env python # Simple XenAPI plugin import XenAPIPlugin, time def main(session, args): if args.has_key("sleep"): secs = int(args["sleep"]) time.sleep(secs) return "args were: %s" % (repr(args)) if __name__ == "__main__": XenAPIPlugin.dispatch({"main": main})
output = _run_command(["ls", "/sys/bus/pci/devices/" + pci_device + "/"]) if "physfn" in output: return "type-VF" if "virtfn" in output: return "type-PF" return "type-PCI" if __name__ == "__main__": # Support both serialized and non-serialized plugin approaches _, methodname = xmlrpclib.loads(sys.argv[1]) if methodname in ['query_gc', 'get_pci_device_details', 'get_pci_type', 'network_config']: utils.register_plugin_calls(query_gc, get_pci_device_details, get_pci_type, network_config) XenAPIPlugin.dispatch( {"host_data": host_data, "set_host_enabled": set_host_enabled, "host_shutdown": host_shutdown, "host_reboot": host_reboot, "host_start": host_start, "host_join": host_join, "get_config": get_config, "set_config": set_config, "iptables_config": iptables_config, "host_uptime": host_uptime})