From 2be0b45297e1b651cce8d7a4c91c5ac073989791 Mon Sep 17 00:00:00 2001 From: anthony Date: Mon, 20 Dec 2010 14:17:47 -0800 Subject: [PATCH] support xenserver 5.6 fp1 in oss --- .../xen/resource/CitrixResourceBase.java | 41 ++- .../xen/resource/XenServerResource.java | 42 --- .../vm/hypervisor/xenserver/xenserver56/patch | 1 + .../xenserver/xenserver56fp1/NFSSR.py | 256 ++++++++++++++++++ .../xenserver/xenserver56fp1/nfs.py | 145 ++++++++++ .../{xenserver56oss => xenserver56fp1}/patch | 0 .../xen/discoverer/XcpServerDiscoverer.java | 6 +- 7 files changed, 444 insertions(+), 47 deletions(-) create mode 100755 scripts/vm/hypervisor/xenserver/xenserver56fp1/NFSSR.py create mode 100755 scripts/vm/hypervisor/xenserver/xenserver56fp1/nfs.py rename scripts/vm/hypervisor/xenserver/{xenserver56oss => xenserver56fp1}/patch (100%) diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index a21a6cf60c2..03c24bae4b5 100644 --- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -103,6 +103,8 @@ import com.cloud.agent.api.ReadyCommand; import com.cloud.agent.api.RebootAnswer; import com.cloud.agent.api.RebootCommand; import com.cloud.agent.api.RebootRouterCommand; +import com.cloud.agent.api.SecurityIngressRuleAnswer; +import com.cloud.agent.api.SecurityIngressRulesCmd; import com.cloud.agent.api.SetupAnswer; import com.cloud.agent.api.SetupCommand; import com.cloud.agent.api.StartAnswer; @@ -433,6 +435,8 @@ public abstract class CitrixResourceBase implements ServerResource { return execute((VpnUsersCfgCommand)cmd); } else if (cmd instanceof CheckSshCommand) { return execute((CheckSshCommand)cmd); + } else if (cmd instanceof SecurityIngressRulesCmd) { + return execute((SecurityIngressRulesCmd) cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } @@ -3194,10 +3198,6 @@ public abstract class CitrixResourceBase implements ServerResource { return com.cloud.host.Host.Type.Routing; } - protected boolean can_bridge_firewall(Connection conn) { - return false; - } - protected boolean getHostInfo(Connection conn) throws IllegalArgumentException{ try { Host myself = Host.getByUuid(conn, _host.uuid); @@ -3663,6 +3663,39 @@ public abstract class CitrixResourceBase implements ServerResource { } } + + protected boolean can_bridge_firewall(Connection conn) { + return Boolean.valueOf(callHostPlugin(conn, "vmops", "can_bridge_firewall", "host_uuid", _host.uuid)); + } + + private Answer execute(SecurityIngressRulesCmd cmd) { + Connection conn = getConnection(); + if (s_logger.isTraceEnabled()) { + s_logger.trace("Sending network rules command to " + _host.ip); + } + + if (!_canBridgeFirewall) { + s_logger.info("Host " + _host.ip + " cannot do bridge firewalling"); + return new SecurityIngressRuleAnswer(cmd, false, "Host " + _host.ip + " cannot do bridge firewalling"); + } + + String result = callHostPlugin(conn, "vmops", "network_rules", + "vmName", cmd.getVmName(), + "vmIP", cmd.getGuestIp(), + "vmMAC", cmd.getGuestMac(), + "vmID", Long.toString(cmd.getVmId()), + "signature", cmd.getSignature(), + "seqno", Long.toString(cmd.getSeqNum()), + "rules", cmd.stringifyRules()); + + if (result == null || result.isEmpty() || !Boolean.parseBoolean(result)) { + s_logger.warn("Failed to program network rules for vm " + cmd.getVmName()); + return new SecurityIngressRuleAnswer(cmd, false, "programming network rules failed"); + } else { + s_logger.info("Programmed network rules for vm " + cmd.getVmName() + " guestIp=" + cmd.getGuestIp() + ", numrules=" + cmd.getRuleSet().length); + return new SecurityIngressRuleAnswer(cmd); + } + } protected Answer execute(DeleteStoragePoolCommand cmd) { Connection conn = getConnection(); diff --git a/core/src/com/cloud/hypervisor/xen/resource/XenServerResource.java b/core/src/com/cloud/hypervisor/xen/resource/XenServerResource.java index f1a8e09ccaa..a98167bd7d7 100644 --- a/core/src/com/cloud/hypervisor/xen/resource/XenServerResource.java +++ b/core/src/com/cloud/hypervisor/xen/resource/XenServerResource.java @@ -46,15 +46,6 @@ public class XenServerResource extends CitrixResourceBase { super(); } - @Override - public Answer executeRequest(Command cmd) { - if (cmd instanceof SecurityIngressRulesCmd) { - return execute((SecurityIngressRulesCmd) cmd); - } else { - return super.executeRequest(cmd); - } - } - @Override protected String getGuestOsType(String stdType, boolean bootFromCD) { return CitrixHelper.getXenServerGuestOsType(stdType); @@ -77,39 +68,6 @@ public class XenServerResource extends CitrixResourceBase { files.add(file); return files; } - - @Override - protected boolean can_bridge_firewall(Connection conn) { - return Boolean.valueOf(callHostPlugin(conn, "vmops", "can_bridge_firewall", "host_uuid", _host.uuid)); - } - - private Answer execute(SecurityIngressRulesCmd cmd) { - Connection conn = getConnection(); - if (s_logger.isTraceEnabled()) { - s_logger.trace("Sending network rules command to " + _host.ip); - } - if (!_canBridgeFirewall) { - s_logger.info("Host " + _host.ip + " cannot do bridge firewalling"); - return new SecurityIngressRuleAnswer(cmd, false, "Host " + _host.ip + " cannot do bridge firewalling"); - } - - String result = callHostPlugin(conn, "vmops", "network_rules", - "vmName", cmd.getVmName(), - "vmIP", cmd.getGuestIp(), - "vmMAC", cmd.getGuestMac(), - "vmID", Long.toString(cmd.getVmId()), - "signature", cmd.getSignature(), - "seqno", Long.toString(cmd.getSeqNum()), - "rules", cmd.stringifyRules()); - - if (result == null || result.isEmpty() || !Boolean.parseBoolean(result)) { - s_logger.warn("Failed to program network rules for vm " + cmd.getVmName()); - return new SecurityIngressRuleAnswer(cmd, false, "programming network rules failed"); - } else { - s_logger.info("Programmed network rules for vm " + cmd.getVmName() + " guestIp=" + cmd.getGuestIp() + ", numrules=" + cmd.getRuleSet().length); - return new SecurityIngressRuleAnswer(cmd); - } - } } diff --git a/scripts/vm/hypervisor/xenserver/xenserver56/patch b/scripts/vm/hypervisor/xenserver/xenserver56/patch index 26a1b76a1f3..8d3ca210e33 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver56/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver56/patch @@ -14,6 +14,7 @@ nfs.py=/opt/xensource/sm vmops=..,0755,/etc/xapi.d/plugins vmopsSnapshot=..,0755,/etc/xapi.d/plugins hostvmstats.py=..,0755,/opt/xensource/sm +systemvm.iso=../../../../../vms,0644,/opt/xensource/packages/iso id_rsa.cloud=..,0600,/root/.ssh network_info.sh=..,0755,/opt/xensource/bin setupxenserver.sh=..,0755,/opt/xensource/bin diff --git a/scripts/vm/hypervisor/xenserver/xenserver56fp1/NFSSR.py b/scripts/vm/hypervisor/xenserver/xenserver56fp1/NFSSR.py new file mode 100755 index 00000000000..81b1a38dd76 --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/xenserver56fp1/NFSSR.py @@ -0,0 +1,256 @@ +#!/usr/bin/python +# Copyright (C) 2006-2007 XenSource Ltd. +# Copyright (C) 2008-2009 Citrix Ltd. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation; version 2.1 only. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# FileSR: local-file storage repository + +import SR, VDI, SRCommand, FileSR, util +import errno +import os, re, sys +import time +import xml.dom.minidom +import xs_errors +import nfs +import vhdutil +from lock import Lock +import cleanup + +CAPABILITIES = ["SR_PROBE","SR_UPDATE", "SR_CACHING", \ + "VDI_CREATE","VDI_DELETE","VDI_ATTACH","VDI_DETACH", \ + "VDI_UPDATE", "VDI_CLONE","VDI_SNAPSHOT","VDI_RESIZE", \ + "VDI_RESIZE_ONLINE", "VDI_RESET_ON_BOOT", "ATOMIC_PAUSE"] + +CONFIGURATION = [ [ 'server', 'hostname or IP address of NFS server (required)' ], \ + [ 'serverpath', 'path on remote server (required)' ] ] + + +DRIVER_INFO = { + 'name': 'NFS VHD', + 'description': 'SR plugin which stores disks as VHD files on a remote NFS filesystem', + 'vendor': 'Citrix Systems Inc', + 'copyright': '(C) 2008 Citrix Systems Inc', + 'driver_version': '1.0', + 'required_api_version': '1.0', + 'capabilities': CAPABILITIES, + 'configuration': CONFIGURATION + } + + +# The mountpoint for the directory when performing an sr_probe. All probes +# are guaranteed to be serialised by xapi, so this single mountpoint is fine. +PROBE_MOUNTPOINT = "probe" +NFSPORT = 2049 +DEFAULT_TRANSPORT = "tcp" + + +class NFSSR(FileSR.FileSR): + """NFS file-based storage repository""" + def handles(type): + return type == 'nfs' + handles = staticmethod(handles) + + + def load(self, sr_uuid): + self.ops_exclusive = FileSR.OPS_EXCLUSIVE + self.lock = Lock(vhdutil.LOCK_TYPE_SR, self.uuid) + self.sr_vditype = SR.DEFAULT_TAP + if not self.dconf.has_key('server'): + raise xs_errors.XenError('ConfigServerMissing') + self.remoteserver = self.dconf['server'] + self.path = os.path.join(SR.MOUNT_BASE, sr_uuid) + + # Test for the optional 'nfsoptions' dconf attribute + self.transport = DEFAULT_TRANSPORT + if self.dconf.has_key('useUDP') and self.dconf['useUDP'] == 'true': + self.transport = "udp" + + + def validate_remotepath(self, scan): + if not self.dconf.has_key('serverpath'): + if scan: + try: + self.scan_exports(self.dconf['server']) + except: + pass + raise xs_errors.XenError('ConfigServerPathMissing') + if not self._isvalidpathstring(self.dconf['serverpath']): + raise xs_errors.XenError('ConfigServerPathBad', \ + opterr='serverpath is %s' % self.dconf['serverpath']) + + def check_server(self): + try: + nfs.check_server_tcp(self.remoteserver) + except nfs.NfsException, exc: + raise xs_errors.XenError('NFSVersion', + opterr=exc.errstr) + + + def mount(self, mountpoint, remotepath): + try: + nfs.soft_mount(mountpoint, self.remoteserver, remotepath, self.transport) + except nfs.NfsException, exc: + raise xs_errors.XenError('NFSMount', opterr=exc.errstr) + + + def attach(self, sr_uuid): + self.validate_remotepath(False) + #self.remotepath = os.path.join(self.dconf['serverpath'], sr_uuid) + self.remotepath = self.dconf['serverpath'] + util._testHost(self.dconf['server'], NFSPORT, 'NFSTarget') + self.mount_remotepath(sr_uuid) + + + def mount_remotepath(self, sr_uuid): + if not self._checkmount(): + self.check_server() + self.mount(self.path, self.remotepath) + + return super(NFSSR, self).attach(sr_uuid) + + + def probe(self): + # Verify NFS target and port + util._testHost(self.dconf['server'], NFSPORT, 'NFSTarget') + + self.validate_remotepath(True) + self.check_server() + + temppath = os.path.join(SR.MOUNT_BASE, PROBE_MOUNTPOINT) + + self.mount(temppath, self.dconf['serverpath']) + try: + return nfs.scan_srlist(temppath) + finally: + try: + nfs.unmount(temppath, True) + except: + pass + + + def detach(self, sr_uuid): + """Detach the SR: Unmounts and removes the mountpoint""" + if not self._checkmount(): + return + util.SMlog("Aborting GC/coalesce") + cleanup.abort(self.uuid) + + # Change directory to avoid unmount conflicts + os.chdir(SR.MOUNT_BASE) + + try: + nfs.unmount(self.path, True) + except nfs.NfsException, exc: + raise xs_errors.XenError('NFSUnMount', opterr=exc.errstr) + + return super(NFSSR, self).detach(sr_uuid) + + + def create(self, sr_uuid, size): + util._testHost(self.dconf['server'], NFSPORT, 'NFSTarget') + self.validate_remotepath(True) + if self._checkmount(): + raise xs_errors.XenError('NFSAttached') + + # Set the target path temporarily to the base dir + # so that we can create the target SR directory + self.remotepath = self.dconf['serverpath'] + try: + self.mount_remotepath(sr_uuid) + except Exception, exn: + try: + os.rmdir(self.path) + except: + pass + raise exn + + #newpath = os.path.join(self.path, sr_uuid) + #if util.ioretry(lambda: util.pathexists(newpath)): + # if len(util.ioretry(lambda: util.listdir(newpath))) != 0: + # self.detach(sr_uuid) + # raise xs_errors.XenError('SRExists') + #else: + # try: + # util.ioretry(lambda: util.makedirs(newpath)) + # except util.CommandException, inst: + # if inst.code != errno.EEXIST: + # self.detach(sr_uuid) + # raise xs_errors.XenError('NFSCreate', + # opterr='remote directory creation error is %d' + # % inst.code) + self.detach(sr_uuid) + + def delete(self, sr_uuid): + # try to remove/delete non VDI contents first + super(NFSSR, self).delete(sr_uuid) + try: + if self._checkmount(): + self.detach(sr_uuid) + + # Set the target path temporarily to the base dir + # so that we can remove the target SR directory + self.remotepath = self.dconf['serverpath'] + self.mount_remotepath(sr_uuid) + newpath = os.path.join(self.path, sr_uuid) + + if util.ioretry(lambda: util.pathexists(newpath)): + util.ioretry(lambda: os.rmdir(newpath)) + self.detach(sr_uuid) + except util.CommandException, inst: + self.detach(sr_uuid) + if inst.code != errno.ENOENT: + raise xs_errors.XenError('NFSDelete') + + def vdi(self, uuid, loadLocked = False): + if not loadLocked: + return NFSFileVDI(self, uuid) + return NFSFileVDI(self, uuid) + + def _checkmount(self): + return util.ioretry(lambda: util.pathexists(self.path)) \ + and util.ioretry(lambda: util.ismount(self.path)) + + def scan_exports(self, target): + util.SMlog("scanning2 (target=%s)" % target) + dom = nfs.scan_exports(target) + print >>sys.stderr,dom.toprettyxml() + +class NFSFileVDI(FileSR.FileVDI): + def attach(self, sr_uuid, vdi_uuid): + try: + vdi_ref = self.sr.srcmd.params['vdi_ref'] + self.session.xenapi.VDI.remove_from_xenstore_data(vdi_ref, \ + "vdi-type") + self.session.xenapi.VDI.remove_from_xenstore_data(vdi_ref, \ + "storage-type") + self.session.xenapi.VDI.add_to_xenstore_data(vdi_ref, \ + "storage-type", "nfs") + except: + util.logException("NFSSR:attach") + pass + + return super(NFSFileVDI, self).attach(sr_uuid, vdi_uuid) + + def clone(self, sr_uuid, vdi_uuid): + timestamp_before = int(util.get_mtime(self.sr.path)) + ret = super(NFSFileVDI, self).clone(sr_uuid, vdi_uuid) + timestamp_after = int(util.get_mtime(self.sr.path)) + if timestamp_after == timestamp_before: + util.SMlog("SR dir timestamp didn't change, updating") + timestamp_after += 1 + os.utime(self.sr.path, (timestamp_after, timestamp_after)) + return ret + + +if __name__ == '__main__': + SRCommand.run(NFSSR, DRIVER_INFO) +else: + SR.registerSR(NFSSR) diff --git a/scripts/vm/hypervisor/xenserver/xenserver56fp1/nfs.py b/scripts/vm/hypervisor/xenserver/xenserver56fp1/nfs.py new file mode 100755 index 00000000000..3a8463e30c5 --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/xenserver56fp1/nfs.py @@ -0,0 +1,145 @@ +#!/usr/bin/python +# Copyright (C) 2006-2007 XenSource Ltd. +# Copyright (C) 2008-2009 Citrix Ltd. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation; version 2.1 only. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# nfs.py: NFS related utility functions + +import util, errno, os, xml.dom.minidom + +# The algorithm for tcp and udp (at least in the linux kernel) for +# NFS timeout on softmounts is as follows: +# +# UDP: +# As long as the request wasn't started more than timeo * (2 ^ retrans) +# in the past, keep doubling the timeout. +# +# TCP: +# As long as the request wasn't started more than timeo * (1 + retrans) +# in the past, keep increaing the timeout by timeo. +# +# The time when the retrans may retry has been made will be: +# For udp: timeo * (2 ^ retrans * 2 - 1) +# For tcp: timeo * n! where n is the smallest n for which n! > 1 + retrans +# +# thus for retrans=1, timeo can be the same for both tcp and udp, +# because the first doubling (timeo*2) is the same as the first increment +# (timeo+timeo). + +SOFTMOUNT_TIMEOUT = int((40.0/3.0) * 10.0) # 1/10 s +SOFTMOUNT_RETRANS = 10 +RPCINFO_BIN = "/usr/sbin/rpcinfo" +SHOWMOUNT_BIN = "/usr/sbin/showmount" + + +class NfsException(Exception): + def __init__(self, errstr): + self.errstr = errstr + + +def check_server_tcp(server): + """Make sure that NFS over TCP/IP V3 is supported on the server. + Returns True if everything is OK, False otherwise.""" + try: + util.ioretry(lambda: util.pread([RPCINFO_BIN,"-t", + "%s" % server, "nfs","3"]), + errlist=[errno.EPERM], maxretry=2, nofail=True) + except util.CommandException, inst: + raise NfsException("rpcinfo failed or timed out: return code %d" % + inst.code) + + +def soft_mount(mountpoint, remoteserver, remotepath, transport): + """Mount the remote NFS export at 'mountpoint'""" + try: + if not util.ioretry(lambda: util.isdir(mountpoint)): + util.ioretry(lambda: util.makedirs(mountpoint)) + except util.CommandException, inst: + raise NfsException("Failed to make directory: code is %d" % + inst.code) + + options = "soft,timeo=%d,retrans=%d,%s,noac" % (SOFTMOUNT_TIMEOUT, + SOFTMOUNT_RETRANS, + transport) + try: + util.ioretry(lambda: + util.pread(["mount.nfs", "%s:%s" + % (remoteserver, remotepath), + mountpoint, "-o", options]), + errlist=[errno.EPIPE, errno.EIO], + maxretry=2, nofail=True) + except util.CommandException, inst: + raise NfsException("mount failed with return code %d" % inst.code) + + +def unmount(mountpoint, rmmountpoint): + """Unmount the mounted mountpoint""" + try: + util.pread(["umount", mountpoint]) + except util.CommandException, inst: + raise NfsException("umount failed with return code %d" % inst.code) + + if rmmountpoint: + try: + os.rmdir(mountpoint) + except OSError, inst: + raise NfsException("rmdir failed with error '%s'" % inst.strerror) + + +def scan_exports(target): + util.SMlog("scanning") + cmd = [SHOWMOUNT_BIN, "--no-headers", "-e", target] + dom = xml.dom.minidom.Document() + element = dom.createElement("nfs-exports") + dom.appendChild(element) + for val in util.pread2(cmd).split('\n'): + if not len(val): + continue + entry = dom.createElement('Export') + element.appendChild(entry) + + subentry = dom.createElement("Target") + entry.appendChild(subentry) + textnode = dom.createTextNode(target) + subentry.appendChild(textnode) + + (path, access) = val.split() + subentry = dom.createElement("Path") + entry.appendChild(subentry) + textnode = dom.createTextNode(path) + subentry.appendChild(textnode) + + subentry = dom.createElement("Accesslist") + entry.appendChild(subentry) + textnode = dom.createTextNode(access) + subentry.appendChild(textnode) + + return dom + +def scan_srlist(path): + dom = xml.dom.minidom.Document() + element = dom.createElement("SRlist") + dom.appendChild(element) + for val in filter(util.match_uuid, util.ioretry( \ + lambda: util.listdir(path))): + fullpath = os.path.join(path, val) + if not util.ioretry(lambda: util.isdir(fullpath)): + continue + + entry = dom.createElement('SR') + element.appendChild(entry) + + subentry = dom.createElement("UUID") + entry.appendChild(subentry) + textnode = dom.createTextNode(val) + subentry.appendChild(textnode) + + return dom.toprettyxml() diff --git a/scripts/vm/hypervisor/xenserver/xenserver56oss/patch b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch similarity index 100% rename from scripts/vm/hypervisor/xenserver/xenserver56oss/patch rename to scripts/vm/hypervisor/xenserver/xenserver56fp1/patch diff --git a/server/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java b/server/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java index 657494f0591..2e15bded5f0 100644 --- a/server/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java +++ b/server/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java @@ -53,6 +53,7 @@ import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.xen.resource.CitrixResourceBase; import com.cloud.hypervisor.xen.resource.XcpServerResource; +import com.cloud.hypervisor.xen.resource.XenServer56FP1Resource; import com.cloud.hypervisor.xen.resource.XenServerConnectionPool; import com.cloud.hypervisor.xen.resource.XenServerResource; import com.cloud.resource.Discoverer; @@ -375,7 +376,10 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L if(prodBrand.equals("XenServer") && prodVersion.equals("5.6.0")) return new XenServerResource(); - String msg = "Only support XCP 0.1.1 and XenServer 5.6 , but this one is " + prodBrand + " " + prodVersion; + if(prodBrand.equals("XenServer") && prodVersion.equals("5.6.100")) + return new XenServer56FP1Resource(); + + String msg = "Only support XCP 0.1.1, XenServer 5.6 and XenServer 5.6 FP1 , but this one is " + prodBrand + " " + prodVersion; _alertMgr.sendAlert(AlertManager.ALERT_TYPE_HOST, dcId, podId, msg, msg); s_logger.debug(msg); throw new RuntimeException(msg);