// Copyright 2012 Citrix Systems, Inc. Licensed under the // Apache License, Version 2.0 (the "License"); you may not use this // file except in compliance with the License. Citrix Systems, Inc. // reserves all rights not expressly granted by the License. // You may obtain 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. // // Automatically generated by addcopyright.py at 04/03/2012 package com.cloud.storage.resource; import java.io.File; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; import com.cloud.agent.api.CheckHealthAnswer; import com.cloud.agent.api.CheckHealthCommand; import com.cloud.agent.api.Command; import com.cloud.agent.api.GetStorageStatsAnswer; import com.cloud.agent.api.GetStorageStatsCommand; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.PingStorageCommand; import com.cloud.agent.api.ReadyAnswer; import com.cloud.agent.api.ReadyCommand; import com.cloud.agent.api.SecStorageFirewallCfgCommand; import com.cloud.agent.api.SecStorageSetupCommand; import com.cloud.agent.api.SecStorageFirewallCfgCommand.PortConfig; import com.cloud.agent.api.SecStorageVMSetupCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupStorageCommand; import com.cloud.agent.api.storage.CreateEntityDownloadURLCommand; import com.cloud.agent.api.storage.DeleteEntityDownloadURLCommand; import com.cloud.agent.api.storage.DeleteTemplateCommand; import com.cloud.agent.api.storage.DownloadCommand; import com.cloud.agent.api.storage.DownloadProgressCommand; import com.cloud.agent.api.storage.UploadCommand; import com.cloud.agent.api.storage.ssCommand; import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.resource.ServerResource; import com.cloud.resource.ServerResourceBase; import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageLayer; import com.cloud.storage.template.DownloadManager; import com.cloud.storage.template.DownloadManagerImpl; import com.cloud.storage.template.TemplateInfo; import com.cloud.storage.template.UploadManager; import com.cloud.storage.template.UploadManagerImpl; import com.cloud.utils.NumbersUtil; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.utils.net.NfsUtils; import com.cloud.utils.script.Script; /** * Implementation of Secondary Storage Resource to handle CIFS share * * TODOD: After mount rest of the functionality should be similar to NfsSecondaryStroage. * Move common functionality of NFS and CIFS secondary storage resource class to base class **/ public class CifsSecondaryStorageResource extends ServerResourceBase implements SecondaryStorageResource { private static final Logger s_logger = Logger.getLogger(CifsSecondaryStorageResource.class); int _timeout; String _instance; String _parent; String _dc; String _pod; String _guid; String _nfsPath; String _mountParent; Map<String, Object> _params; StorageLayer _storage; boolean _inSystemVM = false; boolean _sslCopy = false; Random _rand = new Random(System.currentTimeMillis()); DownloadManager _dlMgr; UploadManager _upldMgr; private String _configSslScr; private String _configAuthScr; private String _configIpFirewallScr; private String _publicIp; private String _hostname; private String _localgw; private String _eth1mask; private String _eth1ip; @Override public void disconnected() { if (_parent != null && !_inSystemVM) { Script script = new Script(!_inSystemVM, "umount", _timeout, s_logger); script.add(_parent); script.execute(); File file = new File(_parent); file.delete(); } } @Override public Answer executeRequest(Command cmd) { if (cmd instanceof DownloadProgressCommand) { return _dlMgr.handleDownloadCommand(this, (DownloadProgressCommand)cmd); } else if (cmd instanceof DownloadCommand) { return _dlMgr.handleDownloadCommand(this, (DownloadCommand)cmd); } else if (cmd instanceof UploadCommand) { return _upldMgr.handleUploadCommand(this, (UploadCommand)cmd); } else if (cmd instanceof CreateEntityDownloadURLCommand){ return _upldMgr.handleCreateEntityURLCommand((CreateEntityDownloadURLCommand)cmd); } else if(cmd instanceof DeleteEntityDownloadURLCommand){ return _upldMgr.handleDeleteEntityDownloadURLCommand((DeleteEntityDownloadURLCommand)cmd); } else if (cmd instanceof GetStorageStatsCommand) { return execute((GetStorageStatsCommand)cmd); } else if (cmd instanceof CheckHealthCommand) { return new CheckHealthAnswer((CheckHealthCommand)cmd, true); } else if (cmd instanceof DeleteTemplateCommand) { return execute((DeleteTemplateCommand) cmd); } else if (cmd instanceof ReadyCommand) { return new ReadyAnswer((ReadyCommand)cmd); } else if (cmd instanceof SecStorageFirewallCfgCommand){ return execute((SecStorageFirewallCfgCommand)cmd); } else if (cmd instanceof SecStorageVMSetupCommand){ return execute((SecStorageVMSetupCommand)cmd); } else if (cmd instanceof SecStorageSetupCommand){ return new Answer(cmd, true, "success"); } else { return Answer.createUnsupportedCommandAnswer(cmd); } } private Answer execute(SecStorageVMSetupCommand cmd) { if (!_inSystemVM){ return new Answer(cmd, true, null); } boolean success = true; StringBuilder result = new StringBuilder(); for (String cidr: cmd.getAllowedInternalSites()) { String tmpresult = allowOutgoingOnPrivate(cidr); if (tmpresult != null) { result.append(", ").append(tmpresult); success = false; } } if (success) { if (cmd.getCopyPassword() != null && cmd.getCopyUserName() != null) { String tmpresult = configureAuth(cmd.getCopyUserName(), cmd.getCopyPassword()); if (tmpresult != null) { result.append("Failed to configure auth for copy ").append(tmpresult); success = false; } } } return new Answer(cmd, success, result.toString()); } private String allowOutgoingOnPrivate(String destCidr) { Script command = new Script("/bin/bash", s_logger); String intf = "eth1"; command.add("-c"); command.add("iptables -I OUTPUT -o " + intf + " -d " + destCidr + " -p tcp -m state --state NEW -m tcp -j ACCEPT"); String result = command.execute(); if (result != null) { s_logger.warn("Error in allowing outgoing to " + destCidr + ", err=" + result ); return "Error in allowing outgoing to " + destCidr + ", err=" + result; } addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, destCidr); return null; } private Answer execute(SecStorageFirewallCfgCommand cmd) { if (!_inSystemVM){ return new Answer(cmd, true, null); } List<String> ipList = new ArrayList<String>(); for (PortConfig pCfg:cmd.getPortConfigs()){ if (pCfg.isAdd()) { ipList.add(pCfg.getSourceIp()); } } boolean success = true; String result; result = configureIpFirewall(ipList); if (result !=null) success = false; return new Answer(cmd, success, result); } protected GetStorageStatsAnswer execute(final GetStorageStatsCommand cmd) { final long usedSize = getUsedSize(); final long totalSize = getTotalSize(); if (usedSize == -1 || totalSize == -1) { return new GetStorageStatsAnswer(cmd, "Unable to get storage stats"); } else { return new GetStorageStatsAnswer(cmd, totalSize, usedSize) ; } } @Override public String getRootDir(ssCommand cmd){ return null; } protected Answer execute(final DeleteTemplateCommand cmd) { String relativeTemplatePath = cmd.getTemplatePath(); String parent = _parent; if (relativeTemplatePath.startsWith(File.separator)) { relativeTemplatePath = relativeTemplatePath.substring(1); } if (!parent.endsWith(File.separator)) { parent += File.separator; } String absoluteTemplatePath = parent + relativeTemplatePath; File tmpltParent = new File(absoluteTemplatePath).getParentFile(); String details = null; if (!tmpltParent.exists()) { details = "template parent directory " + tmpltParent.getName() + " doesn't exist"; s_logger.debug(details); return new Answer(cmd, true, details); } File[] tmpltFiles = tmpltParent.listFiles(); if (tmpltFiles == null || tmpltFiles.length == 0) { details = "No files under template parent directory " + tmpltParent.getName(); s_logger.debug(details); } else { boolean found = false; for (File f : tmpltFiles) { if (!found && f.getName().equals("template.properties")) { found = true; } if (!f.delete()) { return new Answer(cmd, false, "Unable to delete file " + f.getName() + " under Template path " + relativeTemplatePath); } } if (!found) { details = "Can not find template.properties under " + tmpltParent.getName(); s_logger.debug(details); } } if (!tmpltParent.delete()) { details = "Unable to delete directory " + tmpltParent.getName() + " under Template path " + relativeTemplatePath; s_logger.debug(details); return new Answer(cmd, false, details); } return new Answer(cmd, true, null); } protected long getUsedSize() { return _storage.getUsedSpace(_parent); } protected long getTotalSize() { return _storage.getTotalSpace(_parent); } protected long convertFilesystemSize(final String size) { if (size == null || size.isEmpty()) { return -1; } long multiplier = 1; if (size.endsWith("T")) { multiplier = 1024l * 1024l * 1024l * 1024l; } else if (size.endsWith("G")) { multiplier = 1024l * 1024l * 1024l; } else if (size.endsWith("M")) { multiplier = 1024l * 1024l; } else { assert (false) : "Well, I have no idea what this is: " + size; } return (long)(Double.parseDouble(size.substring(0, size.length() - 1)) * multiplier); } @Override public Type getType() { return Host.Type.SecondaryStorage; } @Override public PingCommand getCurrentStatus(final long id) { return new PingStorageCommand(Host.Type.Storage, id, new HashMap<String, Boolean>()); } @Override public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { _eth1ip = (String)params.get("eth1ip"); if (_eth1ip != null) { //can only happen inside service vm params.put("private.network.device", "eth1"); } else { s_logger.warn("Wait, what's going on? eth1ip is null!!"); } String eth2ip = (String) params.get("eth2ip"); if (eth2ip != null) { params.put("public.network.device", "eth2"); } _publicIp = (String) params.get("eth2ip"); _hostname = (String) params.get("name"); super.configure(name, params); _params = params; String value = (String)params.get("scripts.timeout"); _timeout = NumbersUtil.parseInt(value, 1440) * 1000; _storage = (StorageLayer)params.get(StorageLayer.InstanceConfigKey); if (_storage == null) { value = (String)params.get(StorageLayer.ClassConfigKey); if (value == null) { value = "com.cloud.storage.JavaStorageLayer"; } try { Class<?> clazz = Class.forName(value); _storage = (StorageLayer)ComponentLocator.inject(clazz); _storage.configure("StorageLayer", params); } catch (ClassNotFoundException e) { throw new ConfigurationException("Unable to find class " + value); } } _configSslScr = Script.findScript(getDefaultScriptsDir(), "config_ssl.sh"); if (_configSslScr != null) { s_logger.info("config_ssl.sh found in " + _configSslScr); } _configAuthScr = Script.findScript(getDefaultScriptsDir(), "config_auth.sh"); if (_configSslScr != null) { s_logger.info("config_auth.sh found in " + _configAuthScr); } _configIpFirewallScr = Script.findScript(getDefaultScriptsDir(), "ipfirewall.sh"); if (_configIpFirewallScr != null) { s_logger.info("_configIpFirewallScr found in " + _configIpFirewallScr); } _guid = (String)params.get("guid"); if (_guid == null) { throw new ConfigurationException("Unable to find the guid"); } _dc = (String)params.get("zone"); if (_dc == null) { throw new ConfigurationException("Unable to find the zone"); } _pod = (String)params.get("pod"); _instance = (String)params.get("instance"); _mountParent = (String)params.get("mount.parent"); if (_mountParent == null) { _mountParent = File.separator + "mnt"; } if (_instance != null) { _mountParent = _mountParent + File.separator + _instance; } _nfsPath = (String)params.get("mount.path"); if (_nfsPath == null) { throw new ConfigurationException("Unable to find mount.path"); } String inSystemVM = (String)params.get("secondary.storage.vm"); if (inSystemVM == null || "true".equalsIgnoreCase(inSystemVM)) { _inSystemVM = true; _localgw = (String)params.get("localgw"); if (_localgw != null) { //can only happen inside service vm _eth1mask = (String)params.get("eth1mask"); String internalDns1 = (String)params.get("dns1"); String internalDns2 = (String)params.get("dns2"); if (internalDns1 == null) { s_logger.warn("No DNS entry found during configuration of NfsSecondaryStorage"); } else { addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, internalDns1); } String mgmtHost = (String)params.get("host"); String nfsHost = NfsUtils.getHostPart(_nfsPath); if (nfsHost == null) { s_logger.error("Invalid or corrupt nfs url " + _nfsPath); throw new CloudRuntimeException("Unable to determine host part of nfs path"); } try { InetAddress nfsHostAddr = InetAddress.getByName(nfsHost); nfsHost = nfsHostAddr.getHostAddress(); } catch (UnknownHostException uhe) { s_logger.error("Unable to resolve nfs host " + nfsHost); throw new CloudRuntimeException("Unable to resolve nfs host to an ip address " + nfsHost); } addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, nfsHost); addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, mgmtHost); if (internalDns2 != null) { addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, internalDns2); } } String useSsl = (String)params.get("sslcopy"); if (useSsl != null) { _sslCopy = Boolean.parseBoolean(useSsl); if (_sslCopy) { configureSSL(); } } startAdditionalServices(); _params.put("install.numthreads", "50"); _params.put("secondary.storage.vm", "true"); } _parent = mount(_nfsPath, _mountParent); if (_parent == null) { throw new ConfigurationException("Unable to create mount point"); } s_logger.info("Mount point established at " + _parent); try { _params.put("template.parent", _parent); _params.put(StorageLayer.InstanceConfigKey, _storage); _dlMgr = new DownloadManagerImpl(); _dlMgr.configure("DownloadManager", _params); _upldMgr = new UploadManagerImpl(); _upldMgr.configure("UploadManager", params); } catch (ConfigurationException e) { s_logger.warn("Caught problem while configuring DownloadManager", e); return false; } return true; } private void startAdditionalServices() { Script command = new Script("/bin/bash", s_logger); command.add("-c"); command.add("if [ -f /etc/init.d/ssh ]; then service ssh restart; else service sshd restart; fi "); String result = command.execute(); if (result != null) { s_logger.warn("Error in starting sshd service err=" + result ); } command = new Script("/bin/bash", s_logger); command.add("-c"); command.add("iptables -I INPUT -i eth1 -p tcp -m state --state NEW -m tcp --dport 3922 -j ACCEPT"); result = command.execute(); if (result != null) { s_logger.warn("Error in opening up ssh port err=" + result ); } } private void addRouteToInternalIpOrCidr(String localgw, String eth1ip, String eth1mask, String destIpOrCidr) { s_logger.debug("addRouteToInternalIp: localgw=" + localgw + ", eth1ip=" + eth1ip + ", eth1mask=" + eth1mask + ",destIp=" + destIpOrCidr); if (destIpOrCidr == null) { s_logger.debug("addRouteToInternalIp: destIp is null"); return; } if (!NetUtils.isValidIp(destIpOrCidr) && !NetUtils.isValidCIDR(destIpOrCidr)){ s_logger.warn(" destIp is not a valid ip address or cidr destIp=" + destIpOrCidr); return; } boolean inSameSubnet = false; if (NetUtils.isValidIp(destIpOrCidr)) { if (eth1ip != null && eth1mask != null) { inSameSubnet = NetUtils.sameSubnet(eth1ip, destIpOrCidr, eth1mask); } else { s_logger.warn("addRouteToInternalIp: unable to determine same subnet: _eth1ip=" + eth1ip + ", dest ip=" + destIpOrCidr + ", _eth1mask=" + eth1mask); } } else { inSameSubnet = NetUtils.isNetworkAWithinNetworkB(destIpOrCidr, NetUtils.ipAndNetMaskToCidr(eth1ip, eth1mask)); } if (inSameSubnet) { s_logger.debug("addRouteToInternalIp: dest ip " + destIpOrCidr + " is in the same subnet as eth1 ip " + eth1ip); return; } Script command = new Script("/bin/bash", s_logger); command.add("-c"); command.add("ip route delete " + destIpOrCidr); command.execute(); command = new Script("/bin/bash", s_logger); command.add("-c"); command.add("ip route add " + destIpOrCidr + " via " + localgw); String result = command.execute(); if (result != null) { s_logger.warn("Error in configuring route to internal ip err=" + result ); } else { s_logger.debug("addRouteToInternalIp: added route to internal ip=" + destIpOrCidr + " via " + localgw); } } private void configureSSL() { Script command = new Script(_configSslScr); command.add(_publicIp); command.add(_hostname); String result = command.execute(); if (result != null) { s_logger.warn("Unable to configure httpd to use ssl"); } } private String configureAuth(String user, String passwd) { Script command = new Script(_configAuthScr); command.add(user); command.add(passwd); String result = command.execute(); if (result != null) { s_logger.warn("Unable to configure httpd to use auth"); } return result; } private String configureIpFirewall(List<String> ipList){ Script command = new Script(_configIpFirewallScr); for (String ip : ipList){ command.add(ip); } String result = command.execute(); if (result != null) { s_logger.warn("Unable to configure firewall for command : " +command); } return result; } protected String mount(String path, String parent) { String mountPoint = null; for (int i = 0; i < 10; i++) { String mntPt = parent + File.separator + Integer.toHexString(_rand.nextInt(Integer.MAX_VALUE)); File file = new File(mntPt); if (!file.exists()) { if (_storage.mkdir(mntPt)) { mountPoint = mntPt; break; } } s_logger.debug("Unable to create mount: " + mntPt); } if (mountPoint == null) { s_logger.warn("Unable to create a mount point"); return null; } Script script = null; String result = null; script = new Script(!_inSystemVM, "umount", _timeout, s_logger); script.add(path); result = script.execute(); if( _parent != null ) { script = new Script("rmdir", _timeout, s_logger); script.add(_parent); result = script.execute(); } Script command = new Script(!_inSystemVM, "mount", _timeout, s_logger); command.add("-t", "cifs"); if (_inSystemVM) { //Fedora Core 12 errors out with any -o option executed from java //command.add("-o", "soft,timeo=133,retrans=2147483647,tcp,acdirmax=0,acdirmin=0"); } String tok[] = path.split(":"); //command.add(path); command.add("//"+tok[0]+tok[1]); command.add(mountPoint); result = command.execute(); if (result != null) { s_logger.warn("Unable to mount " + path + " due to " + result); File file = new File(mountPoint); if (file.exists()) file.delete(); return null; } // XXX: Adding the check for creation of snapshots dir here. Might have to move it somewhere more logical later. if (!checkForSnapshotsDir(mountPoint)) { return null; } // Create the volumes dir if (!checkForVolumesDir(mountPoint)) { return null; } return mountPoint; } @Override public boolean start() { return true; } @Override public boolean stop() { return true; } @Override public StartupCommand[] initialize() { /*disconnected(); _parent = mount(_nfsPath, _mountParent); if( _parent == null ) { s_logger.warn("Unable to mount the nfs server"); return null; } try { _params.put("template.parent", _parent); _params.put(StorageLayer.InstanceConfigKey, _storage); _dlMgr = new DownloadManagerImpl(); _dlMgr.configure("DownloadManager", _params); } catch (ConfigurationException e) { s_logger.warn("Caught problem while configuring folers", e); return null; }*/ final StartupStorageCommand cmd = new StartupStorageCommand(_parent, StoragePoolType.NetworkFilesystem, getTotalSize(), new HashMap<String, TemplateInfo>()); cmd.setResourceType(Storage.StorageResourceType.SECONDARY_STORAGE); cmd.setIqn(null); fillNetworkInformation(cmd); cmd.setDataCenter(_dc); cmd.setPod(_pod); cmd.setGuid(_guid); cmd.setName(_guid); cmd.setVersion(NfsSecondaryStorageResource.class.getPackage().getImplementationVersion()); cmd.getHostDetails().put("mount.parent", _mountParent); cmd.getHostDetails().put("mount.path", _nfsPath); String tok[] = _nfsPath.split(":"); cmd.setNfsShare("nfs://" + tok[0] + tok[1]); if (cmd.getHostDetails().get("orig.url") == null) { if (tok.length != 2) { throw new CloudRuntimeException("Not valid NFS path" + _nfsPath); } String nfsUrl = "nfs://" + tok[0] + tok[1]; cmd.getHostDetails().put("orig.url", nfsUrl); } InetAddress addr; try { addr = InetAddress.getByName(tok[0]); cmd.setPrivateIpAddress(addr.getHostAddress()); } catch (UnknownHostException e) { cmd.setPrivateIpAddress(tok[0]); } return new StartupCommand [] {cmd}; } protected boolean checkForSnapshotsDir(String mountPoint) { String snapshotsDirLocation = mountPoint + File.separator + "snapshots"; return createDir("snapshots", snapshotsDirLocation, mountPoint); } protected boolean checkForVolumesDir(String mountPoint) { String volumesDirLocation = mountPoint + "/" + "volumes"; return createDir("volumes", volumesDirLocation, mountPoint); } protected boolean createDir(String dirName, String dirLocation, String mountPoint) { boolean dirExists = false; File dir = new File(dirLocation); if (dir.exists()) { if (dir.isDirectory()) { s_logger.debug(dirName + " already exists on secondary storage, and is mounted at " + mountPoint); dirExists = true; } else { if (dir.delete() && _storage.mkdir(dirLocation)) { dirExists = true; } } } else if (_storage.mkdir(dirLocation)) { dirExists = true; } if (dirExists) { s_logger.info(dirName + " directory created/exists on Secondary Storage."); } else { s_logger.info(dirName + " directory does not exist on Secondary Storage."); } return dirExists; } @Override protected String getDefaultScriptsDir() { return "./scripts/storage/secondary"; } }