diff --git a/agent/bindir/mycloud-setup-agent b/agent/bindir/mycloud-setup-agent new file mode 100755 index 00000000000..52fc025a870 --- /dev/null +++ b/agent/bindir/mycloud-setup-agent @@ -0,0 +1,137 @@ +#!/usr/bin/python +import os +import logging +import sys +import socket +import subprocess +import time + +from cloudutils.cloudException import CloudRuntimeException, CloudInternalException +from cloudutils.utilities import initLoging, bash +from cloudutils.configFileOps import configFileOps +from cloudutils.globalEnv import globalEnv +from cloudutils.networkConfig import networkConfig +from cloudutils.syscfg import sysConfigFactory + +from optparse import OptionParser + +url="http://rightscale-cloudstack.s3.amazonaws.com/kvm/centos/5.4/RightImage_CentOS_5.4_x64_v5.6.34.qcow2.bz2" +destFolder="/mnt/template/tmpl/1/4/" +metaFile="template.properties" + +def getUserInputs(): + print "Welcome to myCloud Setup:" + + mgtSvr = "myagent.cloud.com" + + cfo = configFileOps("/etc/cloud/agent/agent.properties") + oldToken = cfo.getEntry("zone") + if oldToken == "default": + oldToken = "" + zoneToken = raw_input("Please input the Zone Token:[%s]"%oldToken) + + if zoneToken == "": + if oldToken == "": + print "Please input a valid zone token" + exit(1) + zoneToken = oldToken + + try: + defaultNic = networkConfig.getDefaultNetwork() + except: + print "Failed to get default route. Please configure your network to add a default route" + exit(1) + + network = defaultNic.name + + return [mgtSvr, zoneToken, network] + +def downloadTemplate(): + if not os.path.exists(destFolder): + os.makedirs(destFolder) + oldName =url.split("/")[-1] + templateFile=url.split("/")[-1].replace(".bz2","") + + templateFullPath = destFolder + templateFile + metaFullPath = destFolder + metaFile + if os.path.exists(templateFullPath): + if os.path.exists(metaFullPath): + return True + os.remove(templateFullPath) + + print "Need to download myCloud template into your local disk, from " + url + " to " + destFolder + " :" + try: + proc = subprocess.Popen(["/bin/bash", "-c", "wget -O - " + url + " | bunzip2 > " + destFolder + templateFile]) + proc.communicate() + ret = proc.poll() + if ret is None or ret < 0: + raise CloudRuntimeException("Failed to download template") + except KeyboardInterrupt: + if os.path.exists(templateFullPath): + os.remove(templateFullPath) + raise CloudRuntimeException("Downloading process is interrupted") + + file = open(metaFullPath, "w") + physicalSize = os.stat(templateFullPath).st_size + virtualSize = bash("qemu-img info " + templateFullPath + " |grep virtual").getStdout().split("(")[1].split(" ")[0] + cfo = configFileOps(metaFullPath) + cfo.addEntry("filename", templateFile) + cfo.addEntry("id", "4") + cfo.addEntry("qcow2.size", str(physicalSize)) + cfo.addEntry("public", "true") + cfo.addEntry("uniquename", "Rightscale CentOS 5.4") + cfo.addEntry("qcow2.virtualsize", virtualSize) + cfo.addEntry("virtualsize", virtualSize) + cfo.addEntry("hvm", "true") + cfo.addEntry("description", "Rightscale CentOS 5.4") + cfo.addEntry("qcow2", "true") + cfo.addEntry("qcow2.filename", templateFile) + cfo.addEntry("size", str(physicalSize)) + cfo.save() + + +if __name__ == '__main__': + initLoging("/var/log/cloud/setupAgent.log") + + glbEnv = globalEnv() + + glbEnv.mode = "Agent" + glbEnv.agentMode = "myCloud" + parser = OptionParser() + parser.add_option("-z", "--zone-token", dest="zone", help="zone token") + + (options, args) = parser.parse_args() + if options.zone is None: + userInputs = getUserInputs() + glbEnv.mgtSvr = userInputs[0] + glbEnv.zone = userInputs[1] + glbEnv.defaultNic = userInputs[2] + else: + glbEnv.zone = options.zone + try: + defaultNic = networkConfig.getDefaultNetwork() + glbEnv.defaultNic = defaultNic.name + except: + print "Failed to get default route. Please configure your network to have a default route" + sys.exit(2) + + #generate UUID + glbEnv.uuid = configFileOps("/etc/cloud/agent/agent.properties").getEntry("guid") + if glbEnv.uuid == "": + glbEnv.uuid = bash("uuidgen").getStdout() + + print "Starting to configure your system:" + syscfg = sysConfigFactory.getSysConfigFactory(glbEnv) + try: + syscfg.config() + downloadTemplate() + syscfg.svo.stopService("cloud-agent") + syscfg.svo.enableService("cloud-agent") + print "myCloud setup is Done!" + except (CloudRuntimeException,CloudInternalException), e: + print e + print "Try to restore your system:" + try: + syscfg.restore() + except: + pass diff --git a/agent/src/com/cloud/agent/dhcp/DhcpPacketParser.java b/agent/src/com/cloud/agent/dhcp/DhcpPacketParser.java new file mode 100644 index 00000000000..ea68ae472dd --- /dev/null +++ b/agent/src/com/cloud/agent/dhcp/DhcpPacketParser.java @@ -0,0 +1,258 @@ +package com.cloud.agent.dhcp; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Formatter; + +import org.apache.log4j.Logger; +import org.jnetpcap.packet.JMemoryPacket; +import org.jnetpcap.packet.JPacket; +import org.jnetpcap.packet.PcapPacket; +import org.jnetpcap.protocol.lan.Ethernet; +import org.jnetpcap.protocol.lan.IEEE802dot1q; +import org.jnetpcap.protocol.network.Ip4; +import org.jnetpcap.protocol.tcpip.Udp; + +import com.cloud.agent.dhcp.DhcpSnooperImpl.DHCPState; + +public class DhcpPacketParser implements Runnable{ + private static final Logger s_logger = Logger.getLogger(DhcpPacketParser.class); + private enum DHCPPACKET { + OP(0), + HTYPE(1), + HLEN(2), + HOPS(3), + XID(4), + SECS(8), + FLAGS(10), + CIADDR(12), + YIADDR(16), + SIDADDR(20), + GIADDR(24), + CHADDR(28), + SNAME(44), + FILE(108), + MAGIC(236), + OPTIONS(240); + int offset; + DHCPPACKET(int i) { + offset = i; + } + int getValue() { + return offset; + } + } + private enum DHCPOPTIONTYPE { + PAD(0), + MESSAGETYPE(53), + REQUESTEDIP(50), + END(255); + int type; + DHCPOPTIONTYPE(int i) { + type = i; + } + int getValue() { + return type; + } + } + private enum DHCPMSGTYPE { + DHCPDISCOVER(1), + DHCPOFFER(2), + DHCPREQUEST(3), + DHCPDECLINE(4), + DHCPACK(5), + DHCPNAK(6), + DHCPRELEASE(7), + DHCPINFORM(8); + int _type; + DHCPMSGTYPE(int type) { + _type = type; + } + int getValue() { + return _type; + } + public static DHCPMSGTYPE valueOf(int type) { + for (DHCPMSGTYPE t : values()) { + if (type == t.getValue()) { + return t; + } + } + return null; + } + } + + private class DHCPMSG { + DHCPMSGTYPE msgType; + byte[] caddr; + byte[] yaddr; + byte[] chaddr; + byte[] requestedIP; + public DHCPMSG() { + caddr = new byte[4]; + yaddr = new byte[4]; + chaddr = new byte[6]; + } + } + + private PcapPacket _buffer; + private int _offset; + private int _len; + private DhcpSnooperImpl _manager; + + + public DhcpPacketParser(PcapPacket buffer, int offset, int len, DhcpSnooperImpl manager) { + _buffer = buffer; + _offset = offset; + _len = len; + _manager = manager; + } + private int getPos(int pos) { + return _offset + pos; + } + private byte getByte(int offset) { + return _buffer.getByte(getPos(offset)); + } + private void getByteArray(int offset, byte[] array) { + _buffer.getByteArray(getPos(offset), array); + } + private long getUInt(int offset) { + return _buffer.getUInt(getPos(offset)); + } + + private DHCPMSG getDhcpMsg() { + long magic = getUInt(DHCPPACKET.MAGIC.getValue()); + if (magic != 0x63538263) { + return null; + } + + DHCPMSG msg = new DHCPMSG(); + + int pos = DHCPPACKET.OPTIONS.getValue(); + while (pos <= _len) { + int type = (int)getByte(pos++) & 0xff; + + if (type == DHCPOPTIONTYPE.END.getValue()) { + break; + } + if (type == DHCPOPTIONTYPE.PAD.getValue()) { + continue; + } + int len = 0; + if (pos <= _len) { + len = ((int)getByte(pos++)) & 0xff; + } + + if (type == DHCPOPTIONTYPE.MESSAGETYPE.getValue() || type == DHCPOPTIONTYPE.REQUESTEDIP.getValue()) { + /*Read data only if needed */ + byte[] data = null; + if ((len + pos) <= _len) { + data = new byte[len]; + getByteArray(pos, data); + } + + if (type == DHCPOPTIONTYPE.MESSAGETYPE.getValue()) { + msg.msgType = DHCPMSGTYPE.valueOf((int)data[0]); + } else if (type == DHCPOPTIONTYPE.REQUESTEDIP.getValue()) { + msg.requestedIP = data; + } + } + + pos += len; + } + + if (msg.msgType == DHCPMSGTYPE.DHCPREQUEST) { + getByteArray(DHCPPACKET.CHADDR.getValue(), msg.chaddr); + getByteArray(DHCPPACKET.CIADDR.getValue(), msg.caddr); + } else if (msg.msgType == DHCPMSGTYPE.DHCPACK) { + getByteArray(DHCPPACKET.YIADDR.getValue(), msg.yaddr); + } + return msg; + } + + private String formatMacAddress(byte[] mac) { + StringBuffer sb = new StringBuffer(); + Formatter formatter = new Formatter(sb); + for (int i = 0; i < mac.length; i++) { + formatter.format("%02X%s", mac[i], (i < mac.length - 1) ? ":" : ""); + } + return sb.toString(); + } + + private String getDestMacAddress() { + Ethernet ether = new Ethernet(); + if (_buffer.hasHeader(ether)) { + byte[] destMac = ether.destination(); + return formatMacAddress(destMac); + } + return null; + } + + private InetAddress getDHCPServerIP() { + Ip4 ip = new Ip4(); + if (_buffer.hasHeader(ip)) { + try { + return InetAddress.getByAddress(ip.source()); + } catch (UnknownHostException e) { + s_logger.debug("Failed to get dhcp server ip address: " + e.toString()); + } + } + return null; + } + + @Override + public void run() { + DHCPMSG msg = getDhcpMsg(); + + if (msg == null) { + return; + } + + if (msg.msgType == DHCPMSGTYPE.DHCPACK) { + InetAddress ip = null; + try { + ip = InetAddress.getByAddress(msg.yaddr); + String macAddr = getDestMacAddress(); + _manager.setIPAddr(macAddr, ip, DHCPState.DHCPACKED, getDHCPServerIP()); + } catch (UnknownHostException e) { + + } + } else if (msg.msgType == DHCPMSGTYPE.DHCPREQUEST) { + InetAddress ip = null; + if (msg.requestedIP != null) { + try { + ip = InetAddress.getByAddress(msg.requestedIP); + } catch (UnknownHostException e) { + } + } + if (ip == null) { + try { + ip = InetAddress.getByAddress(msg.caddr); + } catch (UnknownHostException e) { + } + } + + if (ip != null) { + String macAddr = formatMacAddress(msg.chaddr); + _manager.setIPAddr(macAddr, ip, DHCPState.DHCPREQUESTED, null); + } + } + + } + + private void test() { + JPacket packet = new JMemoryPacket(Ethernet.ID, + " 06fa 8800 00b3 0656 d200 0027 8100 001a 0800 4500 0156 64bf 0000 4011 f3f2 ac1a 6412 ac1a 649e 0043 0044 0001 0000 0001"); + Ethernet eth = new Ethernet(); + if (packet.hasHeader(eth)) { + System.out.print(" ether:" + eth); + } + IEEE802dot1q vlan = new IEEE802dot1q(); + if (packet.hasHeader(vlan)) { + System.out.print(" vlan: " + vlan); + } + + if (packet.hasHeader(Udp.ID)) { + System.out.print("has udp"); + } + } +} diff --git a/agent/src/com/cloud/agent/dhcp/DhcpProtocolParserServer.java b/agent/src/com/cloud/agent/dhcp/DhcpProtocolParserServer.java new file mode 100644 index 00000000000..66fb7571ae5 --- /dev/null +++ b/agent/src/com/cloud/agent/dhcp/DhcpProtocolParserServer.java @@ -0,0 +1,45 @@ +package com.cloud.agent.dhcp; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.apache.log4j.Logger; + +import com.cloud.utils.concurrency.NamedThreadFactory; + +public class DhcpProtocolParserServer extends Thread { + private static final Logger s_logger = Logger.getLogger(DhcpProtocolParserServer.class);; + protected ExecutorService _executor; + private int dhcpServerPort = 67; + private int bufferSize = 300; + protected boolean _running = false; + public DhcpProtocolParserServer(int workers) { + _executor = new ThreadPoolExecutor(workers, 10 * workers, 1, TimeUnit.DAYS, new LinkedBlockingQueue(), new NamedThreadFactory("DhcpListener")); + _running = true; + } + + public void run() { + while(_running) { + try { + DatagramSocket dhcpSocket = new DatagramSocket(dhcpServerPort, InetAddress.getByAddress(new byte[]{0,0,0,0})); + dhcpSocket.setBroadcast(true); + + while (true) { + byte[] buf = new byte[bufferSize]; + DatagramPacket dgp = new DatagramPacket(buf, buf.length); + dhcpSocket.receive(dgp); + // _executor.execute(new DhcpPacketParser(buf)); + } + } catch (IOException e) { + s_logger.debug(e.getMessage()); + } + } + } +} diff --git a/agent/src/com/cloud/agent/dhcp/DhcpSnooperImpl.java b/agent/src/com/cloud/agent/dhcp/DhcpSnooperImpl.java new file mode 100644 index 00000000000..2624294b8ef --- /dev/null +++ b/agent/src/com/cloud/agent/dhcp/DhcpSnooperImpl.java @@ -0,0 +1,327 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.agent.dhcp; + + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; +import org.jnetpcap.Pcap; +import org.jnetpcap.PcapBpfProgram; +import org.jnetpcap.PcapIf; +import org.jnetpcap.packet.PcapPacket; +import org.jnetpcap.packet.PcapPacketHandler; +import org.jnetpcap.protocol.tcpip.Udp; + +import com.cloud.utils.Pair; +import com.cloud.utils.concurrency.NamedThreadFactory; + +@Local(value = { DhcpSnooper.class}) +public class DhcpSnooperImpl implements DhcpSnooper { + private static final Logger s_logger = Logger.getLogger(DhcpSnooperImpl.class); + public enum DHCPState { + DHCPACKED, + DHCPREQUESTED, + DHCPRESET; + } + public class IPAddr { + String _vmName; + InetAddress _ip; + DHCPState _state; + public IPAddr(InetAddress ip, DHCPState state, String vmName) { + _ip = ip; + _state = state; + _vmName = vmName; + } + } + protected ExecutorService _executor; + protected Map _macIpMap; + protected Map _ipMacMap; + private DhcpServer _server; + protected long _timeout = 1200000; + protected InetAddress _dhcpServerIp; + + public DhcpSnooperImpl(String bridge, long timeout) { + + _timeout = timeout; + _executor = new ThreadPoolExecutor(10, 10 * 10, 1, TimeUnit.DAYS, new LinkedBlockingQueue(), new NamedThreadFactory("DhcpListener")); + _macIpMap = new ConcurrentHashMap(); + _ipMacMap = new ConcurrentHashMap(); + _server = new DhcpServer(this, bridge); + _server.start(); + } + + + @Override + public InetAddress getIPAddr(String macAddr, String vmName) { + String macAddrLowerCase = macAddr.toLowerCase(); + IPAddr addr = _macIpMap.get(macAddrLowerCase); + if (addr == null) { + addr = new IPAddr(null, DHCPState.DHCPRESET, vmName); + _macIpMap.put(macAddrLowerCase, addr); + } else { + addr._state = DHCPState.DHCPRESET; + } + + synchronized(addr) { + try { + addr.wait(_timeout); + } catch (InterruptedException e) { + } + if (addr._state == DHCPState.DHCPACKED) { + addr._state = DHCPState.DHCPRESET; + return addr._ip; + } + } + + return null; + } + + public InetAddress getDhcpServerIP() { + return _dhcpServerIp; + } + + + @Override + public void cleanup(String macAddr, String vmName) { + try { + if (macAddr == null) { + return; + } + _macIpMap.remove(macAddr); + _ipMacMap.values().remove(macAddr); + } catch (Exception e) { + s_logger.debug("Failed to cleanup: " + e.toString()); + } + } + + + @Override + public Map syncIpAddr() { + Collection ips = _macIpMap.values(); + HashMap vmIpMap = new HashMap(); + for(IPAddr ip : ips) { + if (ip._state == DHCPState.DHCPACKED) { + vmIpMap.put(ip._vmName, ip._ip); + } + } + return vmIpMap; + } + + @Override + public void initializeMacTable(List> macVmNameList) { + for (Pair macVmname : macVmNameList) { + IPAddr ipAdrr = new IPAddr(null, DHCPState.DHCPRESET, macVmname.second()); + _macIpMap.put(macVmname.first(), ipAdrr); + } + } + + protected void setIPAddr(String macAddr, InetAddress ip, DHCPState state, InetAddress dhcpServerIp) { + String macAddrLowerCase = macAddr.toLowerCase(); + if (state == DHCPState.DHCPREQUESTED) { + IPAddr ipAddr = _macIpMap.get(macAddrLowerCase); + if (ipAddr == null) { + return; + } + + _ipMacMap.put(ip, macAddr); + } else if (state == DHCPState.DHCPACKED) { + _dhcpServerIp = dhcpServerIp; + String destMac = macAddrLowerCase; + if (macAddrLowerCase.equalsIgnoreCase("ff:ff:ff:ff:ff:ff")) { + destMac = _ipMacMap.get(ip); + if (destMac == null) { + return; + } + } + + IPAddr addr = _macIpMap.get(destMac); + if (addr != null) { + addr._ip = ip; + addr._state = state; + synchronized (addr) { + addr.notify(); + } + } + } + } + + /* (non-Javadoc) + * @see com.cloud.agent.dhcp.DhcpSnooper#stop() + */ + @Override + public boolean stop() { + _executor.shutdown(); + _server.StopServer(); + return true; + } + + private class DhcpServer extends Thread { + private DhcpSnooperImpl _manager; + private String _bridge; + private Pcap _pcapedDev; + private boolean _loop; + public DhcpServer(DhcpSnooperImpl mgt, String bridge) { + _manager = mgt; + _bridge = bridge; + _loop = true; + } + public void StopServer() { + _loop = false; + _pcapedDev.breakloop(); + _pcapedDev.close(); + } + + private Pcap initializePcap() { + try { + List alldevs = new ArrayList(); + StringBuilder errBuf = new StringBuilder(); + int r = Pcap.findAllDevs(alldevs, errBuf); + if (r == Pcap.NOT_OK || alldevs.isEmpty()) { + return null; + } + + PcapIf dev = null; + for (PcapIf device : alldevs) { + if (device.getName().equalsIgnoreCase(_bridge)) { + dev = device; + break; + } + } + + if (dev == null) { + s_logger.debug("Pcap: Can't find device: " + _bridge + " to listen on"); + return null; + } + + int snaplen = 64*1024; + int flags = Pcap.MODE_PROMISCUOUS; + int timeout = 10 * 1000; + Pcap pcap = Pcap.openLive(dev.getName(), snaplen, flags, timeout, errBuf); + if (pcap == null) { + s_logger.debug("Pcap: Can't open " + _bridge); + return null; + } + + PcapBpfProgram program = new PcapBpfProgram(); + String expr = "dst port 68 or 67"; + int optimize = 0; + int netmask = 0xFFFFFF00; + if (pcap.compile(program, expr, optimize, netmask) != Pcap.OK) { + s_logger.debug("Pcap: can't compile BPF"); + return null; + } + + if(pcap.setFilter(program) != Pcap.OK) { + s_logger.debug("Pcap: Can't set filter"); + return null; + } + return pcap; + } catch (Exception e) { + s_logger.debug("Failed to initialized: " + e.toString()); + } + return null; + } + + public void run() { + while (_loop) { + try { + _pcapedDev = initializePcap(); + if (_pcapedDev == null) { + return; + } + + PcapPacketHandler jpacketHandler = new PcapPacketHandler() { + public void nextPacket(PcapPacket packet, String user) { + Udp u = new Udp(); + if (packet.hasHeader(u)) { + int offset = u.getOffset() + u.getLength(); + _executor.execute(new DhcpPacketParser(packet, offset, u.length() - u.getLength(), _manager)); + } + } + }; + s_logger.debug("Starting DHCP snooping on " + _bridge); + int retValue = _pcapedDev.loop(-1, jpacketHandler, "pcapPacketHandler"); + if ( retValue == -1) { + s_logger.debug("Pcap: failed to set loop handler"); + } else if ( retValue == -2 && !_loop) { + s_logger.debug("Pcap: terminated"); + return; + } + _pcapedDev.close(); + } catch (Exception e) { + s_logger.debug("Pcap error:" + e.toString()); + } + } + } + } + + static public void main(String args[]) { + s_logger.addAppender(new org.apache.log4j.ConsoleAppender(new org.apache.log4j.PatternLayout(), "System.out")); + final DhcpSnooperImpl manager = new DhcpSnooperImpl("cloudbr0", 10000); + s_logger.debug(manager.getIPAddr("02:00:4c:66:00:03", "i-2-5-VM")); + manager.stop(); + + } + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + // TODO configure timeout here + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public String getName() { + return "DhcpSnooperImpl"; + } + +} diff --git a/agent/src/com/cloud/agent/resource/computing/CloudZonesComputingResource.java b/agent/src/com/cloud/agent/resource/computing/CloudZonesComputingResource.java new file mode 100644 index 00000000000..1144f75cb77 --- /dev/null +++ b/agent/src/com/cloud/agent/resource/computing/CloudZonesComputingResource.java @@ -0,0 +1,312 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +package com.cloud.agent.resource.computing; + + +import java.net.InetAddress; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; +import org.libvirt.Connect; +import org.libvirt.Domain; +import org.libvirt.LibvirtException; + + + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.StartAnswer; +import com.cloud.agent.api.StartCommand; + +import com.cloud.agent.api.StopAnswer; +import com.cloud.agent.api.StopCommand; + +import com.cloud.agent.api.routing.SavePasswordCommand; +import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.agent.api.to.NicTO; +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.agent.dhcp.DhcpSnooper; +import com.cloud.agent.dhcp.DhcpSnooperImpl; + + +import com.cloud.agent.resource.computing.LibvirtComputingResource; +import com.cloud.agent.resource.computing.LibvirtConnection; +import com.cloud.agent.resource.computing.LibvirtVMDef; +import com.cloud.agent.resource.computing.LibvirtVMDef.DiskDef; +import com.cloud.agent.resource.computing.LibvirtVMDef.InterfaceDef; +import com.cloud.agent.vmdata.JettyVmDataServer; +import com.cloud.agent.vmdata.VmDataServer; + + +import com.cloud.network.Networks.TrafficType; +import com.cloud.utils.Pair; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.State; + +/** + * Logic specific to the Cloudzones feature + * + * } + **/ + + +public class CloudZonesComputingResource extends LibvirtComputingResource { + private static final Logger s_logger = Logger.getLogger(CloudZonesComputingResource.class); + protected DhcpSnooper _dhcpSnooper; + String _parent; + long _dhcpTimeout; + protected String _hostIp; + protected String _hostMacAddress; + protected VmDataServer _vmDataServer = new JettyVmDataServer(); + + + private void setupDhcpManager(Connect conn, String bridgeName) { + + _dhcpSnooper = new DhcpSnooperImpl(bridgeName, _dhcpTimeout); + + List> macs = new ArrayList>(); + try { + int[] domainIds = conn.listDomains(); + for (int i = 0; i < domainIds.length; i++) { + Domain vm = conn.domainLookupByID(domainIds[i]); + if (vm.getName().startsWith("i-")) { + List nics = getInterfaces(conn, vm.getName()); + InterfaceDef nic = nics.get(0); + macs.add(new Pair(nic.getMacAddress(), vm.getName())); + } + } + } catch (LibvirtException e) { + s_logger.debug("Failed to get MACs: " + e.toString()); + } + + _dhcpSnooper.initializeMacTable(macs); + } + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + boolean success = super.configure(name, params); + if (! success) { + return false; + } + + _parent = (String)params.get("mount.path"); + + try { + _dhcpTimeout = Long.parseLong((String)params.get("dhcp.timeout")); + } catch (Exception e) { + _dhcpTimeout = 1200000; + } + + _hostIp = (String)params.get("host.ip"); + _hostMacAddress = (String)params.get("host.mac.address"); + + try { + Connect conn; + conn = LibvirtConnection.getConnection(); + setupDhcpManager(conn, _guestBridgeName); + } catch (LibvirtException e) { + s_logger.debug("Failed to get libvirt connection:" + e.toString()); + return false; + } + + _dhcpSnooper.configure(name, params); + _vmDataServer.configure(name, params); + + return true; + } + + @Override + protected synchronized StartAnswer execute(StartCommand cmd) { + VirtualMachineTO vmSpec = cmd.getVirtualMachine(); + String vmName = vmSpec.getName(); + LibvirtVMDef vm = null; + + State state = State.Stopped; + Connect conn = null; + try { + conn = LibvirtConnection.getConnection(); + synchronized (_vms) { + _vms.put(vmName, State.Starting); + } + + vm = createVMFromSpec(vmSpec); + + createVbd(conn, vmSpec, vmName, vm); + + createVifs(conn, vmSpec, vm); + + s_logger.debug("starting " + vmName + ": " + vm.toString()); + startDomain(conn, vmName, vm.toString()); + + + NicTO[] nics = vmSpec.getNics(); + for (NicTO nic : nics) { + if (nic.isSecurityGroupEnabled()) { + if (vmSpec.getType() != VirtualMachine.Type.User) { + default_network_rules_for_systemvm(conn, vmName); + } else { + nic.setIp(null); + default_network_rules(conn, vmName, nic, vmSpec.getId()); + } + } + } + + // Attach each data volume to the VM, if there is a deferred attached disk + for (DiskDef disk : vm.getDevices().getDisks()) { + if (disk.isAttachDeferred()) { + attachOrDetachDisk(conn, true, vmName, disk.getDiskPath(), disk.getDiskSeq()); + } + } + + if (vmSpec.getType() == VirtualMachine.Type.User) { + for (NicTO nic: nics) { + if (nic.getType() == TrafficType.Guest) { + InetAddress ipAddr = _dhcpSnooper.getIPAddr(nic.getMac(), vmName); + if (ipAddr == null) { + s_logger.debug("Failed to get guest DHCP ip, stop it"); + StopCommand stpCmd = new StopCommand(vmName); + execute(stpCmd); + return new StartAnswer(cmd, "Failed to get guest DHCP ip, stop it"); + } + s_logger.debug(ipAddr); + nic.setIp(ipAddr.getHostAddress()); + + post_default_network_rules(conn, vmName, nic, vmSpec.getId(), _dhcpSnooper.getDhcpServerIP(), _hostIp, _hostMacAddress); + _vmDataServer.handleVmStarted(cmd.getVirtualMachine()); + } + } + } + + state = State.Running; + return new StartAnswer(cmd); + } catch (Exception e) { + s_logger.warn("Exception ", e); + if (conn != null) { + handleVmStartFailure(conn, vmName, vm); + } + return new StartAnswer(cmd, e.getMessage()); + } finally { + synchronized (_vms) { + if (state != State.Stopped) { + _vms.put(vmName, state); + } else { + _vms.remove(vmName); + } + } + } + } + + protected Answer execute(StopCommand cmd) { + final String vmName = cmd.getVmName(); + + Long bytesReceived = new Long(0); + Long bytesSent = new Long(0); + + State state = null; + synchronized(_vms) { + state = _vms.get(vmName); + _vms.put(vmName, State.Stopping); + } + try { + Connect conn = LibvirtConnection.getConnection(); + + try { + Domain dm = conn.domainLookupByUUID(UUID.nameUUIDFromBytes(vmName.getBytes())); + } catch (LibvirtException e) { + state = State.Stopped; + return new StopAnswer(cmd, null, 0, bytesSent, bytesReceived); + } + + String macAddress = null; + if (vmName.startsWith("i-")) { + List nics = getInterfaces(conn, vmName); + if (!nics.isEmpty()) { + macAddress = nics.get(0).getMacAddress(); + } + } + + destroy_network_rules_for_vm(conn, vmName); + String result = stopVM(conn, vmName, defineOps.UNDEFINE_VM); + + try { + cleanupVnet(conn, cmd.getVnet()); + _dhcpSnooper.cleanup(macAddress, vmName); + _vmDataServer.handleVmStopped(cmd.getVmName()); + } catch (Exception e) { + + } + + state = State.Stopped; + return new StopAnswer(cmd, result, 0, bytesSent, bytesReceived); + } catch (LibvirtException e) { + return new StopAnswer(cmd, e.getMessage()); + } finally { + synchronized(_vms) { + if (state != null) { + _vms.put(vmName, state); + } else { + _vms.remove(vmName); + } + } + } + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof VmDataCommand) { + return execute((VmDataCommand)cmd); + } else if (cmd instanceof SavePasswordCommand) { + return execute ((SavePasswordCommand)cmd); + } + return super.executeRequest(cmd); + } + + protected Answer execute(final VmDataCommand cmd) { + return _vmDataServer.handleVmDataCommand(cmd); + } + + protected Answer execute(final SavePasswordCommand cmd) { + return new Answer(cmd); + } + +} diff --git a/build.xml b/build.xml index bf633f6cdf8..f1f4b7fb1ac 100755 --- a/build.xml +++ b/build.xml @@ -17,25 +17,12 @@ - - - - - - - - - - - - - - - - - - + + + + + diff --git a/build/build-cloud.xml b/build/build-cloud.xml index 24fc26c3e1b..4f6b71d0aeb 100755 --- a/build/build-cloud.xml +++ b/build/build-cloud.xml @@ -218,7 +218,7 @@ - + @@ -280,6 +280,8 @@ + + @@ -529,7 +531,7 @@ - + @@ -629,7 +631,19 @@ - + + + + + + + + + + + + + diff --git a/client/tomcatconf/commands-ext.properties.in b/client/tomcatconf/commands-ext.properties.in new file mode 100644 index 00000000000..565a61d786f --- /dev/null +++ b/client/tomcatconf/commands-ext.properties.in @@ -0,0 +1,38 @@ +#### usage commands +generateUsageRecords=com.cloud.api.commands.GenerateUsageRecordsCmd;1 +listUsageRecords=com.cloud.api.commands.GetUsageRecordsCmd;1 +listUsageTypes=com.cloud.api.commands.ListUsageTypesCmd;1 + +#### external firewall commands +addExternalFirewall=com.cloud.api.commands.AddExternalFirewallCmd;1 +deleteExternalFirewall=com.cloud.api.commands.DeleteExternalFirewallCmd;1 +listExternalFirewalls=com.cloud.api.commands.ListExternalFirewallsCmd;1 + +#### external loadbalancer commands +addExternalLoadBalancer=com.cloud.api.commands.AddExternalLoadBalancerCmd;1 +deleteExternalLoadBalancer=com.cloud.api.commands.DeleteExternalLoadBalancerCmd;1 +listExternalLoadBalancers=com.cloud.api.commands.ListExternalLoadBalancersCmd;1 + +### Network Devices commands +addNetworkDevice=com.cloud.api.commands.AddNetworkDeviceCmd;1 +listNetworkDevice=com.cloud.api.commands.ListNetworkDeviceCmd;1 +deleteNetworkDevice=com.cloud.api.commands.DeleteNetworkDeviceCmd;1 + +#### traffic monitor commands +addTrafficMonitor=com.cloud.api.commands.AddTrafficMonitorCmd;1 +deleteTrafficMonitor=com.cloud.api.commands.DeleteTrafficMonitorCmd;1 +listTrafficMonitors=com.cloud.api.commands.ListTrafficMonitorsCmd;1 + +####Netapp integration commands +createVolumeOnFiler=com.cloud.api.commands.netapp.CreateVolumeCmd;15 +destroyVolumeOnFiler=com.cloud.api.commands.netapp.DestroyVolumeCmd;15 +listVolumesOnFiler=com.cloud.api.commands.netapp.ListVolumesCmd;15 +createLunOnFiler=com.cloud.api.commands.netapp.CreateLunCmd;15 +destroyLunOnFiler=com.cloud.api.commands.netapp.DestroyLunCmd;15 +listLunsOnFiler=com.cloud.api.commands.netapp.ListLunsCmd;15 +associateLun=com.cloud.api.commands.netapp.AssociateLunCmd;15 +dissociateLun=com.cloud.api.commands.netapp.DissociateLunCmd;15 +createPool=com.cloud.api.commands.netapp.CreatePoolCmd;15 +deletePool=com.cloud.api.commands.netapp.DeletePoolCmd;15 +modifyPool=com.cloud.api.commands.netapp.ModifyPoolCmd;15 +listPools=com.cloud.api.commands.netapp.ListPoolsCmd;15 diff --git a/client/tomcatconf/components-cloudzones.xml.in b/client/tomcatconf/components-cloudzones.xml.in new file mode 100755 index 00000000000..0ca02e2035c --- /dev/null +++ b/client/tomcatconf/components-cloudzones.xml.in @@ -0,0 +1,15 @@ + + + + + + + + + + + + true + + + diff --git a/client/tomcatconf/components-premium.xml.in b/client/tomcatconf/components-premium.xml.in new file mode 100755 index 00000000000..82a903864c4 --- /dev/null +++ b/client/tomcatconf/components-premium.xml.in @@ -0,0 +1,77 @@ + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + diff --git a/client/tomcatconf/simulator.properties.in b/client/tomcatconf/simulator.properties.in new file mode 100644 index 00000000000..e515cacd4e1 --- /dev/null +++ b/client/tomcatconf/simulator.properties.in @@ -0,0 +1,14 @@ +host=127.0.0.1 +port=8250 +workers=3 +zone=1 +pod=1 +run=13 +sequence=r +agent.save.path=/tmp/agents +latency=2 +latency.start.range=2 +latency.end.range=2 +delay.distribution={(5,10); (5,10); (5,10); (5,10); (5,10); (5,10); (5,10); (5,10); (5,10); (10,120)} +property.scan.enabled=1 +property.scan.interval=300 \ No newline at end of file diff --git a/cloud.spec b/cloud.spec index 2bc7392333a..a9e1ef853ab 100644 --- a/cloud.spec +++ b/cloud.spec @@ -36,7 +36,7 @@ BuildRequires: glibc-devel BuildRequires: /usr/bin/mkisofs BuildRequires: MySQL-python -%global _premium %(tar jtvmf %{SOURCE0} '*/cloudstack-proprietary/' --occurrence=1 2>/dev/null | wc -l) +#%global _premium %(tar jtvmf %{SOURCE0} '*/cloudstack-proprietary/' --occurrence=1 2>/dev/null | wc -l) %description This is the Cloud.com Stack, a highly-scalable elastic, open source, @@ -161,6 +161,8 @@ Requires: /usr/bin/ssh-keygen Requires: mkisofs Requires: MySQL-python Requires: python-paramiko +Requires: ipmitool +Requires: %{name}-utils = %{version} Group: System Environment/Libraries %description client The Cloud.com management server is the central point of coordination, @@ -223,13 +225,21 @@ Requires: /sbin/service Requires: /sbin/chkconfig Group: System Environment/Libraries +%if 0%{?rhel} >= 6 +Requires: cloud-kvm +%else Requires: kvm +%endif %if 0%{?fedora} >= 14 Requires: cloud-qemu-kvm Requires: cloud-qemu-img %endif +%if 0%{?rhel} >= 6 +Requires: cloud-qemu-img +%endif + %if 0%{?rhel} >= 5 Requires: qemu-img %endif @@ -290,64 +300,37 @@ Group: System Environment/Libraries The Cloud.com command line tools contain a few Python modules that can call cloudStack APIs. -%if %{_premium} - -%package premium-agent -Summary: Cloud.com premium agent -Requires: cloud-agent -Requires: jnetpcap -Group: System Environment/Libraries -%description premium-agent -The Cloud.com premium agent - -%package test -Summary: Cloud.com test suite -Requires: java >= 1.6.0 -Requires: %{name}-utils = %{version}, %{name}-deps = %{version}, wget -Group: System Environment/Libraries -Obsoletes: vmops-test < %{version}-%{release} -%description test -The Cloud.com test package contains a suite of automated tests -that the very much appreciated QA team at Cloud.com constantly -uses to help increase the quality of the Cloud.com Stack. - -%package premium -Summary: Cloud.com premium components -Obsoletes: vmops-premium < %{version}-%{release} -Provides: %{name}-premium-plugin-zynga = %{version}-%{release} -Obsoletes: %{name}-premium-plugin-zynga < %{version}-%{release} -Provides: %{name}-premium-vendor-zynga = %{version}-%{release} -Obsoletes: %{name}-premium-vendor-zynga < %{version}-%{release} -Requires: java >= 1.6.0 -Requires: ipmitool -Requires: %{name}-utils = %{version} -License: CSL 1.1 -Group: System Environment/Libraries -%description premium -The Cloud.com premium components expand the range of features on your Cloud.com Stack. - -%package usage -Summary: Cloud.com usage monitor -Obsoletes: vmops-usage < %{version}-%{release} -Requires: java >= 1.6.0 -Requires: %{name}-utils = %{version}, %{name}-core = %{version}, %{name}-deps = %{version}, %{name}-server = %{version}, %{name}-premium = %{version}, %{name}-daemonize = %{version} -Requires: %{name}-setup = %{version} -Requires: %{name}-client = %{version} -License: CSL 1.1 -Group: System Environment/Libraries -%description usage -The Cloud.com usage monitor provides usage accounting across the entire cloud for -cloud operators to charge based on usage parameters. - -%endif +#%if %{_premium} +# +#%package test +#Summary: Cloud.com test suite +#Requires: java >= 1.6.0 +#Requires: %{name}-utils = %{version}, %{name}-deps = %{version}, wget +#Group: System Environment/Libraries +#Obsoletes: vmops-test < %{version}-%{release} +#%description test +#The Cloud.com test package contains a suite of automated tests +#that the very much appreciated QA team at Cloud.com constantly +#uses to help increase the quality of the Cloud.com Stack. +# +#%package usage +#Summary: Cloud.com usage monitor +#Obsoletes: vmops-usage < %{version}-%{release} +#Requires: java >= 1.6.0 +#Requires: %{name}-utils = %{version}, %{name}-core = %{version}, %{name}-deps = %{version}, %{name}-server = %{version}, %{name}-premium = %{version}, %{name}-daemonize = %{version} +#Requires: %{name}-setup = %{version} +#Requires: %{name}-client = %{version} +#License: CSL 1.1 +#Group: System Environment/Libraries +#%description usage +#The Cloud.com usage monitor provides usage accounting across the entire cloud for +#cloud operators to charge based on usage parameters. +# +#%endif %prep -%if %{_premium} -echo Doing premium build -%else -echo Doing open source build -%endif +echo Doing CloudStack build %setup -q -n %{name}-%{_ver} @@ -381,11 +364,9 @@ id %{name} > /dev/null 2>&1 || /usr/sbin/useradd -M -c "Cloud.com unprivileged u -r -s /bin/sh -d %{_sharedstatedir}/%{name}/management %{name}|| true # set max file descriptors for cloud user to 4096 -grep "cloud" /etc/security/limits.conf &>/dev/null -if [ $? -eq 1 ]; then - echo "cloud hard nofile 4096" >> /etc/security/limits.conf - echo "cloud soft nofile 4096" >> /etc/security/limits.conf -fi +sed -i /"cloud"/d /etc/security/limits.conf +echo "cloud hard nofile 4096" >> /etc/security/limits.conf +echo "cloud soft nofile 4096" >> /etc/security/limits.conf rm -rf %{_localstatedir}/cache/%{name} # user harcoded here, also hardcoded on wscript @@ -409,28 +390,28 @@ fi -%if %{_premium} - -%preun usage -if [ "$1" == "0" ] ; then - /sbin/chkconfig --del %{name}-usage > /dev/null 2>&1 || true - /sbin/service %{name}-usage stop > /dev/null 2>&1 || true -fi - -%pre usage -id %{name} > /dev/null 2>&1 || /usr/sbin/useradd -M -c "Cloud.com unprivileged user" \ - -r -s /bin/sh -d %{_sharedstatedir}/%{name}/management %{name}|| true -# user harcoded here, also hardcoded on wscript - -%post usage -if [ "$1" == "1" ] ; then - /sbin/chkconfig --add %{name}-usage > /dev/null 2>&1 || true - /sbin/chkconfig --level 345 %{name}-usage on > /dev/null 2>&1 || true -else - /sbin/service %{name}-usage condrestart >/dev/null 2>&1 || true -fi - -%endif +#%if %{_premium} +# +#%preun usage +#if [ "$1" == "0" ] ; then +# /sbin/chkconfig --del %{name}-usage > /dev/null 2>&1 || true +# /sbin/service %{name}-usage stop > /dev/null 2>&1 || true +#fi +# +#%pre usage +#id %{name} > /dev/null 2>&1 || /usr/sbin/useradd -M -c "Cloud.com unprivileged user" \ +# -r -s /bin/sh -d %{_sharedstatedir}/%{name}/management %{name}|| true +## user harcoded here, also hardcoded on wscript +# +#%post usage +#if [ "$1" == "1" ] ; then +# /sbin/chkconfig --add %{name}-usage > /dev/null 2>&1 || true +# /sbin/chkconfig --level 345 %{name}-usage on > /dev/null 2>&1 || true +#else +# /sbin/service %{name}-usage condrestart >/dev/null 2>&1 || true +#fi +# +#%endif %pre agent-scripts id %{name} > /dev/null 2>&1 || /usr/sbin/useradd -M -c "Cloud.com unprivileged user" \ @@ -484,6 +465,8 @@ fi %files server %defattr(0644,root,root,0755) %{_javadir}/%{name}-server.jar +%{_javadir}/%{name}-vmware-base.jar +%{_javadir}/%{name}-ovm.jar %{_sysconfdir}/%{name}/server/* %files agent-scripts @@ -524,10 +507,7 @@ fi %{_javadir}/%{name}-jsch-0.1.42.jar %{_javadir}/%{name}-iControl.jar %{_javadir}/%{name}-manageontap.jar - -%defattr(0644,root,root,0755) -%{_javadir}/%{name}-premium/*.jar -%exclude %{_javadir}/%{name}-premium/servlet-api.jar +%{_javadir}/vmware*.jar %files core %defattr(0644,root,root,0755) @@ -555,9 +535,7 @@ fi %files client %defattr(0644,root,root,0775) %{_sysconfdir}/%{name}/management/* -%if %{_premium} -%exclude %{_sysconfdir}/%{name}/management/*premium* -%endif +%{_sysconfdir}/%{name}/management/*premium* %config(noreplace) %attr(0640,root,%{name}) %{_sysconfdir}/%{name}/management/db.properties %config(noreplace) %{_sysconfdir}/%{name}/management/log4j-%{name}.xml %config(noreplace) %{_sysconfdir}/%{name}/management/tomcat6.conf @@ -596,6 +574,7 @@ fi %{_libdir}/%{name}/agent/images %attr(0755,root,root) %{_bindir}/%{name}-setup-agent %dir %attr(0770,root,root) %{_localstatedir}/log/%{name}/agent +%attr(0755,root,root) %{_bindir}/mycloud-setup-agent %files console-proxy %defattr(0644,root,root,0755) @@ -618,44 +597,27 @@ fi %files baremetal-agent %attr(0755,root,root) %{_bindir}/cloud-setup-baremetal -%if %{_premium} - -%files premium-agent -%{_javadir}/cloud-agent-extras.jar -%attr(0755,root,root) %{_bindir}/mycloud-setup-agent - -%files test -%defattr(0644,root,root,0755) -%attr(0755,root,root) %{_bindir}/%{name}-run-test -%{_javadir}/%{name}-test.jar -%{_sharedstatedir}/%{name}/test/* -%{_libdir}/%{name}/test/* -%{_sysconfdir}/%{name}/test/* - -%files premium -%defattr(0644,root,root,0755) -%{_javadir}/%{name}-core-extras.jar -%{_javadir}/%{name}-server-extras.jar -%{_javadir}/%{name}-vmware-base.jar -%{_javadir}/%{name}-ovm.jar -# maintain the following list in sync with files agent-scripts -%{_libdir}/%{name}/agent/premium-scripts/* -%{_sysconfdir}/%{name}/management/commands-ext.properties -%{_sysconfdir}/%{name}/management/components-premium.xml -%{_datadir}/%{name}/setup/create-database-premium.sql -%{_datadir}/%{name}/setup/create-schema-premium.sql - -%files usage -%defattr(0644,root,root,0775) -%{_javadir}/%{name}-usage.jar -%attr(0755,root,root) %{_initrddir}/%{name}-usage -%attr(0755,root,root) %{_libexecdir}/usage-runner -%dir %attr(0770,root,%{name}) %{_localstatedir}/log/%{name}/usage -%{_sysconfdir}/%{name}/usage/usage-components.xml -%config(noreplace) %{_sysconfdir}/%{name}/usage/log4j-%{name}_usage.xml -%config(noreplace) %attr(0640,root,%{name}) %{_sysconfdir}/%{name}/usage/db.properties - -%endif +#%if %{_premium} +# +#%files test +#%defattr(0644,root,root,0755) +#%attr(0755,root,root) %{_bindir}/%{name}-run-test +#%{_javadir}/%{name}-test.jar +#%{_sharedstatedir}/%{name}/test/* +#%{_libdir}/%{name}/test/* +#%{_sysconfdir}/%{name}/test/* +# +#%files usage +#%defattr(0644,root,root,0775) +#%{_javadir}/%{name}-usage.jar +#%attr(0755,root,root) %{_initrddir}/%{name}-usage +#%attr(0755,root,root) %{_libexecdir}/usage-runner +#%dir %attr(0770,root,%{name}) %{_localstatedir}/log/%{name}/usage +#%{_sysconfdir}/%{name}/usage/usage-components.xml +#%config(noreplace) %{_sysconfdir}/%{name}/usage/log4j-%{name}_usage.xml +#%config(noreplace) %attr(0640,root,%{name}) %{_sysconfdir}/%{name}/usage/db.properties +# +#%endif %changelog * Mon May 3 2010 Manuel Amador (Rudd-O) 1.9.12 diff --git a/core/src/com/cloud/agent/api/DirectNetworkUsageAnswer.java b/core/src/com/cloud/agent/api/DirectNetworkUsageAnswer.java new file mode 100644 index 00000000000..c08b9e90b07 --- /dev/null +++ b/core/src/com/cloud/agent/api/DirectNetworkUsageAnswer.java @@ -0,0 +1,59 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.agent.api; + +import java.util.HashMap; +import java.util.Map; + +public class DirectNetworkUsageAnswer extends Answer { + + Map ipBytesSentAndReceived; + + protected DirectNetworkUsageAnswer() { + } + + public DirectNetworkUsageAnswer(Command command) { + super(command); + this.ipBytesSentAndReceived = new HashMap(); + } + + public DirectNetworkUsageAnswer(Command command, Exception e) { + super(command, e); + this.ipBytesSentAndReceived = null; + } + + public void put(String ip, long[] bytesSentAndReceived) { + this.ipBytesSentAndReceived.put(ip, bytesSentAndReceived); + } + + public long[] get(String ip) { + long[] entry = ipBytesSentAndReceived.get(ip); + if (entry == null) { + ipBytesSentAndReceived.put(ip, new long[]{0, 0}); + return ipBytesSentAndReceived.get(ip); + } else { + return entry; + } + } + + public Map getIpBytesSentAndReceived() { + return ipBytesSentAndReceived; + } +} diff --git a/core/src/com/cloud/agent/api/DirectNetworkUsageCommand.java b/core/src/com/cloud/agent/api/DirectNetworkUsageCommand.java new file mode 100644 index 00000000000..3d8e40e954d --- /dev/null +++ b/core/src/com/cloud/agent/api/DirectNetworkUsageCommand.java @@ -0,0 +1,65 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.agent.api; + +import java.util.Date; +import java.util.List; + +public class DirectNetworkUsageCommand extends Command { + + private List publicIps; + private Date start; + private Date end; + + public DirectNetworkUsageCommand(List publicIps, Date start, Date end) { + this.setPublicIps(publicIps); + this.setStart(start); + this.setEnd(end); + } + + @Override + public boolean executeInSequence() { + return false; + } + + public void setPublicIps(List publicIps) { + this.publicIps = publicIps; + } + + public List getPublicIps() { + return publicIps; + } + + public void setStart(Date start) { + this.start = start; + } + + public Date getStart() { + return start; + } + + public void setEnd(Date end) { + this.end = end; + } + + public Date getEnd() { + return end; + } +} diff --git a/core/src/com/cloud/agent/api/ExternalNetworkResourceUsageAnswer.java b/core/src/com/cloud/agent/api/ExternalNetworkResourceUsageAnswer.java new file mode 100644 index 00000000000..d22fd4d1147 --- /dev/null +++ b/core/src/com/cloud/agent/api/ExternalNetworkResourceUsageAnswer.java @@ -0,0 +1,44 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.agent.api; + +import java.util.HashMap; +import java.util.Map; + +public class ExternalNetworkResourceUsageAnswer extends Answer { + public Map ipBytes; + public Map guestVlanBytes; + + protected ExternalNetworkResourceUsageAnswer() { + } + + public ExternalNetworkResourceUsageAnswer(Command command) { + super(command); + this.ipBytes = new HashMap(); + this.guestVlanBytes = new HashMap(); + } + + public ExternalNetworkResourceUsageAnswer(Command command, Exception e) { + super(command, e); + this.ipBytes = null; + this.guestVlanBytes = null; + } + +} diff --git a/core/src/com/cloud/agent/api/ExternalNetworkResourceUsageCommand.java b/core/src/com/cloud/agent/api/ExternalNetworkResourceUsageCommand.java new file mode 100644 index 00000000000..03c6825f004 --- /dev/null +++ b/core/src/com/cloud/agent/api/ExternalNetworkResourceUsageCommand.java @@ -0,0 +1,31 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.agent.api; + +public class ExternalNetworkResourceUsageCommand extends Command { + + public ExternalNetworkResourceUsageCommand() { + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/com/cloud/agent/api/RecurringNetworkUsageAnswer.java b/core/src/com/cloud/agent/api/RecurringNetworkUsageAnswer.java new file mode 100644 index 00000000000..55ea6901ae6 --- /dev/null +++ b/core/src/com/cloud/agent/api/RecurringNetworkUsageAnswer.java @@ -0,0 +1,37 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.agent.api; + + +public class RecurringNetworkUsageAnswer extends Answer { + + + protected RecurringNetworkUsageAnswer() { + } + + public RecurringNetworkUsageAnswer(Command command) { + super(command); + } + + public RecurringNetworkUsageAnswer(Command command, Exception e) { + super(command, e); + } + +} diff --git a/core/src/com/cloud/agent/api/RecurringNetworkUsageCommand.java b/core/src/com/cloud/agent/api/RecurringNetworkUsageCommand.java new file mode 100644 index 00000000000..ae4a2f800ef --- /dev/null +++ b/core/src/com/cloud/agent/api/RecurringNetworkUsageCommand.java @@ -0,0 +1,39 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.agent.api; + + +public class RecurringNetworkUsageCommand extends Command implements CronCommand{ + int interval; + + public RecurringNetworkUsageCommand(int interval) { + this.interval = interval; + } + + public int getInterval() { + return interval; + } + + @Override + public boolean executeInSequence() { + return false; + } + +} diff --git a/core/src/com/cloud/agent/api/StartupVMMAgentCommand.java b/core/src/com/cloud/agent/api/StartupVMMAgentCommand.java new file mode 100644 index 00000000000..d396033a7fa --- /dev/null +++ b/core/src/com/cloud/agent/api/StartupVMMAgentCommand.java @@ -0,0 +1,87 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.agent.api; + +import com.cloud.agent.api.Command; +import com.cloud.host.Host; + +/** + * Implementation of bootstrap command sent from management server to agent running on + * System Center Virtual Machine Manager host + **/ + +public class StartupVMMAgentCommand extends Command { + Host.Type type; + long dataCenter; + Long pod; + String clusterName; + String guid; + String managementServerIP; + String port; + String version; + + public StartupVMMAgentCommand() { + + } + + public StartupVMMAgentCommand(long dataCenter, Long pod, String clusterName, String guid, String managementServerIP, String port, String version) { + super(); + this.dataCenter = dataCenter; + this.pod = pod; + this.clusterName = clusterName; + this.guid = guid; + this.type = Host.Type.Routing; + this.managementServerIP = managementServerIP; + this.port = port; + } + + public long getDataCenter() { + return dataCenter; + } + + public Long getPod() { + return pod; + } + + public String getClusterName() { + return clusterName; + } + + public String getGuid() { + return guid; + } + + public String getManagementServerIP() { + return managementServerIP; + } + + public String getport() { + return port; + } + + public void setVersion(String version) { + this.version = version; + } + + @Override + public boolean executeInSequence() { + return false; + } +} \ No newline at end of file diff --git a/core/src/com/cloud/hypervisor/hyperv/resource/HypervDummyResourceBase.java b/core/src/com/cloud/hypervisor/hyperv/resource/HypervDummyResourceBase.java new file mode 100644 index 00000000000..61ca5a74b37 --- /dev/null +++ b/core/src/com/cloud/hypervisor/hyperv/resource/HypervDummyResourceBase.java @@ -0,0 +1,67 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.hypervisor.hyperv.resource; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.host.Host.Type; +import com.cloud.resource.ServerResource; +import com.cloud.resource.ServerResourceBase; + +/** + * Implementation of dummy resource to be returned from discoverer + **/ + +public class HypervDummyResourceBase extends ServerResourceBase implements + ServerResource { + + @Override + public Type getType() { + // TODO Auto-generated method stub + return null; + } + + @Override + public StartupCommand[] initialize() { + // TODO Auto-generated method stub + return null; + } + + @Override + public PingCommand getCurrentStatus(long id) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Answer executeRequest(Command cmd) { + // TODO Auto-generated method stub + return null; + } + + @Override + protected String getDefaultScriptsDir() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/core/src/com/cloud/hypervisor/hyperv/resource/HypervResource.java b/core/src/com/cloud/hypervisor/hyperv/resource/HypervResource.java new file mode 100755 index 00000000000..1a710aea272 --- /dev/null +++ b/core/src/com/cloud/hypervisor/hyperv/resource/HypervResource.java @@ -0,0 +1,954 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.hypervisor.hyperv.resource; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStreamReader; +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.AttachIsoCommand; +import com.cloud.agent.api.AttachVolumeCommand; +import com.cloud.agent.api.BackupSnapshotCommand; +import com.cloud.agent.api.CheckHealthCommand; +import com.cloud.agent.api.CheckOnHostCommand; +import com.cloud.agent.api.CheckVirtualMachineCommand; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand; +import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand; +import com.cloud.agent.api.CreateVolumeFromSnapshotCommand; +import com.cloud.agent.api.DeleteSnapshotBackupCommand; +import com.cloud.agent.api.DeleteSnapshotsDirCommand; +import com.cloud.agent.api.DeleteStoragePoolCommand; +import com.cloud.agent.api.GetHostStatsAnswer; +import com.cloud.agent.api.GetHostStatsCommand; +import com.cloud.agent.api.GetStorageStatsAnswer; +import com.cloud.agent.api.GetStorageStatsCommand; +import com.cloud.agent.api.GetVmStatsCommand; +import com.cloud.agent.api.GetVncPortCommand; +import com.cloud.agent.api.HostStatsEntry; +import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.ManageSnapshotCommand; +import com.cloud.agent.api.MigrateCommand; +import com.cloud.agent.api.ModifySshKeysCommand; +import com.cloud.agent.api.ModifyStoragePoolAnswer; +import com.cloud.agent.api.ModifyStoragePoolCommand; +import com.cloud.agent.api.NetworkUsageCommand; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.PingTestCommand; +import com.cloud.agent.api.PoolEjectCommand; +import com.cloud.agent.api.PrepareForMigrationCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.RebootCommand; +import com.cloud.agent.api.RebootRouterCommand; +import com.cloud.agent.api.SetupCommand; +import com.cloud.agent.api.StartAnswer; +import com.cloud.agent.api.StartCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.agent.api.StopCommand; +import com.cloud.agent.api.ValidateSnapshotCommand; +import com.cloud.agent.api.check.CheckSshAnswer; +import com.cloud.agent.api.check.CheckSshCommand; +import com.cloud.agent.api.routing.DhcpEntryCommand; +import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.LoadBalancerConfigCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; +import com.cloud.agent.api.routing.SavePasswordCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetStaticNatRulesCommand; +import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.agent.api.routing.VpnUsersCfgCommand; +import com.cloud.agent.api.storage.CopyVolumeCommand; +import com.cloud.agent.api.storage.CreateAnswer; +import com.cloud.agent.api.storage.CreateCommand; +import com.cloud.agent.api.storage.DestroyCommand; +import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; +import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; +import com.cloud.agent.api.to.StorageFilerTO; +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.agent.api.to.VolumeTO; +import com.cloud.host.Host.Type; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.hypervisor.vmware.resource.SshHelper; +import com.cloud.resource.ServerResource; +import com.cloud.resource.ServerResourceBase; +import com.cloud.serializer.GsonHelper; +import com.cloud.storage.Volume; +import com.cloud.storage.template.TemplateInfo; +import com.cloud.utils.Pair; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.DiskProfile; +import com.cloud.vm.VirtualMachine.State; +import com.google.gson.Gson; + +/** + * Implementation of resource base class for Hyper-V hypervisor + **/ + +@Local(value={ServerResource.class}) +public class HypervResource extends ServerResourceBase implements ServerResource{ + private String _dcId; + private String _podId; + private String _clusterId; + private String _guid; + private String _name; + private static final Logger s_logger = Logger.getLogger(HypervResource.class); + private IAgentControl agentControl; + private volatile Boolean _wakeUp = false; + protected Gson _gson; + protected HashMap _vms = new HashMap(512); + protected final int DEFAULT_DOMR_SSHPORT = 3922; + + public HypervResource() + { + _gson = GsonHelper.getGsonLogger(); + } + + @Override + public Answer executeRequest(Command cmd) { + + if (cmd instanceof CreateCommand) { + return execute((CreateCommand) cmd); + } else if (cmd instanceof SetPortForwardingRulesCommand) { + s_logger.info("SCVMM agent recived command SetPortForwardingRulesCommand"); + //return execute((SetPortForwardingRulesCommand) cmd); + } else if (cmd instanceof SetStaticNatRulesCommand) { + s_logger.info("SCVMM agent recived command SetStaticNatRulesCommand"); + //return execute((SetStaticNatRulesCommand) cmd); + }else if (cmd instanceof LoadBalancerConfigCommand) { + s_logger.info("SCVMM agent recived command SetStaticNatRulesCommand"); + //return execute((LoadBalancerConfigCommand) cmd); + } else if (cmd instanceof IpAssocCommand) { + s_logger.info("SCVMM agent recived command IPAssocCommand"); + //return execute((IPAssocCommand) cmd); + } else if (cmd instanceof SavePasswordCommand) { + s_logger.info("SCVMM agent recived command SavePasswordCommand"); + //return execute((SavePasswordCommand) cmd); + } else if (cmd instanceof DhcpEntryCommand) { + return execute((DhcpEntryCommand) cmd); + } else if (cmd instanceof VmDataCommand) { + //return execute((VmDataCommand) cmd); + } else if (cmd instanceof ReadyCommand) { + s_logger.info("SCVMM agent recived ReadyCommand: " + _gson.toJson(cmd)); + return new ReadyAnswer((ReadyCommand) cmd); + } else if (cmd instanceof GetHostStatsCommand) { + return execute((GetHostStatsCommand) cmd); + } else if (cmd instanceof GetVmStatsCommand) { + s_logger.info("SCVMM agent recived command GetVmStatsCommand"); + //return execute((GetVmStatsCommand) cmd); + } else if (cmd instanceof CheckHealthCommand) { + //return execute((CheckHealthCommand) cmd); + } else if (cmd instanceof StopCommand) { + //return execute((StopCommand) cmd); + } else if (cmd instanceof RebootRouterCommand) { + //return execute((RebootRouterCommand) cmd); + } else if (cmd instanceof RebootCommand) { + //return execute((RebootCommand) cmd); + } else if (cmd instanceof CheckVirtualMachineCommand) { + s_logger.info("SCVMM agent recived command CheckVirtualMachineCommand"); + //return execute((CheckVirtualMachineCommand) cmd); + } else if (cmd instanceof PrepareForMigrationCommand) { + //return execute((PrepareForMigrationCommand) cmd); + } else if (cmd instanceof MigrateCommand) { + //return execute((MigrateCommand) cmd); + } else if (cmd instanceof DestroyCommand) { + //return execute((DestroyCommand) cmd); + } else if (cmd instanceof ModifyStoragePoolCommand) { + return execute((ModifyStoragePoolCommand) cmd); + } else if (cmd instanceof DeleteStoragePoolCommand) { + s_logger.info("SCVMM agent recived command DeleteStoragePoolCommand"); + Answer answer = new Answer(cmd, true, "success"); + return answer; + //return execute((DeleteStoragePoolCommand) cmd); + } else if (cmd instanceof CopyVolumeCommand) { + s_logger.info("SCVMM agent recived command CopyVolumeCommand"); + //return execute((CopyVolumeCommand) cmd); + } else if (cmd instanceof AttachVolumeCommand) { + s_logger.info("SCVMM agent recived command AttachVolumeCommand"); + //return execute((AttachVolumeCommand) cmd); + } else if (cmd instanceof AttachIsoCommand) { + //return execute((AttachIsoCommand) cmd); + } else if (cmd instanceof ValidateSnapshotCommand) { + //return execute((ValidateSnapshotCommand) cmd); + } else if (cmd instanceof ManageSnapshotCommand) { + //return execute((ManageSnapshotCommand) cmd); + } else if (cmd instanceof BackupSnapshotCommand) { + //return execute((BackupSnapshotCommand) cmd); + } else if (cmd instanceof DeleteSnapshotBackupCommand) { + //return execute((DeleteSnapshotBackupCommand) cmd); + } else if (cmd instanceof CreateVolumeFromSnapshotCommand) { + //return execute((CreateVolumeFromSnapshotCommand) cmd); + } else if (cmd instanceof DeleteSnapshotsDirCommand) { + //return execute((DeleteSnapshotsDirCommand) cmd); + } else if (cmd instanceof CreatePrivateTemplateFromVolumeCommand) { + //return execute((CreatePrivateTemplateFromVolumeCommand) cmd); + } else if (cmd instanceof CreatePrivateTemplateFromSnapshotCommand) { + //return execute((CreatePrivateTemplateFromSnapshotCommand) cmd); + } else if (cmd instanceof GetStorageStatsCommand) { + return execute((GetStorageStatsCommand) cmd); + } else if (cmd instanceof PrimaryStorageDownloadCommand) { + s_logger.info("SCVMM agent recived command PrimaryStorageDownloadCommand"); + return execute((PrimaryStorageDownloadCommand) cmd); + } else if (cmd instanceof GetVncPortCommand) { + //return execute((GetVncPortCommand) cmd); + } else if (cmd instanceof SetupCommand) { + //return execute((SetupCommand) cmd); + } else if (cmd instanceof MaintainCommand) { + //return execute((MaintainCommand) cmd); + } else if (cmd instanceof PingTestCommand) { + s_logger.info("SCVMM agent recived command PingTestCommand"); + //return execute((PingTestCommand) cmd); + } else if (cmd instanceof CheckOnHostCommand) { + s_logger.info("SCVMM agent recived command CheckOnHostCommand"); + //return execute((CheckOnHostCommand) cmd); + } else if (cmd instanceof ModifySshKeysCommand) { + //return execute((ModifySshKeysCommand) cmd); + } else if (cmd instanceof PoolEjectCommand) { + //return execute((PoolEjectCommand) cmd); + } else if (cmd instanceof NetworkUsageCommand) { + //return execute((NetworkUsageCommand) cmd); + } else if (cmd instanceof StartCommand) { + return execute((StartCommand) cmd); + } else if (cmd instanceof RemoteAccessVpnCfgCommand) { + //return execute((RemoteAccessVpnCfgCommand) cmd); + } else if (cmd instanceof VpnUsersCfgCommand) { + //return execute((VpnUsersCfgCommand) cmd); + } else if (cmd instanceof CheckSshCommand) { + return execute((CheckSshCommand)cmd); + } else { + s_logger.info("SCVMM agent recived unimplemented command: " + _gson.toJson(cmd)); + return Answer.createUnsupportedCommandAnswer(cmd); + } + + return Answer.createUnsupportedCommandAnswer(cmd); + } + + public PrimaryStorageDownloadAnswer execute(PrimaryStorageDownloadCommand cmd) { + + if (s_logger.isInfoEnabled()) { + s_logger.info("Executing resource PrimaryStorageDownloadCommand: " + _gson.toJson(cmd)); + } + + try { + String secondaryStorageUrl = cmd.getSecondaryStorageUrl(); + String primaryStroageUrl = cmd.getPrimaryStorageUrl(); + String templateUuidName =null; + assert ((primaryStroageUrl != null) && (secondaryStorageUrl != null)); + // FIXME: paths and system vm name are hard coded + String templateUrl = cmd.getUrl(); + String templatePath = templateUrl.replace('/', '\\'); + templatePath = templatePath.substring(4); + if (!templatePath.endsWith(".vhd")) { + String templateName = cmd.getName(); + templateUuidName = UUID.nameUUIDFromBytes((templateName + "@" + cmd.getPoolUuid()).getBytes()).toString(); + if (!templatePath.endsWith("\\")) { + templatePath = templatePath + "\\"; + } + //templatePath = templatePath + templateUuidName + ".vhd"; + templatePath = templatePath + "systemvm.vhd"; + } + + s_logger.info("template URL: "+ templateUrl + "template name: "+ cmd.getName() + " sec storage " + secondaryStorageUrl+ " pri storage" + primaryStroageUrl); + + StringBuilder cmdStr = new StringBuilder("cmd /c powershell.exe "); + cmdStr.append("copy-item '"); + cmdStr.append(templatePath.toCharArray()); + cmdStr.append("' 'C:\\programdata\\Virtual Machine Manager Library Files\\VHDs\\';"); + + s_logger.info("Running command: " + cmdStr); + Process p = Runtime.getRuntime().exec(cmdStr.toString()); + p.getOutputStream().close(); + + InputStreamReader temperrReader = new InputStreamReader(new BufferedInputStream(p.getErrorStream())); + BufferedReader errreader = new BufferedReader(temperrReader); + if (errreader.ready()) { + String errorOutput = new String(""); + s_logger.info("errors found while running cmdlet: " + cmdStr.toString()); + while (true){ + String errline = errreader.readLine(); + if (errline == null) { + break; + } + errorOutput = errorOutput + errline; + } + s_logger.info(errorOutput); + } + + p.getErrorStream().close(); + InputStreamReader tempReader = new InputStreamReader(new BufferedInputStream(p.getInputStream())); + BufferedReader reader = new BufferedReader(tempReader); + + String output = new String(""); + + while (true){ + String line = reader.readLine(); + if (line == null) { + break; + } + output = output + line; + } + p.getInputStream().close(); + + s_logger.info("Command output: "+ output); + + if (output.contains("FullyQualifiedErrorId") || output.contains("Error") || output.contains("Exception")) { + return new PrimaryStorageDownloadAnswer("Failed to copy template to SCVMM library share from secondary storage."); + } + + return new PrimaryStorageDownloadAnswer(templateUuidName, 0); + + } catch (Exception e) + { + s_logger.info("Exception caught: "+e.getMessage()); + return new PrimaryStorageDownloadAnswer("Failed to copy template to SCVMM library share from secondary storage."); + } + } + + protected Answer execute(CreateCommand cmd) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Executing resource CreateCommand: " + _gson.toJson(cmd)); + } + + try { + long volId = cmd.getVolumeId(); + String templateUrl = cmd.getTemplateUrl();; + StorageFilerTO pool = cmd.getPool(); + DiskProfile diskchar = cmd.getDiskCharacteristics(); + + if (diskchar.getType() == Volume.Type.ROOT) { + if (cmd.getTemplateUrl() == null) { + //create root volume + VolumeTO vol = new VolumeTO(cmd.getVolumeId(), + diskchar.getType(), + pool.getType(), pool.getUuid(), cmd.getDiskCharacteristics().getName(), + pool.getPath(), cmd.getDiskCharacteristics().getName(), cmd.getDiskCharacteristics().getSize(), + null); + return new CreateAnswer(cmd, vol); + } else { + VolumeTO vol = new VolumeTO(cmd.getVolumeId(), + diskchar.getType(), + pool.getType(), pool.getUuid(), cmd.getDiskCharacteristics().getName(), + pool.getPath(), cmd.getDiskCharacteristics().getName(), cmd.getDiskCharacteristics().getSize(), null); + return new CreateAnswer(cmd, vol); + } + + } else { + //create data volume + String volumeUuid = "cloud.worker." + UUID.randomUUID().toString(); + VolumeTO vol = new VolumeTO(cmd.getVolumeId(), + diskchar.getType(), + pool.getType(), pool.getUuid(), cmd.getDiskCharacteristics().getName(), + pool.getPath(), volumeUuid, cmd.getDiskCharacteristics().getSize(), null); + return new CreateAnswer(cmd, vol); + } + } catch (Exception ex) { + return null; + } + } + + protected StartAnswer execute(StartCommand cmd) { + + if (s_logger.isInfoEnabled()) { + s_logger.info("Executing resource StartCommand: " + _gson.toJson(cmd)); + } + + VirtualMachineTO vmSpec = cmd.getVirtualMachine(); + String vmName = vmSpec.getName(); + State state = State.Stopped; + String scriptFileName = vmName+ ".ps1"; + String newLine = System.getProperty("line.separator"); + String bootArgsDiskName = vmName+"-bootparams.vhd"; + String bootArgsDiskPath = "C:\\ProgramData\\Virtual Machine Manager Library Files\\VHDs\\"+bootArgsDiskName; + + try { + // mark VM as starting state so that sync() can know not to report stopped too early + synchronized (_vms) { + _vms.put(vmName, State.Starting); + { + // create and attach boot parameter disk + String diskpartScriptName = vmName+"-Diskpart.txt"; + + StringBuilder cmdDiskpart = new StringBuilder("create vdisk file=\"");cmdDiskpart.append(bootArgsDiskPath.toCharArray());cmdDiskpart.append("\" maximum=10 type=expandable" + newLine); + cmdDiskpart.append("select vdisk file=\"");cmdDiskpart.append(bootArgsDiskPath.toCharArray());cmdDiskpart.append("\"" + newLine); + cmdDiskpart.append("attach vdisk" + newLine); + cmdDiskpart.append("create partition primary" + newLine); + cmdDiskpart.append("format fs=ntfs label=\"test vhd\" quick" + newLine); + cmdDiskpart.append("assign letter="+vmName.toCharArray()[0]+ newLine); + cmdDiskpart.append("attach vdisk" + newLine); + + File f=new File(diskpartScriptName); + FileOutputStream fop=new FileOutputStream(f); + fop.write(cmdDiskpart.toString().getBytes()); + fop.flush(); + fop.close(); + + s_logger.info("Running diskpart attach command"); + Process p = Runtime.getRuntime().exec("cmd.exe /c diskpart.exe /s "+diskpartScriptName); + p.getOutputStream().close(); + + InputStreamReader temperrReader = new InputStreamReader(new BufferedInputStream(p.getErrorStream())); + BufferedReader errreader = new BufferedReader(temperrReader); + + if (errreader.ready()) { + String errorOutput = new String(""); + while (true){ + String errline = errreader.readLine(); + if (errline == null) { + break; + } + errorOutput = errorOutput + errline; + } + s_logger.info("errors found while running diskpart command: " + errorOutput); + } + + p.getErrorStream().close(); + InputStreamReader tempReader = new InputStreamReader(new BufferedInputStream(p.getInputStream())); + BufferedReader reader = new BufferedReader(tempReader); + + String output = new String(""); + + while (true){ + String line = reader.readLine(); + if (line == null) { + break; + } + output = output + line; + } + p.getInputStream().close(); + s_logger.info("diskpart detahc command output: "+ output); + } + // wait for a while so that disk formatting is done + Thread.sleep(60000); + + //create boot args on the disk + String bootArgs = vmSpec.getBootArgs(); + String Drive = vmName.substring(0,1); + File fBootargs =new File(Drive+":\\cmdline"); + FileOutputStream fopBoot=new FileOutputStream(fBootargs); + fopBoot.write(bootArgs.toString().getBytes()); + fopBoot.flush(); + fopBoot.close(); + + //detach the boot parameter disk + { + String diskpartDetachScriptName = vmName+"-Detach-Diskpart.txt"; + + StringBuilder cmdDetachDiskpart = new StringBuilder("select vdisk file=\""); + cmdDetachDiskpart.append(bootArgsDiskPath.toCharArray());cmdDetachDiskpart.append("\"" + newLine); + cmdDetachDiskpart.append("detach vdisk" + newLine); + + File fd=new File(diskpartDetachScriptName); + FileOutputStream fdop=new FileOutputStream(fd); + fdop.write(cmdDetachDiskpart.toString().getBytes()); + fdop.flush(); + fdop.close(); + + s_logger.info("Running diskpart detach command"); + Process pd = Runtime.getRuntime().exec("cmd.exe /c diskpart.exe /s "+diskpartDetachScriptName); + pd.getOutputStream().close(); + InputStreamReader temperrReader1 = new InputStreamReader(new BufferedInputStream(pd.getErrorStream())); + BufferedReader errreader1 = new BufferedReader(temperrReader1); + + if (errreader1.ready()) { + String errorOutput = new String(""); + while (true){ + String errline = errreader1.readLine(); + if (errline == null) { + break; + } + errorOutput = errorOutput + errline; + } + s_logger.info("errors found while running diskpart detach command: " + errorOutput); + } + + pd.getErrorStream().close(); + InputStreamReader tempReader1 = new InputStreamReader(new BufferedInputStream(pd.getInputStream())); + BufferedReader reader1 = new BufferedReader(tempReader1); + + String output1 = new String(""); + + while (true){ + String line = reader1.readLine(); + if (line == null) { + break; + } + output1 = output1 + line; + } + + pd.getInputStream().close(); + s_logger.info("diskpart detach command output: "+ output1); + } + } + + UUID id = UUID.randomUUID(); + String hwProfileId = id.toString(); + + StringBuilder cmdStr = new StringBuilder("Add-PSSnapin Microsoft.SystemCenter.VirtualMachineManager;" + newLine); + cmdStr.append("Get-VMMServer -ComputerName localhost;" + newLine); + cmdStr.append("$JobGroupId = [Guid]::NewGuid().ToString();" + newLine); + cmdStr.append("$hwProfileId = [Guid]::NewGuid().ToString(); " + newLine); + cmdStr.append("$CPUType = Get-CPUType -VMMServer localhost | where {$_.Name -eq " + "'1.20 GHz Athlon MP'}; " + newLine); + cmdStr.append("$ISO = Get-ISO -VMMServer localhost | where { $_.Name -match \"systemvm\" }; " + newLine); + cmdStr.append("$VMHost = Get-VMHost -VMMServer localhost | where {$_.Name -eq \"HYPERVHOST.hypervdc.intranet.lab.vmops.com\"}; " + newLine); + cmdStr.append("$VNetwork = Get-VirtualNetwork -VMHost $VMHost -Name \"public\"; " + newLine); + cmdStr.append("New-VirtualNetworkAdapter -VMMServer localhost -JobGroup $JobGroupID -PhysicalAddressType Dynamic -VirtualNetwork $vnetwork; " + newLine); + cmdStr.append("New-VirtualNetworkAdapter -VMMServer localhost -JobGroup $JobGroupID -PhysicalAddressType Dynamic -VirtualNetwork $vnetwork; " + newLine); + cmdStr.append("New-VirtualNetworkAdapter -VMMServer localhost -JobGroup $JobGroupID -PhysicalAddressType Dynamic -VirtualNetwork $vnetwork; " + newLine); + cmdStr.append("New-VirtualDVDDrive -VMMServer localhost -JobGroup $JobGroupID -Bus 1 -LUN 0 -ISO $ISO ; " + newLine); + cmdStr.append("New-HardwareProfile -VMMServer localhost -JobGroup $JobGroupID -Owner \"HYPERVDC\\Administrator\" -CPUType $CPUType -Name $hwProfileId"); + cmdStr.append(" -Description \"Profile used to create a VM/Template\"" + + " -CPUCount 1 -MemoryMB 512 -RelativeWeight 100 -HighlyAvailable $true -NumLock $false -BootOrder \"CD\", " + + "\"IdeHardDrive\", \"PxeBoot\", \"Floppy\" -LimitCPUFunctionality $false -LimitCPUForMigration $false; " + newLine); + + cmdStr.append("$JobGroupId = [Guid]::NewGuid().ToString(); " + newLine); + + //refresh library share + cmdStr.append("$share = Get-LibraryShare;"+newLine); + cmdStr.append("Refresh-LibraryShare -LibraryShare $share;"+newLine); + + // create root disk + cmdStr.append("$VirtualHardDisk1 = Get-VirtualHardDisk -VMMServer localhost | where {$_.Location -eq \"\\\\scvmm.hypervdc.intranet.lab.vmops.com\\MSSCVMMLibrary\\VHDs\\systemvm.vhd\"} | where {$_.HostName -eq \"scvmm.hypervdc.intranet.lab.vmops.com\"}" + newLine); + cmdStr.append("New-VirtualDiskDrive -VMMServer localhost -JobGroup $JobGroupID -IDE -Bus 0 -LUN 0 -VirtualHardDisk $VirtualHardDisk1 -Filename \""); + cmdStr.append(vmName.toCharArray()); + cmdStr.append("-systemvm.vhd\"; " + newLine); + + // create boot param data disk + cmdStr.append("$VirtualHardDisk2 = Get-VirtualHardDisk -VMMServer localhost " + + " | where {$_.Location -eq \"\\\\scvmm.hypervdc.intranet.lab.vmops.com\\MSSCVMMLibrary\\VHDs\\"); + cmdStr.append(bootArgsDiskName.toCharArray()); + cmdStr.append("\" } | where {$_.HostName -eq \"scvmm.hypervdc.intranet.lab.vmops.com\"}" + newLine); + cmdStr.append("New-VirtualDiskDrive -VMMServer localhost -JobGroup $JobGroupID -IDE -Bus 0 -LUN 1 -VirtualHardDisk $VirtualHardDisk2 -Filename \""); + cmdStr.append(bootArgsDiskName.toCharArray()); + cmdStr.append("\";"+newLine); + + cmdStr.append("$HardwareProfile = Get-HardwareProfile -VMMServer localhost | where {$_.Name -eq $hwProfileId};" + newLine); + cmdStr.append("$OperatingSystem = Get-OperatingSystem -VMMServer localhost | where {$_.Name -eq 'Other Linux (32 bit)'};" + newLine); + + cmdStr.append("New-VM -VMMServer localhost -Name \""); + cmdStr.append(vmName.toCharArray()); + + cmdStr.append("\" -Description \"\" -Owner \"HYPERVDC\\Administrator\" -VMHost $VMHost -Path \"C:\\ClusterStorage\\Volume1\" -HardwareProfile $HardwareProfile " + + " -JobGroup $JobGroupID" + + " -OperatingSystem $OperatingSystem -RunAsSystem -StartVM -StartAction NeverAutoTurnOnVM -StopAction SaveVM;" + newLine); + + File f=new File(scriptFileName); + FileOutputStream fop=new FileOutputStream(f); + fop.write(cmdStr.toString().getBytes()); + fop.flush(); + fop.close(); + s_logger.info("Running command: " + cmdStr); + Process p = Runtime.getRuntime().exec("cmd.exe /c Powershell -Command \" & '.\\" + scriptFileName +"'\""); + p.getOutputStream().close(); + + InputStreamReader temperrReader = new InputStreamReader(new BufferedInputStream(p.getErrorStream())); + BufferedReader errreader = new BufferedReader(temperrReader); + + if (errreader.ready()) { + String errorOutput = new String(""); + while (true){ + String errline = errreader.readLine(); + if (errline == null) { + break; + } + errorOutput = errorOutput + errline; + } + s_logger.info("errors found while running cmdlet to create VM: " + errorOutput); + } + + p.getErrorStream().close(); + InputStreamReader tempReader = new InputStreamReader(new BufferedInputStream(p.getInputStream())); + BufferedReader reader = new BufferedReader(tempReader); + + String output = new String(""); + + while (true){ + String line = reader.readLine(); + if (line == null) { + break; + } + output = output + line; + } + p.getInputStream().close(); + + s_logger.info("vm create cmmdlet output: "+ output); + + if (output.contains("FullyQualifiedErrorId") || output.contains("Error") || output.contains("Exception")) { + s_logger.info("No errors found in running cmdlet "+ cmdStr.toString()); + return new StartAnswer(cmd, "Failed to start VM"); + } + + state = State.Running; + return new StartAnswer(cmd); + } catch (Exception e){ + return new StartAnswer(cmd, "Failed to start VM"); + } finally { + //delete the PS script file + //File f=new File(".\\"+scriptFileName); + //f.delete(); + synchronized (_vms) { + if (state != State.Stopped) { + _vms.put(vmName, state); + } else { + _vms.remove(vmName); + } + } + } + } + + protected Answer execute(DhcpEntryCommand cmd) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Executing resource DhcpEntryCommand: " + _gson.toJson(cmd)); + } + + String args = " " + cmd.getVmMac(); + args += " " + cmd.getVmIpAddress(); + args += " " + cmd.getVmName(); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Run command on domR " + cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP) + ", /root/edithosts.sh " + args); + } + + try { + Pair result = SshHelper.sshExecute(cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP), DEFAULT_DOMR_SSHPORT, "root", + new File("id_rsa.cloud"), null, "/root/edithosts.sh " + args); + + if (!result.first()) { + s_logger.error("dhcp_entry command on domR " + cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP) + + " failed, message: " + result.second()); + + return new Answer(cmd, false, "DhcpEntry failed due to " + result.second()); + } + + if (s_logger.isInfoEnabled()) { + s_logger.info("dhcp_entry command on domain router " + cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP) + " completed"); + } + + } catch (Throwable e) { + s_logger.error("Unexpected exception ", e); + return new Answer(cmd, false, "DhcpEntry failed due to exception"); + } + + return new Answer(cmd); + } + + protected CheckSshAnswer execute(CheckSshCommand cmd) { + String vmName = cmd.getName(); + String privateIp = cmd.getIp(); + int cmdPort = cmd.getPort(); + + if (s_logger.isInfoEnabled()) { + s_logger.info("Ping VM:" + cmd.getName() + " IP:" + privateIp + " port:" + cmdPort); + } + return new CheckSshAnswer(cmd); + } + + @Override + public void setAgentControl(IAgentControl agentControl) { + this.agentControl = agentControl; + } + + @Override + public Type getType() { + // TODO Auto-generated method stub + return null; + } + + @Override + public StartupCommand[] initialize() { + + s_logger.info("recieved initialize request for cluster:" + _clusterId); + List vmHostList = getHostsInCluster(_clusterId); + + if (vmHostList.size() == 0) { + s_logger.info("cluster is not recognized or zero instances in the cluster"); + } + + StartupCommand[] answerCmds = new StartupCommand[vmHostList.size()]; + + int index =0; + for (String hostName: vmHostList) { + s_logger.info("Node :" + hostName); + StartupRoutingCommand cmd = new StartupRoutingCommand(); + answerCmds[index] = cmd; + fillHostInfo(cmd,hostName); + index++; + } + + s_logger.info("response sent to initialize request for cluster:" + _clusterId); + return answerCmds; + } + + protected void fillHostInfo(StartupRoutingCommand cmd, String hostName) { + + Map details = cmd.getHostDetails(); + if (details == null) { + details = new HashMap(); + } + + try { + fillHostHardwareInfo(cmd); + fillHostNetworkInfo(cmd); + fillHostDetailsInfo(details); + } catch (Exception e) { + s_logger.error("Exception while retrieving host info ", e); + throw new CloudRuntimeException("Exception while retrieving host info"); + } + + cmd.setName(hostName); + cmd.setHostDetails(details); + cmd.setGuid(_guid); + cmd.setDataCenter(_dcId); + cmd.setPod(_podId); + cmd.setCluster(_clusterId); + cmd.setHypervisorType(HypervisorType.Hyperv); + } + + private void fillHostDetailsInfo(Map details) throws Exception { + + } + + private Answer execute(GetHostStatsCommand cmd) { + + if (s_logger.isInfoEnabled()) { + s_logger.info("Executing resource GetHostStatsCommand: " + _gson.toJson(cmd)); + } + + try { + // FIXME: get the actual host stats by running powershell cmdlet. This is just for prototype. + HostStatsEntry hostStats = new HostStatsEntry(cmd.getHostId(), 0, 10000, 10000, + "host", 2*1024*1024, 1*1024*1024, 1*1024*1024, 0); + s_logger.info("returning stats :" + 2*1024*1024 + " " + 1*1024*1024 + " " + 1*1024*1024); + return new GetHostStatsAnswer(cmd, hostStats); + } catch (Exception e) { + HostStatsEntry hostStats = new HostStatsEntry(cmd.getHostId(), 0, 0, 0, + "host", 0, 0, 0, 0); + String msg = "Unable to execute GetHostStatsCommand due to exception " + e.getMessage(); + s_logger.error(msg, e); + return new GetHostStatsAnswer(cmd, hostStats); + } + } + + protected Answer execute(ModifyStoragePoolCommand cmd) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Executing resource ModifyStoragePoolCommand: " + _gson.toJson(cmd)); + } + + try { + StorageFilerTO pool = cmd.getPool(); + s_logger.info("Primary storage pool details: " + pool.getHost() + " " + pool.getPath()); + Map tInfo = new HashMap(); + // FIXME: get the actual storage capacity and storage stats of CSV volume + // by running powershell cmdlet. This hardcoding just for prototype. + ModifyStoragePoolAnswer answer = new ModifyStoragePoolAnswer(cmd, + 1024*1024*1024*1024L, 512*1024*1024*1024L, tInfo); + + return answer; + } catch (Throwable e) { + return new Answer(cmd, false, "Unable to execute ModifyStoragePoolCommand due to exception " + e.getMessage()); + } + } + + protected Answer execute(GetStorageStatsCommand cmd) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Executing GetStorageStatsCommand command: " + _gson.toJson(cmd)); + } + // FIXME: get the actual storage capacity and storage stats of CSV volume + return new GetStorageStatsAnswer(cmd, 1024*1024*1024*1024L, 512*1024*1024*1024L); + } + + private void fillHostHardwareInfo(StartupRoutingCommand cmd) throws RemoteException { + try { + // FIXME: get the actual host capacity by running cmdlet.This hardcoding just for prototype + cmd.setCaps("hvm"); + cmd.setDom0MinMemory(0); + cmd.setSpeed(100000); + cmd.setCpus(6); + long ram = new Long("211642163904"); + cmd.setMemory(ram); + } catch (Throwable e) { + s_logger.error("Unable to query host network info due to exception ", e); + throw new CloudRuntimeException("Unable to query host network info due to exception"); + } + } + + private void fillHostNetworkInfo(StartupRoutingCommand cmd) throws RemoteException { + try { + // FIXME: get the actual host and storage IP by running cmdlet.This hardcoding just for prototype + cmd.setPrivateIpAddress("192.168.154.236"); + cmd.setPrivateNetmask("255.255.255.0"); + cmd.setPrivateMacAddress("00:16:3e:77:e2:a0"); + + cmd.setStorageIpAddress("192.168.154.36"); + cmd.setStorageNetmask("255.255.255.0"); + cmd.setStorageMacAddress("00:16:3e:77:e2:a0"); + } catch (Throwable e) { + s_logger.error("Unable to query host network info due to exception ", e); + throw new CloudRuntimeException("Unable to query host network info due to exception"); + } + } + + private List getHostsInCluster(String clusterName) + { + List hypervHosts = new ArrayList(); + + try { + //StringBuilder cmd = new StringBuilder("cmd /c powershell.exe -OutputFormat XML "); + StringBuilder cmd = new StringBuilder("cmd /c powershell.exe "); + cmd.append("-Command Add-PSSnapin Microsoft.SystemCenter.VirtualMachineManager; "); + cmd.append("Get-VMMServer -ComputerName localhost; "); + cmd.append("Get-VMHostCluster "); + cmd.append(clusterName.toCharArray()); + + Process p = Runtime.getRuntime().exec(cmd.toString()); + p.getOutputStream().close(); + + InputStreamReader temperrReader = new InputStreamReader(new BufferedInputStream(p.getErrorStream())); + BufferedReader errreader = new BufferedReader(temperrReader); + if (errreader.ready()) { + String errorOutput = new String(""); + s_logger.info("errors found while running cmdlet Get-VMHostCluster"); + while (true){ + String errline = errreader.readLine(); + if (errline == null) { + break; + } + errorOutput = errorOutput + errline; + } + s_logger.info(errorOutput); + } else { + s_logger.info("No errors found in running cmdlet:" + cmd); + } + + p.getErrorStream().close(); + /* + InputStream in = (InputStream) p.getInputStream(); + XMLInputFactory factory = XMLInputFactory.newInstance(); + XMLStreamReader parser = factory.createXMLStreamReader(in); + + while(parser.hasNext()) { + + int eventType = parser.next(); + switch (eventType) { + + case START_ELEMENT: + // Do something + break; + case END_ELEMENT: + // Do something + break; + // And so on ... + } + } + parser.close(); + */ + + InputStreamReader tempReader = new InputStreamReader(new BufferedInputStream(p.getInputStream())); + BufferedReader reader = new BufferedReader(tempReader); + + String output = new String(""); + + while (true){ + String line = reader.readLine(); + if (line == null) { + break; + } + output = output + line; + } + + String nodesListStr = output.substring(output.indexOf("Nodes")); + nodesListStr = nodesListStr.substring(nodesListStr.indexOf('{', 0)+1, nodesListStr.indexOf('}', 0)); + String[] nodesList = nodesListStr.split(","); + + for (String node : nodesList) { + hypervHosts.add(node); + } + + p.getInputStream().close(); + } catch (Exception e) + { + s_logger.info("Exception caught: "+e.getMessage()); + } + return hypervHosts; + } + + protected HashMap sync() { + HashMap changes = new HashMap(); + + try { + synchronized (_vms) { + } + + } catch (Throwable e) { + s_logger.error("Unable to perform sync information collection process at this point due to exception ", e); + return null; + } + return changes; + } + + @Override + public PingCommand getCurrentStatus(long id) { + HashMap newStates = sync(); + if (newStates == null) { + newStates = new HashMap(); + } + PingRoutingCommand cmd = new PingRoutingCommand(com.cloud.host.Host.Type.Routing, id, newStates); + return cmd; + } + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + + _dcId = params.get("zone").toString(); + _podId= params.get("pod").toString(); + _clusterId = params.get("cluster").toString(); + _guid = params.get("guid").toString(); + + boolean success = super.configure(name, params); + if (! success) { + return false; + } + return true; + } + + @Override + protected String getDefaultScriptsDir() { + // TODO Auto-generated method stub + return null; + } +} diff --git a/core/src/com/cloud/network/resource/F5BigIpResource.java b/core/src/com/cloud/network/resource/F5BigIpResource.java new file mode 100644 index 00000000000..2de94a4bf58 --- /dev/null +++ b/core/src/com/cloud/network/resource/F5BigIpResource.java @@ -0,0 +1,989 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.network.resource; + +import iControl.CommonEnabledState; +import iControl.CommonIPPortDefinition; +import iControl.CommonStatistic; +import iControl.CommonStatisticType; +import iControl.CommonVirtualServerDefinition; +import iControl.Interfaces; +import iControl.LocalLBLBMethod; +import iControl.LocalLBNodeAddressBindingStub; +import iControl.LocalLBPoolBindingStub; +import iControl.LocalLBProfileContextType; +import iControl.LocalLBVirtualServerBindingStub; +import iControl.LocalLBVirtualServerVirtualServerPersistence; +import iControl.LocalLBVirtualServerVirtualServerProfile; +import iControl.LocalLBVirtualServerVirtualServerResource; +import iControl.LocalLBVirtualServerVirtualServerStatisticEntry; +import iControl.LocalLBVirtualServerVirtualServerStatistics; +import iControl.LocalLBVirtualServerVirtualServerType; +import iControl.NetworkingMemberTagType; +import iControl.NetworkingMemberType; +import iControl.NetworkingRouteDomainBindingStub; +import iControl.NetworkingSelfIPBindingStub; +import iControl.NetworkingVLANBindingStub; +import iControl.NetworkingVLANMemberEntry; +import iControl.SystemConfigSyncBindingStub; +import iControl.SystemConfigSyncSaveMode; + +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.ExternalNetworkResourceUsageAnswer; +import com.cloud.agent.api.ExternalNetworkResourceUsageCommand; +import com.cloud.agent.api.MaintainAnswer; +import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupExternalLoadBalancerCommand; +import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.IpAssocAnswer; +import com.cloud.agent.api.routing.LoadBalancerConfigCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.LoadBalancerTO; +import com.cloud.agent.api.to.LoadBalancerTO.DestinationTO; +import com.cloud.host.Host; +import com.cloud.resource.ServerResource; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.exception.ExecutionException; +import com.cloud.utils.net.NetUtils; + +public class F5BigIpResource implements ServerResource { + + private enum LbAlgorithm { + RoundRobin(null, LocalLBLBMethod.LB_METHOD_ROUND_ROBIN), + LeastConn(null, LocalLBLBMethod.LB_METHOD_LEAST_CONNECTION_MEMBER); + + String persistenceProfileName; + LocalLBLBMethod method; + + LbAlgorithm(String persistenceProfileName, LocalLBLBMethod method) { + this.persistenceProfileName = persistenceProfileName; + this.method = method; + } + + public String getPersistenceProfileName() { + return persistenceProfileName; + } + + public LocalLBLBMethod getMethod() { + return method; + } + } + + private enum LbProtocol { + tcp, + udp; + } + + private String _name; + private String _zoneId; + private String _ip; + private String _username; + private String _password; + private String _publicInterface; + private String _privateInterface; + private Integer _numRetries; + private String _guid; + private boolean _inline; + + private Interfaces _interfaces; + private LocalLBVirtualServerBindingStub _virtualServerApi; + private LocalLBPoolBindingStub _loadbalancerApi; + private LocalLBNodeAddressBindingStub _nodeApi; + private NetworkingVLANBindingStub _vlanApi; + private NetworkingSelfIPBindingStub _selfIpApi; + private NetworkingRouteDomainBindingStub _routeDomainApi; + private SystemConfigSyncBindingStub _configSyncApi; + private String _objectNamePathSep = "-"; + private String _routeDomainIdentifier = "%"; + + private static final Logger s_logger = Logger.getLogger(F5BigIpResource.class); + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + try { + XTrustProvider.install(); + + _name = (String) params.get("name"); + if (_name == null) { + throw new ConfigurationException("Unable to find name"); + } + + _zoneId = (String) params.get("zoneId"); + if (_zoneId == null) { + throw new ConfigurationException("Unable to find zone"); + } + + _ip = (String) params.get("ip"); + if (_ip == null) { + throw new ConfigurationException("Unable to find IP"); + } + + _username = (String) params.get("username"); + if (_username == null) { + throw new ConfigurationException("Unable to find username"); + } + + _password = (String) params.get("password"); + if (_password == null) { + throw new ConfigurationException("Unable to find password"); + } + + _publicInterface = (String) params.get("publicInterface"); + if (_publicInterface == null) { + throw new ConfigurationException("Unable to find public interface"); + } + + _privateInterface = (String) params.get("privateInterface"); + if (_privateInterface == null) { + throw new ConfigurationException("Unable to find private interface"); + } + + _numRetries = NumbersUtil.parseInt((String) params.get("numRetries"), 1); + + _guid = (String)params.get("guid"); + if (_guid == null) { + throw new ConfigurationException("Unable to find the guid"); + } + + _inline = Boolean.parseBoolean((String) params.get("inline")); + + if (!login()) { + throw new ExecutionException("Failed to login to the F5 BigIp."); + } + + return true; + } catch (Exception e) { + throw new ConfigurationException(e.getMessage()); + } + + } + + @Override + public StartupCommand[] initialize() { + StartupExternalLoadBalancerCommand cmd = new StartupExternalLoadBalancerCommand(); + cmd.setName(_name); + cmd.setDataCenter(_zoneId); + cmd.setPod(""); + cmd.setPrivateIpAddress(_ip); + cmd.setStorageIpAddress(""); + cmd.setVersion(""); + cmd.setGuid(_guid); + return new StartupCommand[]{cmd}; + } + + @Override + public Host.Type getType() { + return Host.Type.ExternalLoadBalancer; + } + + @Override + public String getName() { + return _name; + } + + @Override + public PingCommand getCurrentStatus(final long id) { + return new PingCommand(Host.Type.ExternalLoadBalancer, id); + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public void disconnected() { + return; + } + + @Override + public IAgentControl getAgentControl() { + return null; + } + + @Override + public void setAgentControl(IAgentControl agentControl) { + return; + } + + @Override + public Answer executeRequest(Command cmd) { + return executeRequest(cmd, _numRetries); + } + + private Answer executeRequest(Command cmd, int numRetries) { + if (cmd instanceof ReadyCommand) { + return execute((ReadyCommand) cmd); + } else if (cmd instanceof MaintainCommand) { + return execute((MaintainCommand) cmd); + } else if (cmd instanceof IpAssocCommand) { + return execute((IpAssocCommand) cmd, numRetries); + } else if (cmd instanceof LoadBalancerConfigCommand) { + return execute((LoadBalancerConfigCommand) cmd, numRetries); + } else if (cmd instanceof ExternalNetworkResourceUsageCommand) { + return execute((ExternalNetworkResourceUsageCommand) cmd); + } else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } + + private Answer retry(Command cmd, int numRetries) { + int numRetriesRemaining = numRetries - 1; + s_logger.error("Retrying " + cmd.getClass().getSimpleName() + ". Number of retries remaining: " + numRetriesRemaining); + return executeRequest(cmd, numRetriesRemaining); + } + + private boolean shouldRetry(int numRetries) { + return (numRetries > 0 && login()); + } + + private Answer execute(ReadyCommand cmd) { + return new ReadyAnswer(cmd); + } + + private Answer execute(MaintainCommand cmd) { + return new MaintainAnswer(cmd); + } + + private synchronized Answer execute(IpAssocCommand cmd, int numRetries) { + String[] results = new String[cmd.getIpAddresses().length]; + int i = 0; + try { + IpAddressTO[] ips = cmd.getIpAddresses(); + for (IpAddressTO ip : ips) { + long guestVlanTag = Long.valueOf(ip.getVlanId()); + String vlanSelfIp = _inline ? tagAddressWithRouteDomain(ip.getVlanGateway(), guestVlanTag) : ip.getVlanGateway(); + String vlanNetmask = ip.getVlanNetmask(); + + // Delete any existing guest VLAN with this tag, self IP, and netmask + deleteGuestVlan(guestVlanTag, vlanSelfIp, vlanNetmask); + + if (ip.isAdd()) { + // Add a new guest VLAN + addGuestVlan(guestVlanTag, vlanSelfIp, vlanNetmask); + } + + saveConfiguration(); + results[i++] = ip.getPublicIp() + " - success"; + } + + } catch (ExecutionException e) { + s_logger.error("Failed to execute IPAssocCommand due to " + e); + + if (shouldRetry(numRetries)) { + return retry(cmd, numRetries); + } else { + results[i++] = IpAssocAnswer.errorResult; + } + } + + return new IpAssocAnswer(cmd, results); + } + + private synchronized Answer execute(LoadBalancerConfigCommand cmd, int numRetries) { + try { + long guestVlanTag = Long.parseLong(cmd.getAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG)); + LoadBalancerTO[] loadBalancers = cmd.getLoadBalancers(); + for (LoadBalancerTO loadBalancer : loadBalancers) { + LbProtocol lbProtocol; + try { + if (loadBalancer.getProtocol() == null) { + lbProtocol = LbProtocol.tcp; + } else { + lbProtocol = LbProtocol.valueOf(loadBalancer.getProtocol()); + } + } catch (IllegalArgumentException e) { + throw new ExecutionException("Got invalid protocol: " + loadBalancer.getProtocol()); + } + + LbAlgorithm lbAlgorithm; + if (loadBalancer.getAlgorithm().equals("roundrobin")) { + lbAlgorithm = LbAlgorithm.RoundRobin; + } else if (loadBalancer.getAlgorithm().equals("leastconn")) { + lbAlgorithm = LbAlgorithm.LeastConn; + } else { + throw new ExecutionException("Got invalid algorithm: " + loadBalancer.getAlgorithm()); + } + + String srcIp = _inline ? tagAddressWithRouteDomain(loadBalancer.getSrcIp(), guestVlanTag) : loadBalancer.getSrcIp(); + int srcPort = loadBalancer.getSrcPort(); + String virtualServerName = genVirtualServerName(lbProtocol, srcIp, srcPort); + + boolean destinationsToAdd = false; + for (DestinationTO destination : loadBalancer.getDestinations()) { + if (!destination.isRevoked()) { + destinationsToAdd = true; + break; + } + } + + if (!loadBalancer.isRevoked() && destinationsToAdd) { + // Add the pool + addPool(virtualServerName, lbAlgorithm); + + // Add pool members + List activePoolMembers = new ArrayList(); + for (DestinationTO destination : loadBalancer.getDestinations()) { + if (!destination.isRevoked()) { + String destIp = _inline ? tagAddressWithRouteDomain(destination.getDestIp(), guestVlanTag) : destination.getDestIp(); + addPoolMember(virtualServerName, destIp, destination.getDestPort()); + activePoolMembers.add(destIp + "-" + destination.getDestPort()); + } + } + + // Delete any pool members that aren't in the current list of destinations + deleteInactivePoolMembers(virtualServerName, activePoolMembers); + + // Add the virtual server + addVirtualServer(virtualServerName, lbProtocol, srcIp, srcPort); + } else { + // Delete the virtual server with this protocol, source IP, and source port, along with its default pool and all pool members + deleteVirtualServerAndDefaultPool(virtualServerName); + } + } + + saveConfiguration(); + return new Answer(cmd); + } catch (ExecutionException e) { + s_logger.error("Failed to execute LoadBalancerConfigCommand due to " + e); + + if (shouldRetry(numRetries)) { + return retry(cmd, numRetries); + } else { + return new Answer(cmd, e); + } + + } + } + + private synchronized ExternalNetworkResourceUsageAnswer execute(ExternalNetworkResourceUsageCommand cmd) { + try { + return getIpBytesSentAndReceived(cmd); + } catch (ExecutionException e) { + return new ExternalNetworkResourceUsageAnswer(cmd, e); + } + } + + private void saveConfiguration() throws ExecutionException { + try { + _configSyncApi.save_configuration("", SystemConfigSyncSaveMode.SAVE_BASE_LEVEL_CONFIG); + _configSyncApi.save_configuration("", SystemConfigSyncSaveMode.SAVE_HIGH_LEVEL_CONFIG); + s_logger.debug("Successfully saved F5 BigIp configuration."); + } catch (RemoteException e) { + s_logger.error("Failed to save F5 BigIp configuration due to: " + e); + throw new ExecutionException(e.getMessage()); + } + } + + private void addGuestVlan(long vlanTag, String vlanSelfIp, String vlanNetmask) throws ExecutionException { + try { + String vlanName = genVlanName(vlanTag); + List allVlans = getVlans(); + if (!allVlans.contains(vlanName)) { + String[] vlanNames = genStringArray(vlanName); + long[] vlanTags = genLongArray(vlanTag); + CommonEnabledState[] commonEnabledState = {CommonEnabledState.STATE_DISABLED}; + + // Create the interface name + NetworkingVLANMemberEntry[][] vlanMemberEntries = {{new NetworkingVLANMemberEntry()}}; + vlanMemberEntries[0][0].setMember_type(NetworkingMemberType.MEMBER_INTERFACE); + vlanMemberEntries[0][0].setTag_state(NetworkingMemberTagType.MEMBER_TAGGED); + vlanMemberEntries[0][0].setMember_name(_privateInterface); + + s_logger.debug("Creating a guest VLAN with tag " + vlanTag); + _vlanApi.create(vlanNames, vlanTags, vlanMemberEntries, commonEnabledState, new long[]{10L}, new String[]{"00:00:00:00:00:00"}); + + if (!getVlans().contains(vlanName)) { + throw new ExecutionException("Failed to create vlan with tag " + vlanTag); + } + } + + if (_inline) { + List allRouteDomains = getRouteDomains(); + if (!allRouteDomains.contains(vlanTag)) { + long[] routeDomainIds = genLongArray(vlanTag); + String[][] vlanNames = new String[][]{genStringArray(genVlanName(vlanTag))}; + + s_logger.debug("Creating route domain " + vlanTag); + _routeDomainApi.create(routeDomainIds, vlanNames); + + if (!getRouteDomains().contains(vlanTag)) { + throw new ExecutionException("Failed to create route domain " + vlanTag); + } + } + } + + List allSelfIps = getSelfIps(); + if (!allSelfIps.contains(vlanSelfIp)) { + String[] selfIpsToCreate = genStringArray(vlanSelfIp); + String[] vlans = genStringArray(vlanName); + String[] netmasks = genStringArray(vlanNetmask); + long[] unitIds = genLongArray(0L); + CommonEnabledState[] enabledStates = new CommonEnabledState[]{CommonEnabledState.STATE_DISABLED}; + + s_logger.debug("Creating self IP " + vlanSelfIp); + _selfIpApi.create(selfIpsToCreate, vlans, netmasks, unitIds, enabledStates); + + if (!getSelfIps().contains(vlanSelfIp)) { + throw new ExecutionException("Failed to create self IP " + vlanSelfIp); + } + } + } catch (RemoteException e) { + s_logger.error(e); + throw new ExecutionException(e.getMessage()); + } + + } + + private void deleteGuestVlan(long vlanTag, String vlanSelfIp, String vlanNetmask) throws ExecutionException { + try { + // Delete all virtual servers and pools that use this guest VLAN + deleteVirtualServersInGuestVlan(vlanSelfIp, vlanNetmask); + + List allSelfIps = getSelfIps(); + if (allSelfIps.contains(vlanSelfIp)) { + s_logger.debug("Deleting self IP " + vlanSelfIp); + _selfIpApi.delete_self_ip(genStringArray(vlanSelfIp)); + + if (getSelfIps().contains(vlanSelfIp)) { + throw new ExecutionException("Failed to delete self IP " + vlanSelfIp); + } + } + + if (_inline) { + List allRouteDomains = getRouteDomains(); + if (allRouteDomains.contains(vlanTag)) { + s_logger.debug("Deleting route domain " + vlanTag); + _routeDomainApi.delete_route_domain(genLongArray(vlanTag)); + + if (getRouteDomains().contains(vlanTag)) { + throw new ExecutionException("Failed to delete route domain " + vlanTag); + } + } + } + + String vlanName = genVlanName(vlanTag); + List allVlans = getVlans(); + if (allVlans.contains(vlanName)) { + _vlanApi.delete_vlan(genStringArray(vlanName)); + + if (getVlans().contains(vlanName)) { + throw new ExecutionException("Failed to delete VLAN with tag: " + vlanTag); + } + } + } catch (RemoteException e) { + throw new ExecutionException(e.getMessage()); + } + } + + private void deleteVirtualServersInGuestVlan(String vlanSelfIp, String vlanNetmask) throws ExecutionException { + vlanSelfIp = stripRouteDomainFromAddress(vlanSelfIp); + List virtualServersToDelete = new ArrayList(); + + List allVirtualServers = getVirtualServers(); + for (String virtualServerName : allVirtualServers) { + // Check if the virtual server's default pool has members in this guest VLAN + List poolMembers = getMembers(virtualServerName); + for (String poolMemberName : poolMembers) { + String poolMemberIp = stripRouteDomainFromAddress(getIpAndPort(poolMemberName)[0]); + if (NetUtils.sameSubnet(vlanSelfIp, poolMemberIp, vlanNetmask)) { + virtualServersToDelete.add(virtualServerName); + break; + } + } + } + + for (String virtualServerName : virtualServersToDelete) { + s_logger.debug("Found a virtual server (" + virtualServerName + ") for guest network with self IP " + vlanSelfIp + " that is active when the guest network is being destroyed."); + deleteVirtualServerAndDefaultPool(virtualServerName); + } + } + + private String genVlanName(long vlanTag) { + return "vlan-" + String.valueOf(vlanTag); + } + + private List getRouteDomains() throws ExecutionException { + try { + List routeDomains = new ArrayList(); + long[] routeDomainsArray = _routeDomainApi.get_list(); + + for (long routeDomainName : routeDomainsArray) { + routeDomains.add(routeDomainName); + } + + return routeDomains; + } catch (RemoteException e) { + throw new ExecutionException(e.getMessage()); + } + } + + private List getSelfIps() throws ExecutionException { + try { + List selfIps = new ArrayList(); + String[] selfIpsArray = _selfIpApi.get_list(); + + for (String selfIp : selfIpsArray) { + selfIps.add(selfIp); + } + + return selfIps; + } catch (RemoteException e) { + throw new ExecutionException(e.getMessage()); + } + } + + private List getVlans() throws ExecutionException { + try { + List vlans = new ArrayList(); + String[] vlansArray = _vlanApi.get_list(); + + for (String vlan : vlansArray) { + vlans.add(vlan); + } + + return vlans; + } catch (RemoteException e) { + throw new ExecutionException(e.getMessage()); + } + } + + // Login + + private boolean login() { + try { + _interfaces = new Interfaces(); + + if (!_interfaces.initialize(_ip, _username, _password)) { + throw new ExecutionException("Failed to log in to BigIp appliance"); + } + + _virtualServerApi = _interfaces.getLocalLBVirtualServer(); + _loadbalancerApi = _interfaces.getLocalLBPool(); + _nodeApi = _interfaces.getLocalLBNodeAddress(); + _vlanApi = _interfaces.getNetworkingVLAN(); + _selfIpApi = _interfaces.getNetworkingSelfIP(); + _routeDomainApi = _interfaces.getNetworkingRouteDomain(); + _configSyncApi = _interfaces.getSystemConfigSync(); + + return true; + } catch (Exception e) { + return false; + } + } + + // Virtual server methods + + private void addVirtualServer(String virtualServerName, LbProtocol protocol, String srcIp, int srcPort) throws ExecutionException { + try { + if (!virtualServerExists(virtualServerName)) { + s_logger.debug("Adding virtual server " + virtualServerName); + _virtualServerApi.create(genVirtualServerDefinition(virtualServerName, protocol, srcIp, srcPort), new String[]{"255.255.255.255"}, genVirtualServerResource(virtualServerName), genVirtualServerProfile(protocol)); + _virtualServerApi.set_snat_automap(genStringArray(virtualServerName)); + + if (!virtualServerExists(virtualServerName)) { + throw new ExecutionException("Failed to add virtual server " + virtualServerName); + } + } + } catch (RemoteException e) { + throw new ExecutionException(e.getMessage()); + } + } + + private void deleteVirtualServerAndDefaultPool(String virtualServerName) throws ExecutionException { + try { + if (virtualServerExists(virtualServerName)) { + // Delete the default pool's members + List poolMembers = getMembers(virtualServerName); + for (String poolMember : poolMembers) { + String[] destIpAndPort = getIpAndPort(poolMember); + deletePoolMember(virtualServerName, destIpAndPort[0], Integer.valueOf(destIpAndPort[1])); + } + + // Delete the virtual server + s_logger.debug("Deleting virtual server " + virtualServerName); + _virtualServerApi.delete_virtual_server(genStringArray(virtualServerName)); + + if (getVirtualServers().contains(virtualServerName)) { + throw new ExecutionException("Failed to delete virtual server " + virtualServerName); + } + + // Delete the default pool + deletePool(virtualServerName); + } + } catch (RemoteException e) { + throw new ExecutionException(e.getMessage()); + } + } + + private String genVirtualServerName(LbProtocol protocol, String srcIp, long srcPort) { + srcIp = stripRouteDomainFromAddress(srcIp); + return genObjectName("vs", protocol, srcIp, srcPort); + } + + private boolean virtualServerExists(String virtualServerName) throws ExecutionException { + return getVirtualServers().contains(virtualServerName); + } + + private List getVirtualServers() throws ExecutionException { + try { + List virtualServers = new ArrayList(); + String[] virtualServersArray = _virtualServerApi.get_list(); + + for (String virtualServer : virtualServersArray) { + virtualServers.add(virtualServer); + } + + return virtualServers; + } catch (RemoteException e) { + throw new ExecutionException(e.getMessage()); + } + } + + private iControl.CommonVirtualServerDefinition[] genVirtualServerDefinition(String name, LbProtocol protocol, String srcIp, long srcPort) { + CommonVirtualServerDefinition vsDefs[] = {new CommonVirtualServerDefinition()}; + vsDefs[0].setName(name); + vsDefs[0].setAddress(srcIp); + vsDefs[0].setPort(srcPort); + + if (protocol.equals(LbProtocol.tcp)) { + vsDefs[0].setProtocol(iControl.CommonProtocolType.PROTOCOL_TCP); + } else if (protocol.equals(LbProtocol.udp)) { + vsDefs[0].setProtocol(iControl.CommonProtocolType.PROTOCOL_UDP); + } + + return vsDefs; + } + + private iControl.LocalLBVirtualServerVirtualServerResource[] genVirtualServerResource(String poolName) { + LocalLBVirtualServerVirtualServerResource vsRes[] = {new LocalLBVirtualServerVirtualServerResource()}; + vsRes[0].setType(LocalLBVirtualServerVirtualServerType.RESOURCE_TYPE_POOL); + vsRes[0].setDefault_pool_name(poolName); + return vsRes; + } + + private LocalLBVirtualServerVirtualServerProfile[][] genVirtualServerProfile(LbProtocol protocol) { + LocalLBVirtualServerVirtualServerProfile vsProfs[][] = {{new LocalLBVirtualServerVirtualServerProfile()}}; + vsProfs[0][0].setProfile_context(LocalLBProfileContextType.PROFILE_CONTEXT_TYPE_ALL); + + if (protocol.equals(LbProtocol.tcp)) { + vsProfs[0][0].setProfile_name("http"); + } else if (protocol.equals(LbProtocol.udp)) { + vsProfs[0][0].setProfile_name("udp"); + } + + return vsProfs; + } + + private LocalLBVirtualServerVirtualServerPersistence[][] genPersistenceProfile(String persistenceProfileName) { + LocalLBVirtualServerVirtualServerPersistence[][] persistenceProfs = {{new LocalLBVirtualServerVirtualServerPersistence()}}; + persistenceProfs[0][0].setDefault_profile(true); + persistenceProfs[0][0].setProfile_name(persistenceProfileName); + return persistenceProfs; + } + + // Load balancing pool methods + + private void addPool(String virtualServerName, LbAlgorithm algorithm) throws ExecutionException { + try { + if (!poolExists(virtualServerName)) { + if (algorithm.getPersistenceProfileName() != null) { + algorithm = LbAlgorithm.RoundRobin; + } + + s_logger.debug("Adding pool for virtual server " + virtualServerName + " with algorithm " + algorithm); + _loadbalancerApi.create(genStringArray(virtualServerName), genLbMethod(algorithm), genEmptyMembersArray()); + + if (!poolExists(virtualServerName)) { + throw new ExecutionException("Failed to create new pool for virtual server " + virtualServerName); + } + } + } catch (RemoteException e) { + throw new ExecutionException(e.getMessage()); + } + } + + private void deletePool(String virtualServerName) throws ExecutionException { + try { + if (poolExists(virtualServerName) && getMembers(virtualServerName).size() == 0) { + s_logger.debug("Deleting pool for virtual server " + virtualServerName); + _loadbalancerApi.delete_pool(genStringArray(virtualServerName)); + + if (poolExists(virtualServerName)) { + throw new ExecutionException("Failed to delete pool for virtual server " + virtualServerName); + } + } + } catch (RemoteException e) { + throw new ExecutionException(e.getMessage()); + } + } + + private void addPoolMember(String virtualServerName, String destIp, int destPort) throws ExecutionException { + try { + String memberIdentifier = destIp + "-" + destPort; + + if (poolExists(virtualServerName) && !memberExists(virtualServerName, memberIdentifier)) { + s_logger.debug("Adding member " + memberIdentifier + " into pool for virtual server " + virtualServerName); + _loadbalancerApi.add_member(genStringArray(virtualServerName), genMembers(destIp, destPort)); + + if (!memberExists(virtualServerName, memberIdentifier)) { + throw new ExecutionException("Failed to add new member " + memberIdentifier + " into pool for virtual server " + virtualServerName); + } + } + } catch (RemoteException e) { + throw new ExecutionException(e.getMessage()); + } + } + + private void deleteInactivePoolMembers(String virtualServerName, List activePoolMembers) throws ExecutionException { + List allPoolMembers = getMembers(virtualServerName); + + for (String member : allPoolMembers) { + if (!activePoolMembers.contains(member)) { + String[] ipAndPort = member.split("-"); + deletePoolMember(virtualServerName, ipAndPort[0], Integer.valueOf(ipAndPort[1])); + } + } + } + + private void deletePoolMember(String virtualServerName, String destIp, int destPort) throws ExecutionException { + try { + String memberIdentifier = destIp + "-" + destPort; + List lbPools = getAllLbPools(); + + if (lbPools.contains(virtualServerName) && memberExists(virtualServerName, memberIdentifier)) { + s_logger.debug("Deleting member " + memberIdentifier + " from pool for virtual server " + virtualServerName); + _loadbalancerApi.remove_member(genStringArray(virtualServerName), genMembers(destIp, destPort)); + + if (memberExists(virtualServerName, memberIdentifier)) { + throw new ExecutionException("Failed to delete member " + memberIdentifier + " from pool for virtual server " + virtualServerName); + } + + if (nodeExists(destIp)) { + boolean nodeNeeded = false; + done: + for (String poolToCheck : lbPools) { + for (String memberInPool : getMembers(poolToCheck)) { + if (getIpAndPort(memberInPool)[0].equals(destIp)) { + nodeNeeded = true; + break done; + } + } + } + + if (!nodeNeeded) { + s_logger.debug("Deleting node " + destIp); + _nodeApi.delete_node_address(genStringArray(destIp)); + + if (nodeExists(destIp)) { + throw new ExecutionException("Failed to delete node " + destIp); + } + } + } + } + } catch (RemoteException e) { + throw new ExecutionException(e.getMessage()); + } + } + + private boolean poolExists(String poolName) throws ExecutionException { + return getAllLbPools().contains(poolName); + } + + private boolean memberExists(String poolName, String memberIdentifier) throws ExecutionException { + return getMembers(poolName).contains(memberIdentifier); + } + + private boolean nodeExists(String destIp) throws RemoteException { + return getNodes().contains(destIp); + } + + private String[] getIpAndPort(String memberIdentifier) { + return memberIdentifier.split("-"); + } + + public List getAllLbPools() throws ExecutionException { + try { + List lbPools = new ArrayList(); + String[] pools = _loadbalancerApi.get_list(); + + for (String pool : pools) { + lbPools.add(pool); + } + + return lbPools; + } catch (RemoteException e) { + throw new ExecutionException(e.getMessage()); + } + } + + private List getMembers(String virtualServerName) throws ExecutionException { + try { + List members = new ArrayList(); + String[] virtualServerNames = genStringArray(virtualServerName); + CommonIPPortDefinition[] membersArray = _loadbalancerApi.get_member(virtualServerNames)[0]; + + for (CommonIPPortDefinition member : membersArray) { + members.add(member.getAddress() + "-" + member.getPort()); + } + + return members; + } catch (RemoteException e) { + throw new ExecutionException(e.getMessage()); + } + } + + private List getNodes() throws RemoteException { + List nodes = new ArrayList(); + String[] nodesArray = _nodeApi.get_list(); + + for (String node : nodesArray) { + nodes.add(node); + } + + return nodes; + } + + private iControl.CommonIPPortDefinition[][] genMembers(String destIp, long destPort) { + iControl.CommonIPPortDefinition[] membersInnerArray = new iControl.CommonIPPortDefinition[1]; + membersInnerArray[0] = new iControl.CommonIPPortDefinition(destIp, destPort); + return new iControl.CommonIPPortDefinition[][]{membersInnerArray}; + } + + private iControl.CommonIPPortDefinition[][] genEmptyMembersArray() { + iControl.CommonIPPortDefinition[] membersInnerArray = new iControl.CommonIPPortDefinition[0]; + return new iControl.CommonIPPortDefinition[][]{membersInnerArray}; + } + + private LocalLBLBMethod[] genLbMethod(LbAlgorithm algorithm) { + if (algorithm.getMethod() != null) { + return new LocalLBLBMethod[]{algorithm.getMethod()}; + } else { + return new LocalLBLBMethod[]{LbAlgorithm.RoundRobin.getMethod()}; + } + } + + // Stats methods + + private ExternalNetworkResourceUsageAnswer getIpBytesSentAndReceived(ExternalNetworkResourceUsageCommand cmd) throws ExecutionException { + ExternalNetworkResourceUsageAnswer answer = new ExternalNetworkResourceUsageAnswer(cmd); + + try { + LocalLBVirtualServerVirtualServerStatistics stats = _virtualServerApi.get_all_statistics(); + for (LocalLBVirtualServerVirtualServerStatisticEntry entry : stats.getStatistics()) { + String virtualServerIp = entry.getVirtual_server().getAddress(); + + if (_inline) { + virtualServerIp = stripRouteDomainFromAddress(virtualServerIp); + } + + long[] bytesSentAndReceived = answer.ipBytes.get(virtualServerIp); + + if (bytesSentAndReceived == null) { + bytesSentAndReceived = new long[]{0, 0}; + } + + for (CommonStatistic stat : entry.getStatistics()) { + if (stat.getType().equals(CommonStatisticType.STATISTIC_CLIENT_SIDE_BYTES_OUT)) { + // Add to the outgoing bytes + bytesSentAndReceived[0] += stat.getValue().getLow(); + } else if (stat.getType().equals(CommonStatisticType.STATISTIC_CLIENT_SIDE_BYTES_IN)) { + // Add to the incoming bytes + bytesSentAndReceived[1] += stat.getValue().getLow(); + } + } + + if (bytesSentAndReceived[0] >= 0 && bytesSentAndReceived[1] >= 0) { + answer.ipBytes.put(virtualServerIp, bytesSentAndReceived); + } + } + } catch (Exception e) { + s_logger.error(e); + throw new ExecutionException(e.getMessage()); + } + + return answer; + } + + // Misc methods + + private String tagAddressWithRouteDomain(String address, long vlanTag) { + return address + _routeDomainIdentifier + vlanTag; + } + + private String stripRouteDomainFromAddress(String address) { + int i = address.indexOf(_routeDomainIdentifier); + + if (i > 0) { + address = address.substring(0, i); + } + + return address; + } + + private String genObjectName(Object... args) { + String objectName = ""; + + for (int i = 0; i < args.length; i++) { + objectName += args[i]; + if (i != args.length -1) { + objectName += _objectNamePathSep; + } + } + + return objectName; + } + + private long[] genLongArray(long l) { + return new long[]{l}; + } + + private static String[] genStringArray(String s) { + return new String[]{s}; + } + +} + + + + + + \ No newline at end of file diff --git a/core/src/com/cloud/network/resource/JuniperSrxResource.java b/core/src/com/cloud/network/resource/JuniperSrxResource.java new file mode 100644 index 00000000000..26511ca736c --- /dev/null +++ b/core/src/com/cloud/network/resource/JuniperSrxResource.java @@ -0,0 +1,3074 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.network.resource; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringReader; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.naming.ConfigurationException; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.apache.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.ExternalNetworkResourceUsageAnswer; +import com.cloud.agent.api.ExternalNetworkResourceUsageCommand; +import com.cloud.agent.api.MaintainAnswer; +import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupExternalFirewallCommand; +import com.cloud.agent.api.routing.IpAssocAnswer; +import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetStaticNatRulesCommand; +import com.cloud.agent.api.routing.VpnUsersCfgCommand; +import com.cloud.agent.api.routing.VpnUsersCfgCommand.UsernamePassword; +import com.cloud.agent.api.to.FirewallRuleTO; +import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.PortForwardingRuleTO; +import com.cloud.agent.api.to.StaticNatRuleTO; +import com.cloud.host.Host; +import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.resource.ServerResource; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.exception.ExecutionException; +import com.cloud.utils.net.NetUtils; +import com.cloud.utils.script.Script; + +public class JuniperSrxResource implements ServerResource { + + private String _name; + private String _zoneId; + private String _ip; + private String _username; + private String _password; + private String _guid; + private String _objectNameWordSep; + private PrintWriter _toSrx; + private BufferedReader _fromSrx; + private static Integer _numRetries; + private static Integer _timeoutInSeconds; + private static String _publicZone; + private static String _privateZone; + private static String _publicInterface; + private static String _usageInterface; + private static String _privateInterface; + private static String _ikeProposalName; + private static String _ipsecPolicyName; + private static String _primaryDnsAddress; + private static String _ikeGatewayHostname; + private static String _vpnObjectPrefix; + private static final Logger s_logger = Logger.getLogger(JuniperSrxResource.class); + + private enum SrxXml { + LOGIN("login.xml"), + PRIVATE_INTERFACE_ADD("private-interface-add.xml"), + PRIVATE_INTERFACE_WITH_FILTERS_ADD("private-interface-with-filters-add.xml"), + PRIVATE_INTERFACE_GETONE("private-interface-getone.xml"), + PROXY_ARP_ADD("proxy-arp-add.xml"), + PROXY_ARP_GETONE("proxy-arp-getone.xml"), + PROXY_ARP_GETALL("proxy-arp-getall.xml"), + ZONE_INTERFACE_ADD("zone-interface-add.xml"), + ZONE_INTERFACE_GETONE("zone-interface-getone.xml"), + SRC_NAT_POOL_ADD("src-nat-pool-add.xml"), + SRC_NAT_POOL_GETONE("src-nat-pool-getone.xml"), + SRC_NAT_RULE_ADD("src-nat-rule-add.xml"), + SRC_NAT_RULE_GETONE("src-nat-rule-getone.xml"), + SRC_NAT_RULE_GETALL("src-nat-rule-getall.xml"), + DEST_NAT_POOL_ADD("dest-nat-pool-add.xml"), + DEST_NAT_POOL_GETONE("dest-nat-pool-getone.xml"), + DEST_NAT_POOL_GETALL("dest-nat-pool-getall.xml"), + DEST_NAT_RULE_ADD("dest-nat-rule-add.xml"), + DEST_NAT_RULE_GETONE("dest-nat-rule-getone.xml"), + DEST_NAT_RULE_GETALL("dest-nat-rule-getall.xml"), + STATIC_NAT_RULE_ADD("static-nat-rule-add.xml"), + STATIC_NAT_RULE_GETONE("static-nat-rule-getone.xml"), + STATIC_NAT_RULE_GETALL("static-nat-rule-getall.xml"), + ADDRESS_BOOK_ENTRY_ADD("address-book-entry-add.xml"), + ADDRESS_BOOK_ENTRY_GETONE("address-book-entry-getone.xml"), + ADDRESS_BOOK_ENTRY_GETALL("address-book-entry-getall.xml"), + APPLICATION_ADD("application-add.xml"), + APPLICATION_GETONE("application-getone.xml"), + SECURITY_POLICY_ADD("security-policy-add.xml"), + SECURITY_POLICY_GETONE("security-policy-getone.xml"), + SECURITY_POLICY_GETALL("security-policy-getall.xml"), + SECURITY_POLICY_GROUP("security-policy-group.xml"), + GUEST_VLAN_FILTER_TERM_ADD("guest-vlan-filter-term-add.xml"), + PUBLIC_IP_FILTER_TERM_ADD("public-ip-filter-term-add.xml"), + FILTER_TERM_GETONE("filter-term-getone.xml"), + FILTER_GETONE("filter-getone.xml"), + FIREWALL_FILTER_BYTES_GETALL("firewall-filter-bytes-getall.xml"), + IKE_POLICY_ADD("ike-policy-add.xml"), + IKE_POLICY_GETONE("ike-policy-getone.xml"), + IKE_POLICY_GETALL("ike-policy-getall.xml"), + IKE_GATEWAY_ADD("ike-gateway-add.xml"), + IKE_GATEWAY_GETONE("ike-gateway-getone.xml"), + IKE_GATEWAY_GETALL("ike-gateway-getall.xml"), + IPSEC_VPN_ADD("ipsec-vpn-add.xml"), + IPSEC_VPN_GETONE("ipsec-vpn-getone.xml"), + IPSEC_VPN_GETALL("ipsec-vpn-getall.xml"), + DYNAMIC_VPN_CLIENT_ADD("dynamic-vpn-client-add.xml"), + DYNAMIC_VPN_CLIENT_GETONE("dynamic-vpn-client-getone.xml"), + DYNAMIC_VPN_CLIENT_GETALL("dynamic-vpn-client-getall.xml"), + ADDRESS_POOL_ADD("address-pool-add.xml"), + ADDRESS_POOL_GETONE("address-pool-getone.xml"), + ADDRESS_POOL_GETALL("address-pool-getall.xml"), + ACCESS_PROFILE_ADD("access-profile-add.xml"), + ACCESS_PROFILE_GETONE("access-profile-getone.xml"), + ACCESS_PROFILE_GETALL("access-profile-getall.xml"), + OPEN_CONFIGURATION("open-configuration.xml"), + CLOSE_CONFIGURATION("close-configuration.xml"), + COMMIT("commit.xml"), + ROLLBACK("rollback.xml"), + TEST("test.xml"); + + private String scriptsDir = "premium-scripts/network/juniper"; + private String xml; + + private SrxXml(String filename) { + this.xml = getXml(filename); + } + + public String getXml() { + return xml; + } + + private String getXml(String filename) { + try { + String xmlFilePath = Script.findScript(scriptsDir, filename); + + if (xmlFilePath == null) { + throw new Exception("Failed to find Juniper SRX XML file: " + filename); + } + + FileReader fr = new FileReader(xmlFilePath); + BufferedReader br = new BufferedReader(fr); + + String xml = ""; + String line; + while ((line = br.readLine()) != null) { + xml += line.trim(); + } + + return xml; + } catch (Exception e) { + s_logger.debug(e); + return null; + } + } + } + + public enum UsageFilter { + VLAN_INPUT("vlan-input", null, "vlan-input"), + VLAN_OUTPUT("vlan-output", null, "vlan-output"), + IP_INPUT(_publicZone, "destination-address", "-i"), + IP_OUTPUT(_privateZone, "source-address", "-o"); + + private String name; + private String counterIdentifier; + private String addressType; + + private UsageFilter(String name, String addressType, String counterIdentifier) { + this.name = name; + this.addressType = addressType; + + if (_usageInterface != null) { + counterIdentifier = _usageInterface + counterIdentifier; + } + + this.counterIdentifier = counterIdentifier; + } + + public String getName() { + return name; + } + + public String getCounterIdentifier() { + return counterIdentifier; + } + + public String getAddressType() { + return addressType; + } + } + + private enum SrxCommand { + LOGIN, OPEN_CONFIGURATION, CLOSE_CONFIGURATION, COMMIT, ROLLBACK, CHECK_IF_EXISTS, CHECK_IF_IN_USE, ADD, DELETE, GET_ALL; + } + + private enum Protocol { + tcp, udp, any; + } + + private enum RuleMatchCondition { + ALL, + PUBLIC_PRIVATE_IPS, + PRIVATE_SUBNET; + } + + private enum GuestNetworkType { + SOURCE_NAT, + INTERFACE_NAT; + } + + private enum SecurityPolicyType { + STATIC_NAT("staticnat"), + DESTINATION_NAT("destnat"), + VPN("vpn"); + + private String identifier; + + private SecurityPolicyType(String identifier) { + this.identifier = identifier; + } + + private String getIdentifier() { + return identifier; + } + } + + public Answer executeRequest(Command cmd) { + if (cmd instanceof ReadyCommand) { + return execute((ReadyCommand) cmd); + } else if (cmd instanceof MaintainCommand) { + return execute((MaintainCommand) cmd); + } else if (cmd instanceof IpAssocCommand) { + return execute((IpAssocCommand) cmd); + } else if (cmd instanceof SetStaticNatRulesCommand) { + return execute((SetStaticNatRulesCommand) cmd); + } else if (cmd instanceof SetPortForwardingRulesCommand) { + return execute((SetPortForwardingRulesCommand) cmd); + } else if (cmd instanceof ExternalNetworkResourceUsageCommand) { + return execute((ExternalNetworkResourceUsageCommand) cmd); + } else if (cmd instanceof RemoteAccessVpnCfgCommand) { + return execute((RemoteAccessVpnCfgCommand) cmd); + } else if (cmd instanceof VpnUsersCfgCommand) { + return execute((VpnUsersCfgCommand) cmd); + } else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } + + public boolean configure(String name, Map params) throws ConfigurationException { + try { + _name = (String) params.get("name"); + if (_name == null) { + throw new ConfigurationException("Unable to find name"); + } + + _zoneId = (String) params.get("zoneId"); + if (_zoneId == null) { + throw new ConfigurationException("Unable to find zone"); + } + + _ip = (String) params.get("ip"); + if (_ip == null) { + throw new ConfigurationException("Unable to find IP"); + } + + _username = (String) params.get("username"); + if (_username == null) { + throw new ConfigurationException("Unable to find username"); + } + + _password = (String) params.get("password"); + if (_password == null) { + throw new ConfigurationException("Unable to find password"); + } + + _publicInterface = (String) params.get("publicInterface"); + if (_publicInterface == null) { + throw new ConfigurationException("Unable to find public interface."); + } + + _usageInterface = (String) params.get("usageInterface"); + + _privateInterface = (String) params.get("privateInterface"); + if (_privateInterface == null) { + throw new ConfigurationException("Unable to find private interface."); + } + + _publicZone = (String) params.get("publicZone"); + if (_publicZone == null) { + throw new ConfigurationException("Unable to find public security zone."); + } + + _privateZone = (String) params.get("privateZone"); + if (_privateZone == null) { + throw new ConfigurationException("Unable to find private security zone."); + } + + _guid = (String)params.get("guid"); + if (_guid == null) { + throw new ConfigurationException("Unable to find the guid"); + } + + _numRetries = NumbersUtil.parseInt((String) params.get("numRetries"), 1); + + _timeoutInSeconds = NumbersUtil.parseInt((String) params.get("timeoutInSeconds"), 300); + + _objectNameWordSep = "-"; + + _ikeProposalName = "cloud-ike-proposal"; + _ipsecPolicyName = "cloud-ipsec-policy"; + _ikeGatewayHostname = "cloud"; + _vpnObjectPrefix = "vpn-a"; + _primaryDnsAddress = "4.2.2.2"; + + // Open a socket and login + if (!refreshSrxConnection()) { + throw new ConfigurationException("Unable to open a connection to the SRX."); + } + + return true; + } catch (Exception e) { + throw new ConfigurationException(e.getMessage()); + } + + } + + public StartupCommand[] initialize() { + StartupExternalFirewallCommand cmd = new StartupExternalFirewallCommand(); + cmd.setName(_name); + cmd.setDataCenter(_zoneId); + cmd.setPod(""); + cmd.setPrivateIpAddress(_ip); + cmd.setStorageIpAddress(""); + cmd.setVersion(""); + cmd.setGuid(_guid); + return new StartupCommand[]{cmd}; + } + + public Host.Type getType() { + return Host.Type.ExternalFirewall; + } + + @Override + public String getName() { + return _name; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public PingCommand getCurrentStatus(final long id) { + return new PingCommand(Host.Type.ExternalFirewall, id); + } + + @Override + public void disconnected() { + closeSocket(); + } + + public IAgentControl getAgentControl() { + return null; + } + + public void setAgentControl(IAgentControl agentControl) { + return; + } + + private Answer execute(ReadyCommand cmd) { + return new ReadyAnswer(cmd); + } + + private Answer execute(MaintainCommand cmd) { + return new MaintainAnswer(cmd); + } + + private synchronized ExternalNetworkResourceUsageAnswer execute(ExternalNetworkResourceUsageCommand cmd) { + try { + return getUsageAnswer(cmd); + } catch (ExecutionException e) { + return new ExternalNetworkResourceUsageAnswer(cmd, e); + } + } + + /* + * Login + */ + + private boolean refreshSrxConnection() { + if (!(closeSocket() && openSocket())) { + return false; + } + + try { + return login(); + } catch (ExecutionException e) { + s_logger.error("Failed to login due to " + e.getMessage()); + return false; + } + } + + private boolean login() throws ExecutionException { + String xml = SrxXml.LOGIN.getXml(); + xml = replaceXmlValue(xml, "username", _username); + xml = replaceXmlValue(xml, "password", _password); + return sendRequestAndCheckResponse(SrxCommand.LOGIN, xml); + } + + private boolean openSocket() { + try { + Socket s = new Socket(_ip, 3221); + s.setKeepAlive(true); + s.setSoTimeout(_timeoutInSeconds * 1000); + _toSrx = new PrintWriter(s.getOutputStream(), true); + _fromSrx = new BufferedReader(new InputStreamReader(s.getInputStream())); + return true; + } catch (IOException e) { + s_logger.error(e); + return false; + } + } + + private boolean closeSocket() { + try { + if (_toSrx != null) { + _toSrx.close(); + } + + if (_fromSrx != null) { + _fromSrx.close(); + } + + return true; + } catch (IOException e) { + s_logger.error(e); + return false; + } + } + + /* + * Commit/rollback + */ + + private void openConfiguration() throws ExecutionException { + String xml = SrxXml.OPEN_CONFIGURATION.getXml(); + String successMsg = "Opened a private configuration."; + String errorMsg = "Failed to open a private configuration."; + + if (!sendRequestAndCheckResponse(SrxCommand.OPEN_CONFIGURATION, xml)) { + throw new ExecutionException(errorMsg); + } else { + s_logger.debug(successMsg); + } + } + + private void closeConfiguration() { + String xml = SrxXml.CLOSE_CONFIGURATION.getXml(); + String successMsg = "Closed private configuration."; + String errorMsg = "Failed to close private configuration."; + + try { + if (!sendRequestAndCheckResponse(SrxCommand.CLOSE_CONFIGURATION, xml)) { + s_logger.error(errorMsg); + } + } catch (ExecutionException e) { + s_logger.error(errorMsg); + } + + s_logger.debug(successMsg); + } + + private void commitConfiguration() throws ExecutionException { + String xml = SrxXml.COMMIT.getXml(); + String successMsg = "Committed to global configuration."; + String errorMsg = "Failed to commit to global configuration."; + + if (!sendRequestAndCheckResponse(SrxCommand.COMMIT, xml)) { + throw new ExecutionException(errorMsg); + } else { + s_logger.debug(successMsg); + closeConfiguration(); + } + } + + /* + * Guest networks + */ + + private synchronized Answer execute(IpAssocCommand cmd) { + return execute(cmd, _numRetries); + } + + private Answer execute(IpAssocCommand cmd, int numRetries) { + String[] results = new String[cmd.getIpAddresses().length]; + int i = 0; + try { + IpAddressTO ip; + if (cmd.getIpAddresses().length != 1) { + throw new ExecutionException("Received an invalid number of guest IPs to associate."); + } else { + ip = cmd.getIpAddresses()[0]; + } + + String sourceNatIpAddress = null; + GuestNetworkType type = GuestNetworkType.INTERFACE_NAT; + + if (ip.isSourceNat()) { + type = GuestNetworkType.SOURCE_NAT; + + if (ip.getPublicIp() == null) { + throw new ExecutionException("Source NAT IP address must not be null."); + } else { + sourceNatIpAddress = ip.getPublicIp(); + } + } + + long guestVlanTag = Long.parseLong(cmd.getAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG)); + String guestVlanGateway = cmd.getAccessDetail(NetworkElementCommand.GUEST_NETWORK_GATEWAY); + String cidr = cmd.getAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR); + long cidrSize = NetUtils.cidrToLong(cidr)[1]; + String guestVlanSubnet = NetUtils.getCidrSubNet(guestVlanGateway, cidrSize); + + Long publicVlanTag = null; + if (!ip.getVlanId().equals("untagged")) { + try { + publicVlanTag = Long.parseLong(ip.getVlanId()); + } catch (Exception e) { + throw new ExecutionException("Could not parse public VLAN tag: " + ip.getVlanId()); + } + } + + openConfiguration(); + + // Remove the guest network: + // Remove source, static, and destination NAT rules + // Remove VPN + shutdownGuestNetwork(type, ip.getAccountId(), publicVlanTag, sourceNatIpAddress, guestVlanTag, guestVlanGateway, guestVlanSubnet, cidrSize); + + if (ip.isAdd()) { + // Implement the guest network for this VLAN + implementGuestNetwork(type, publicVlanTag, sourceNatIpAddress, guestVlanTag, guestVlanGateway, guestVlanSubnet, cidrSize); + } + + commitConfiguration(); + results[i++] = ip.getPublicIp() + " - success"; + } catch (ExecutionException e) { + s_logger.error(e); + closeConfiguration(); + + if (numRetries > 0 && refreshSrxConnection()) { + int numRetriesRemaining = numRetries - 1; + s_logger.debug("Retrying IPAssocCommand. Number of retries remaining: " + numRetriesRemaining); + return execute(cmd, numRetriesRemaining); + } else { + results[i++] = IpAssocAnswer.errorResult; + } + } + + return new IpAssocAnswer(cmd, results); + } + + private void implementGuestNetwork(GuestNetworkType type, Long publicVlanTag, String publicIp, long privateVlanTag, String privateGateway, String privateSubnet, long privateCidrNumber) throws ExecutionException { + privateGateway = privateGateway + "/" + privateCidrNumber; + privateSubnet = privateSubnet + "/" + privateCidrNumber; + + managePrivateInterface(SrxCommand.ADD, !type.equals(GuestNetworkType.SOURCE_NAT), privateVlanTag, privateGateway); + manageZoneInterface(SrxCommand.ADD, privateVlanTag); + + if (type.equals(GuestNetworkType.SOURCE_NAT)) { + manageSourceNatPool(SrxCommand.ADD, publicIp); + manageSourceNatRule(SrxCommand.ADD, publicIp, privateSubnet); + manageProxyArp(SrxCommand.ADD, publicVlanTag, publicIp); + manageUsageFilter(SrxCommand.ADD, UsageFilter.IP_OUTPUT, privateSubnet, null, genIpFilterTermName(publicIp)); + manageUsageFilter(SrxCommand.ADD, UsageFilter.IP_INPUT, publicIp, null, genIpFilterTermName(publicIp)); + } else if (type.equals(GuestNetworkType.INTERFACE_NAT)){ + manageUsageFilter(SrxCommand.ADD, UsageFilter.VLAN_OUTPUT, null, privateVlanTag, null); + manageUsageFilter(SrxCommand.ADD, UsageFilter.VLAN_INPUT, null, privateVlanTag, null); + } + + String msg = "Implemented guest network with type " + type + ". Guest VLAN tag: " + privateVlanTag + ", guest gateway: " + privateGateway; + msg += type.equals(GuestNetworkType.SOURCE_NAT) ? ", source NAT IP: " + publicIp : ""; + s_logger.debug(msg); + } + + private void shutdownGuestNetwork(GuestNetworkType type, long accountId, Long publicVlanTag, String sourceNatIpAddress, long privateVlanTag, String privateGateway, String privateSubnet, long privateCidrSize) throws ExecutionException { + // Remove static and destination NAT rules for the guest network + removeStaticAndDestNatRulesInPrivateVlan(privateVlanTag, privateGateway, privateCidrSize); + + privateGateway = privateGateway + "/" + privateCidrSize; + privateSubnet = privateSubnet + "/" + privateCidrSize; + + managePrivateInterface(SrxCommand.DELETE, false, privateVlanTag, privateGateway); + manageZoneInterface(SrxCommand.DELETE, privateVlanTag); + deleteVpnObjectsForAccount(accountId); + + if (type.equals(GuestNetworkType.SOURCE_NAT)) { + manageSourceNatRule(SrxCommand.DELETE, sourceNatIpAddress, privateSubnet); + manageSourceNatPool(SrxCommand.DELETE, sourceNatIpAddress); + manageProxyArp(SrxCommand.DELETE, publicVlanTag, sourceNatIpAddress); + manageUsageFilter(SrxCommand.DELETE, UsageFilter.IP_OUTPUT, privateSubnet, null, genIpFilterTermName(sourceNatIpAddress)); + manageUsageFilter(SrxCommand.DELETE, UsageFilter.IP_INPUT, sourceNatIpAddress, null, genIpFilterTermName(sourceNatIpAddress)); + } else if (type.equals(GuestNetworkType.INTERFACE_NAT)) { + manageUsageFilter(SrxCommand.DELETE, UsageFilter.VLAN_OUTPUT, null, privateVlanTag, null); + manageUsageFilter(SrxCommand.DELETE, UsageFilter.VLAN_INPUT, null, privateVlanTag, null); + } + + String msg = "Shut down guest network with type " + type +". Guest VLAN tag: " + privateVlanTag + ", guest gateway: " + privateGateway; + msg += type.equals(GuestNetworkType.SOURCE_NAT) ? ", source NAT IP: " + sourceNatIpAddress : ""; + s_logger.debug(msg); + } + + /* + * Static NAT + */ + + private synchronized Answer execute(SetStaticNatRulesCommand cmd) { + return execute(cmd, _numRetries); + } + + private Answer execute(SetStaticNatRulesCommand cmd, int numRetries) { + StaticNatRuleTO[] allRules = cmd.getRules(); + Map> activeRules = getActiveRules(allRules); + + try { + openConfiguration(); + + Set ipPairs = activeRules.keySet(); + for (String ipPair : ipPairs) { + String[] ipPairComponents = ipPair.split("-"); + String publicIp = ipPairComponents[0]; + String privateIp = ipPairComponents[1]; + + List activeRulesForIpPair = activeRules.get(ipPair); + Long publicVlanTag = getVlanTag(activeRulesForIpPair.get(0).getSrcVlanTag()); + + // Delete the existing static NAT rule for this IP pair + removeStaticNatRule(publicVlanTag, publicIp, privateIp); + + if (activeRulesForIpPair.size() > 0) { + // If there are active FirewallRules for this IP pair, add the static NAT rule and open the specified port ranges + addStaticNatRule(publicVlanTag, publicIp, privateIp, activeRulesForIpPair); + } + } + + commitConfiguration(); + return new Answer(cmd); + } catch (ExecutionException e) { + s_logger.error(e); + closeConfiguration(); + + if (numRetries > 0 && refreshSrxConnection()) { + int numRetriesRemaining = numRetries - 1; + s_logger.debug("Retrying SetPortForwardingRulesCommand. Number of retries remaining: " + numRetriesRemaining); + return execute(cmd, numRetriesRemaining); + } else { + return new Answer(cmd, e); + } + } + } + + private void addStaticNatRule(Long publicVlanTag, String publicIp, String privateIp, List rules) throws ExecutionException { + manageProxyArp(SrxCommand.ADD, publicVlanTag, publicIp); + manageStaticNatRule(SrxCommand.ADD, publicIp, privateIp); + manageUsageFilter(SrxCommand.ADD, UsageFilter.IP_INPUT, publicIp, null, genIpFilterTermName(publicIp)); + manageAddressBookEntry(SrxCommand.ADD, _privateZone, privateIp, null); + + // Add a new security policy with the current set of applications + addSecurityPolicyAndApplications(SecurityPolicyType.STATIC_NAT, privateIp, extractApplications(rules)); + + s_logger.debug("Added static NAT rule for public IP " + publicIp + ", and private IP " + privateIp); + } + + private void removeStaticNatRule(Long publicVlanTag, String publicIp, String privateIp) throws ExecutionException { + manageStaticNatRule(SrxCommand.DELETE, publicIp, privateIp); + manageProxyArp(SrxCommand.DELETE, publicVlanTag, publicIp); + manageUsageFilter(SrxCommand.DELETE, UsageFilter.IP_INPUT, publicIp, null, genIpFilterTermName(publicIp)); + + // Remove any existing security policy and clean up applications + removeSecurityPolicyAndApplications(SecurityPolicyType.STATIC_NAT, privateIp); + + manageAddressBookEntry(SrxCommand.DELETE, _privateZone, privateIp, null); + + s_logger.debug("Removed static NAT rule for public IP " + publicIp + ", and private IP " + privateIp); + } + + private void removeStaticNatRules(Long privateVlanTag, Map publicVlanTags, List staticNatRules) throws ExecutionException { + for (String[] staticNatRuleToRemove : staticNatRules) { + String staticNatRulePublicIp = staticNatRuleToRemove[0]; + String staticNatRulePrivateIp = staticNatRuleToRemove[1]; + + Long publicVlanTag = null; + if (publicVlanTags.containsKey(staticNatRulePublicIp)) { + publicVlanTag = publicVlanTags.get(staticNatRulePublicIp); + } + + if (privateVlanTag != null) { + s_logger.warn("Found a static NAT rule (" + staticNatRulePublicIp + " <-> " + staticNatRulePrivateIp + ") for guest VLAN with tag " + privateVlanTag + " that is active when the guest network is being removed. Removing rule..."); + } + + removeStaticNatRule(publicVlanTag, staticNatRulePublicIp, staticNatRulePrivateIp); + } + } + + /* + * VPN + */ + + private synchronized Answer execute(RemoteAccessVpnCfgCommand cmd) { + return execute(cmd, _numRetries); + } + + private Answer execute(RemoteAccessVpnCfgCommand cmd, int numRetries) { + long accountId = Long.parseLong(cmd.getAccessDetail(NetworkElementCommand.ACCOUNT_ID)); + String guestNetworkCidr = cmd.getAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR); + String preSharedKey = cmd.getPresharedKey(); + String[] ipRange = cmd.getIpRange().split("-"); + + try { + openConfiguration(); + + // Delete existing VPN objects for this account + deleteVpnObjectsForAccount(accountId); + + if (cmd.isCreate()) { + // Add IKE policy + manageIkePolicy(SrxCommand.ADD, null, accountId, preSharedKey); + + // Add address pool + manageAddressPool(SrxCommand.ADD, null, accountId, guestNetworkCidr, ipRange[0], ipRange[1], _primaryDnsAddress); + } + + commitConfiguration(); + + return new Answer(cmd); + } catch (ExecutionException e) { + s_logger.error(e); + closeConfiguration(); + + if (numRetries > 0 && refreshSrxConnection()) { + int numRetriesRemaining = numRetries - 1; + s_logger.debug("Retrying RemoteAccessVpnCfgCommand. Number of retries remaining: " + numRetriesRemaining); + return execute(cmd, numRetriesRemaining); + } else { + return new Answer(cmd, e); + } + } + + } + + private void deleteVpnObjectsForAccount(long accountId) throws ExecutionException { + // Delete all IKE policies + for (String ikePolicyName : getVpnObjectNames(SrxXml.IKE_POLICY_GETALL, accountId)) { + manageIkePolicy(SrxCommand.DELETE, ikePolicyName, null, null); + } + + // Delete all address pools + for (String addressPoolName : getVpnObjectNames(SrxXml.ADDRESS_POOL_GETALL, accountId)) { + manageAddressPool(SrxCommand.DELETE, addressPoolName, null, null, null, null, null); + } + + // Delete all IKE gateways + for (String ikeGatewayName : getVpnObjectNames(SrxXml.IKE_GATEWAY_GETALL, accountId)) { + manageIkeGateway(SrxCommand.DELETE, ikeGatewayName, null, null, null, null); + } + + // Delete all IPsec VPNs + for (String ipsecVpnName : getVpnObjectNames(SrxXml.IPSEC_VPN_GETALL, accountId)) { + manageIpsecVpn(SrxCommand.DELETE, ipsecVpnName, null, null, null, null); + } + + // Delete all dynamic VPN clients + for (String dynamicVpnClientName : getVpnObjectNames(SrxXml.DYNAMIC_VPN_CLIENT_GETALL, accountId)) { + manageDynamicVpnClient(SrxCommand.DELETE, dynamicVpnClientName, null, null, null, null); + } + + // Delete all access profiles + for (String accessProfileName : getVpnObjectNames(SrxXml.ACCESS_PROFILE_GETALL, accountId)) { + manageAccessProfile(SrxCommand.DELETE, accessProfileName, null, null, null, null); + } + + // Delete all security policies + for (String securityPolicyName : getVpnObjectNames(SrxXml.SECURITY_POLICY_GETALL, accountId)) { + manageSecurityPolicy(SecurityPolicyType.VPN, SrxCommand.DELETE, accountId, null, null, null, securityPolicyName); + } + + // Delete all address book entries + for (String addressBookEntryName : getVpnObjectNames(SrxXml.ADDRESS_BOOK_ENTRY_GETALL, accountId)) { + manageAddressBookEntry(SrxCommand.DELETE, _privateZone, null, addressBookEntryName); + } + + } + + public List getVpnObjectNames(SrxXml xmlObj, long accountId) throws ExecutionException { + List vpnObjectNames = new ArrayList(); + + String xmlRequest = xmlObj.getXml(); + if (xmlObj.equals(SrxXml.SECURITY_POLICY_GETALL)) { + xmlRequest = replaceXmlValue(xmlRequest, "from-zone", _publicZone); + xmlRequest = replaceXmlValue(xmlRequest, "to-zone", _privateZone); + } else if (xmlObj.equals(SrxXml.ADDRESS_BOOK_ENTRY_GETALL)) { + xmlRequest = replaceXmlValue(xmlRequest, "zone", _privateZone); + } + + String xmlResponse = sendRequest(xmlRequest); + Document doc = getDocument(xmlResponse); + NodeList vpnObjectNameNodes = doc.getElementsByTagName("name"); + for (int i = 0; i < vpnObjectNameNodes.getLength(); i++) { + NodeList vpnObjectNameEntries = vpnObjectNameNodes.item(i).getChildNodes(); + for (int j = 0; j < vpnObjectNameEntries.getLength(); j++) { + String vpnObjectName = vpnObjectNameEntries.item(j).getNodeValue(); + if (vpnObjectName.startsWith(genObjectName(_vpnObjectPrefix, String.valueOf(accountId)))) { + vpnObjectNames.add(vpnObjectName); + } + } + } + + return vpnObjectNames; + } + + private synchronized Answer execute(VpnUsersCfgCommand cmd) { + return execute(cmd, _numRetries); + } + + private Answer execute(VpnUsersCfgCommand cmd, int numRetries) { + long accountId = Long.parseLong(cmd.getAccessDetail(NetworkElementCommand.ACCOUNT_ID)); + String guestNetworkCidr = cmd.getAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR); + String ikePolicyName = genIkePolicyName(accountId); + UsernamePassword[] users = cmd.getUserpwds(); + + try { + openConfiguration(); + + for (UsernamePassword user : users) { + SrxCommand srxCmd = user.isAdd() ? SrxCommand.ADD : SrxCommand.DELETE; + + String ipsecVpnName = genIpsecVpnName(accountId, user.getUsername()); + + // IKE gateway + manageIkeGateway(srxCmd, null, accountId, ikePolicyName, _ikeGatewayHostname , user.getUsername()); + + // IPSec VPN + manageIpsecVpn(srxCmd, null, accountId, guestNetworkCidr, user.getUsername(), _ipsecPolicyName); + + // Dynamic VPN client + manageDynamicVpnClient(srxCmd, null, accountId, guestNetworkCidr, ipsecVpnName, user.getUsername()); + + // Access profile + manageAccessProfile(srxCmd, null, accountId, user.getUsername(), user.getPassword(), genAddressPoolName(accountId)); + + // Address book entry + manageAddressBookEntry(srxCmd, _privateZone , guestNetworkCidr, ipsecVpnName); + + // Security policy + manageSecurityPolicy(SecurityPolicyType.VPN, srxCmd, null, null, guestNetworkCidr, null, ipsecVpnName); + } + + commitConfiguration(); + + return new Answer(cmd); + } catch (ExecutionException e) { + s_logger.error(e); + closeConfiguration(); + + if (numRetries > 0 && refreshSrxConnection()) { + int numRetriesRemaining = numRetries - 1; + s_logger.debug("Retrying RemoteAccessVpnCfgCommand. Number of retries remaining: " + numRetriesRemaining); + return execute(cmd, numRetriesRemaining); + } else { + return new Answer(cmd, e); + } + } + + } + + /* + * Destination NAT + */ + + private synchronized Answer execute (SetPortForwardingRulesCommand cmd) { + return execute(cmd, _numRetries); + } + + private Answer execute(SetPortForwardingRulesCommand cmd, int numRetries) { + PortForwardingRuleTO[] allRules = cmd.getRules(); + Map> activeRules = getActiveRules(allRules); + + try { + openConfiguration(); + + Set ipPairs = activeRules.keySet(); + for (String ipPair : ipPairs) { + String[] ipPairComponents = ipPair.split("-"); + String publicIp = ipPairComponents[0]; + String privateIp = ipPairComponents[1]; + + List activeRulesForIpPair = activeRules.get(ipPair); + + // Get a list of all destination NAT rules for the public/private IP address pair + List destNatRules = getDestNatRules(RuleMatchCondition.PUBLIC_PRIVATE_IPS, publicIp, privateIp, null, null); + Map publicVlanTags = getPublicVlanTagsForNatRules(destNatRules); + + // Delete all of these rules, along with the destination NAT pools and security policies they use + removeDestinationNatRules(null, publicVlanTags, destNatRules); + + // If there are active rules for the public/private IP address pair, add them back + for (FirewallRuleTO rule : activeRulesForIpPair) { + Long publicVlanTag = getVlanTag(rule.getSrcVlanTag()); + PortForwardingRuleTO portForwardingRule = (PortForwardingRuleTO) rule; + addDestinationNatRule(getProtocol(rule.getProtocol()), publicVlanTag, portForwardingRule.getSrcIp(), portForwardingRule.getDstIp(), + portForwardingRule.getSrcPortRange()[0], portForwardingRule.getSrcPortRange()[1], + portForwardingRule.getDstPortRange()[0], portForwardingRule.getDstPortRange()[1]); + } + } + + commitConfiguration(); + return new Answer(cmd); + } catch (ExecutionException e) { + s_logger.error(e); + closeConfiguration(); + + if (numRetries > 0 && refreshSrxConnection()) { + int numRetriesRemaining = numRetries - 1; + s_logger.debug("Retrying SetPortForwardingRulesCommand. Number of retries remaining: " + numRetriesRemaining); + return execute(cmd, numRetriesRemaining); + } else { + return new Answer(cmd, e); + } + } + } + + private void addDestinationNatRule(Protocol protocol, Long publicVlanTag, String publicIp, String privateIp, int srcPortStart, int srcPortEnd, int destPortStart, int destPortEnd) throws ExecutionException { + manageProxyArp(SrxCommand.ADD, publicVlanTag, publicIp); + + int offset = 0; + for (int srcPort = srcPortStart; srcPort <= srcPortEnd; srcPort++) { + int destPort = destPortStart + offset; + manageDestinationNatPool(SrxCommand.ADD, privateIp, destPort); + manageDestinationNatRule(SrxCommand.ADD, publicIp, privateIp, srcPort, destPort); + offset += 1; + } + + manageAddressBookEntry(SrxCommand.ADD, _privateZone, privateIp, null); + + List applications = new ArrayList(); + applications.add(new Object[]{protocol, destPortStart, destPortEnd}); + addSecurityPolicyAndApplications(SecurityPolicyType.DESTINATION_NAT, privateIp, applications); + + String srcPortRange = srcPortStart + "-" + srcPortEnd; + String destPortRange = destPortStart + "-" + destPortEnd; + s_logger.debug("Added destination NAT rule for protocol " + protocol + ", public IP " + publicIp + ", private IP " + privateIp + ", source port range " + srcPortRange + ", and dest port range " + destPortRange); + } + + private void removeDestinationNatRule(Long publicVlanTag, String publicIp, String privateIp, int srcPort, int destPort) throws ExecutionException { + manageDestinationNatRule(SrxCommand.DELETE, publicIp, privateIp, srcPort, destPort); + manageDestinationNatPool(SrxCommand.DELETE, privateIp, destPort); + manageProxyArp(SrxCommand.DELETE, publicVlanTag, publicIp); + + removeSecurityPolicyAndApplications(SecurityPolicyType.DESTINATION_NAT, privateIp); + + manageAddressBookEntry(SrxCommand.DELETE, _privateZone, privateIp, null); + + s_logger.debug("Removed destination NAT rule for public IP " + publicIp + ", private IP " + privateIp + ", source port " + srcPort + ", and dest port " + destPort); + } + + + private void removeDestinationNatRules(Long privateVlanTag, Map publicVlanTags, List destNatRules) throws ExecutionException { + for (String[] destNatRule : destNatRules) { + String publicIp = destNatRule[0]; + String privateIp = destNatRule[1]; + int srcPort = Integer.valueOf(destNatRule[2]); + int destPort = Integer.valueOf(destNatRule[3]); + + Long publicVlanTag = null; + if (publicVlanTags.containsKey(publicIp)) { + publicVlanTag = publicVlanTags.get(publicIp); + } + + if (privateVlanTag != null) { + s_logger.warn("Found a destination NAT rule (public IP: " + publicIp + ", private IP: " + privateIp + + ", public port: " + srcPort + ", private port: " + destPort + ") for guest VLAN with tag " + + privateVlanTag + " that is active when the guest network is being removed. Removing rule..."); + } + + removeDestinationNatRule(publicVlanTag, publicIp, privateIp, srcPort, destPort); + } + } + + /* + * General NAT utils + */ + + private List getAllStaticAndDestNatRules() throws ExecutionException { + List staticAndDestNatRules = new ArrayList(); + staticAndDestNatRules.addAll(getStaticNatRules(RuleMatchCondition.ALL, null, null)); + staticAndDestNatRules.addAll(getDestNatRules(RuleMatchCondition.ALL, null, null, null, null)); + return staticAndDestNatRules; + } + + private void removeStaticAndDestNatRulesInPrivateVlan(long privateVlanTag, String privateGateway, long privateCidrSize) throws ExecutionException { + List staticNatRulesToRemove = getStaticNatRules(RuleMatchCondition.PRIVATE_SUBNET, privateGateway, privateCidrSize); + List destNatRulesToRemove = getDestNatRules(RuleMatchCondition.PRIVATE_SUBNET, null, null, privateGateway, privateCidrSize); + + List publicIps = new ArrayList(); + addPublicIpsToList(staticNatRulesToRemove, publicIps); + addPublicIpsToList(destNatRulesToRemove, publicIps); + + Map publicVlanTags = getPublicVlanTagsForPublicIps(publicIps); + + removeStaticNatRules(privateVlanTag, publicVlanTags, staticNatRulesToRemove); + removeDestinationNatRules(privateVlanTag, publicVlanTags, destNatRulesToRemove); + } + + private Map> getActiveRules(FirewallRuleTO[] allRules) { + Map> activeRules = new HashMap>(); + + for (FirewallRuleTO rule : allRules) { + String ipPair; + + if (rule.getPurpose().equals(Purpose.StaticNat)) { + StaticNatRuleTO staticNatRule = (StaticNatRuleTO) rule; + ipPair = staticNatRule.getSrcIp() + "-" + staticNatRule.getDstIp(); + } else if (rule.getPurpose().equals(Purpose.PortForwarding)) { + PortForwardingRuleTO portForwardingRule = (PortForwardingRuleTO) rule; + ipPair = portForwardingRule.getSrcIp() + "-" + portForwardingRule.getDstIp(); + } else { + continue; + } + + ArrayList activeRulesForIpPair = activeRules.get(ipPair); + + if (activeRulesForIpPair == null) { + activeRulesForIpPair = new ArrayList(); + } + + if (!rule.revoked() || rule.isAlreadyAdded()) { + activeRulesForIpPair.add(rule); + } + + activeRules.put(ipPair, activeRulesForIpPair); + } + + return activeRules; + } + + /* + * VPN + */ + + private String genIkePolicyName(long accountId) { + return genObjectName(_vpnObjectPrefix, String.valueOf(accountId)); + } + + private boolean manageIkePolicy(SrxCommand command, String ikePolicyName, Long accountId, String preSharedKey) throws ExecutionException { + if (ikePolicyName == null) { + ikePolicyName = genIkePolicyName(accountId); + } + + String xml; + + switch(command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.IKE_GATEWAY_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "policy-name", ikePolicyName); + return sendRequestAndCheckResponse(command, xml, "name", ikePolicyName); + + case ADD: + if (manageIkePolicy(SrxCommand.CHECK_IF_EXISTS, ikePolicyName, accountId, preSharedKey)) { + return true; + } + + xml = SrxXml.IKE_POLICY_ADD.getXml(); + xml = replaceXmlValue(xml, "policy-name", ikePolicyName); + xml = replaceXmlValue(xml, "proposal-name", _ikeProposalName); + xml = replaceXmlValue(xml, "pre-shared-key", preSharedKey); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add IKE policy: " + ikePolicyName); + } else { + return true; + } + + case DELETE: + if (!manageIkePolicy(SrxCommand.CHECK_IF_EXISTS, ikePolicyName, accountId, preSharedKey)) { + return true; + } + + xml = SrxXml.IKE_GATEWAY_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "policy-name", ikePolicyName); + + if (!sendRequestAndCheckResponse(command, xml, "name", ikePolicyName)) { + throw new ExecutionException("Failed to delete IKE policy: " + ikePolicyName); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + } + + } + + private String genIkeGatewayName(long accountId, String username) { + return genObjectName(_vpnObjectPrefix, String.valueOf(accountId), username); + } + + private boolean manageIkeGateway(SrxCommand command, String ikeGatewayName, Long accountId, String ikePolicyName, String ikeGatewayHostname, String username) throws ExecutionException { + if (ikeGatewayName == null) { + ikeGatewayName = genIkeGatewayName(accountId, username); + } + + String xml; + + switch(command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.IKE_GATEWAY_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "gateway-name", ikeGatewayName); + return sendRequestAndCheckResponse(command, xml, "name", ikeGatewayName); + + case ADD: + if (manageIkeGateway(SrxCommand.CHECK_IF_EXISTS, ikeGatewayName, accountId, ikePolicyName, ikeGatewayHostname, username)) { + return true; + } + + xml = SrxXml.IKE_GATEWAY_ADD.getXml(); + xml = replaceXmlValue(xml, "gateway-name", ikeGatewayName); + xml = replaceXmlValue(xml, "ike-policy-name", ikePolicyName); + xml = replaceXmlValue(xml, "ike-gateway-hostname", ikeGatewayHostname); + xml = replaceXmlValue(xml, "public-interface-name", _publicInterface); + xml = replaceXmlValue(xml, "access-profile-name", genAccessProfileName(accountId, username)); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add IKE gateway: " + ikeGatewayName); + } else { + return true; + } + + case DELETE: + if (!manageIkeGateway(SrxCommand.CHECK_IF_EXISTS, ikeGatewayName, accountId, ikePolicyName, ikeGatewayHostname, username)) { + return true; + } + + xml = SrxXml.IKE_GATEWAY_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "gateway-name", ikeGatewayName); + + if (!sendRequestAndCheckResponse(command, xml, "name", ikeGatewayName)) { + throw new ExecutionException("Failed to delete IKE gateway: " + ikeGatewayName); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + private String genIpsecVpnName(long accountId, String username) { + return genObjectName(_vpnObjectPrefix, String.valueOf(accountId), username); + } + + private boolean manageIpsecVpn(SrxCommand command, String ipsecVpnName, Long accountId, String guestNetworkCidr, String username, String ipsecPolicyName) throws ExecutionException { + if (ipsecVpnName == null) { + ipsecVpnName = genIpsecVpnName(accountId, username); + } + + String xml; + + switch(command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.IPSEC_VPN_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "ipsec-vpn-name", ipsecVpnName); + return sendRequestAndCheckResponse(command, xml, "name", ipsecVpnName); + + case ADD: + if (manageIpsecVpn(SrxCommand.CHECK_IF_EXISTS, ipsecVpnName, accountId, guestNetworkCidr, username, ipsecPolicyName)) { + return true; + } + + xml = SrxXml.IPSEC_VPN_ADD.getXml(); + xml = replaceXmlValue(xml, "ipsec-vpn-name", ipsecVpnName); + xml = replaceXmlValue(xml, "ike-gateway", genIkeGatewayName(accountId, username)); + xml = replaceXmlValue(xml, "ipsec-policy-name", ipsecPolicyName); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add IPSec VPN: " + ipsecVpnName); + } else { + return true; + } + + case DELETE: + if (!manageIpsecVpn(SrxCommand.CHECK_IF_EXISTS, ipsecVpnName, accountId, guestNetworkCidr, username, ipsecPolicyName)) { + return true; + } + + xml = SrxXml.IPSEC_VPN_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "ipsec-vpn-name", ipsecVpnName); + + if (!sendRequestAndCheckResponse(command, xml, "name", ipsecVpnName)) { + throw new ExecutionException("Failed to delete IPSec VPN: " + ipsecVpnName); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + private String genDynamicVpnClientName(long accountId, String username) { + return genObjectName(_vpnObjectPrefix, String.valueOf(accountId), username); + } + + private boolean manageDynamicVpnClient(SrxCommand command, String clientName, Long accountId, String guestNetworkCidr, String ipsecVpnName, String username) throws ExecutionException { + if (clientName == null) { + clientName = genDynamicVpnClientName(accountId, username); + } + + String xml; + + switch(command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.DYNAMIC_VPN_CLIENT_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "client-name", clientName); + return sendRequestAndCheckResponse(command, xml, "name", clientName); + + case ADD: + if (manageDynamicVpnClient(SrxCommand.CHECK_IF_EXISTS, clientName, accountId, guestNetworkCidr, ipsecVpnName, username)) { + return true; + } + + xml = SrxXml.DYNAMIC_VPN_CLIENT_ADD.getXml(); + xml = replaceXmlValue(xml, "client-name", clientName); + xml = replaceXmlValue(xml, "guest-network-cidr", guestNetworkCidr); + xml = replaceXmlValue(xml, "ipsec-vpn-name", ipsecVpnName); + xml = replaceXmlValue(xml, "username", username); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add dynamic VPN client: " + clientName); + } else { + return true; + } + + case DELETE: + if (!manageDynamicVpnClient(SrxCommand.CHECK_IF_EXISTS, clientName, accountId, guestNetworkCidr, ipsecVpnName, username)) { + return true; + } + + xml = SrxXml.DYNAMIC_VPN_CLIENT_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "client-name", clientName); + + if (!sendRequestAndCheckResponse(command, xml, "name", clientName)) { + throw new ExecutionException("Failed to delete dynamic VPN client: " + clientName); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + private String genAddressPoolName(long accountId) { + return genObjectName(_vpnObjectPrefix, String.valueOf(accountId)); + } + + private boolean manageAddressPool(SrxCommand command, String addressPoolName, Long accountId, String guestNetworkCidr, String lowAddress, String highAddress, String primaryDnsAddress) throws ExecutionException { + if (addressPoolName == null) { + addressPoolName = genAddressPoolName(accountId); + } + + String xml; + + switch(command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.ADDRESS_POOL_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "address-pool-name", addressPoolName); + return sendRequestAndCheckResponse(command, xml, "name", addressPoolName); + + case ADD: + if (manageAddressPool(SrxCommand.CHECK_IF_EXISTS, addressPoolName, accountId, guestNetworkCidr, lowAddress, highAddress, primaryDnsAddress)) { + return true; + } + + xml = SrxXml.ADDRESS_POOL_ADD.getXml(); + xml = replaceXmlValue(xml, "address-pool-name", addressPoolName); + xml = replaceXmlValue(xml, "guest-network-cidr", guestNetworkCidr); + xml = replaceXmlValue(xml, "address-range-name", "r-" + addressPoolName); + xml = replaceXmlValue(xml, "low-address", lowAddress); + xml = replaceXmlValue(xml, "high-address", highAddress); + xml = replaceXmlValue(xml, "primary-dns-address", primaryDnsAddress); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add address pool: " + addressPoolName); + } else { + return true; + } + + case DELETE: + if (!manageAddressPool(SrxCommand.CHECK_IF_EXISTS, addressPoolName, accountId, guestNetworkCidr, lowAddress, highAddress, primaryDnsAddress)) { + return true; + } + + xml = SrxXml.ADDRESS_POOL_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "address-pool-name", addressPoolName); + + if (!sendRequestAndCheckResponse(command, xml, "name", addressPoolName)) { + throw new ExecutionException("Failed to delete address pool: " + addressPoolName); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + private String genAccessProfileName(long accountId, String username) { + return genObjectName(_vpnObjectPrefix, String.valueOf(accountId), username); + } + + private boolean manageAccessProfile(SrxCommand command, String accessProfileName, Long accountId, String username, String password, String addressPoolName) throws ExecutionException { + if (accessProfileName == null) { + accessProfileName = genAccessProfileName(accountId, username); + } + + String xml; + + switch(command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.ACCESS_PROFILE_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "access-profile-name", accessProfileName); + return sendRequestAndCheckResponse(command, xml, "name", username); + + case ADD: + if (manageAccessProfile(SrxCommand.CHECK_IF_EXISTS, accessProfileName, accountId, username, password, addressPoolName)) { + return true; + } + + xml = SrxXml.ACCESS_PROFILE_ADD.getXml(); + xml = replaceXmlValue(xml, "access-profile-name", accessProfileName); + xml = replaceXmlValue(xml, "username", username); + xml = replaceXmlValue(xml, "password", password); + xml = replaceXmlValue(xml, "address-pool-name", addressPoolName); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add access profile: " + accessProfileName); + } else { + return true; + } + + case DELETE: + if (!manageAccessProfile(SrxCommand.CHECK_IF_EXISTS, accessProfileName, accountId, username, password, addressPoolName)) { + return true; + } + + xml = SrxXml.ACCESS_PROFILE_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "access-profile-name", accessProfileName); + + if (!sendRequestAndCheckResponse(command, xml, "name", username)) { + throw new ExecutionException("Failed to delete access profile: " + accessProfileName); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + /* + * Private interfaces + */ + + private boolean managePrivateInterface(SrxCommand command, boolean addFilters, long vlanTag, String privateInterfaceIp) throws ExecutionException { + String xml; + + switch (command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.PRIVATE_INTERFACE_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "private-interface-name", _privateInterface); + xml = replaceXmlValue(xml, "vlan-id", String.valueOf(vlanTag)); + return sendRequestAndCheckResponse(command, xml, "name", String.valueOf(vlanTag)); + + case ADD: + if (managePrivateInterface(SrxCommand.CHECK_IF_EXISTS, false, vlanTag, privateInterfaceIp)) { + return true; + } + + xml = addFilters ? SrxXml.PRIVATE_INTERFACE_WITH_FILTERS_ADD.getXml() : SrxXml.PRIVATE_INTERFACE_ADD.getXml(); + xml = replaceXmlValue(xml, "private-interface-name", _privateInterface); + xml = replaceXmlValue(xml, "vlan-id", String.valueOf(vlanTag)); + xml = replaceXmlValue(xml, "private-interface-ip", privateInterfaceIp); + + if (addFilters) { + xml = replaceXmlValue(xml, "input-filter-name", UsageFilter.VLAN_INPUT.getName() + "-" + vlanTag); + xml = replaceXmlValue(xml, "output-filter-name", UsageFilter.VLAN_OUTPUT.getName() + "-" + vlanTag); + } + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add private interface for guest VLAN tag " + vlanTag); + } else { + return true; + } + + case DELETE: + if (!managePrivateInterface(SrxCommand.CHECK_IF_EXISTS, false, vlanTag, privateInterfaceIp)) { + return true; + } + + xml = SrxXml.PRIVATE_INTERFACE_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "private-interface-name", _privateInterface); + xml = replaceXmlValue(xml, "vlan-id", String.valueOf(vlanTag)); + + if (!sendRequestAndCheckResponse(command, xml, "name", String.valueOf(vlanTag))) { + throw new ExecutionException("Failed to delete private interface for guest VLAN tag " + vlanTag); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + } + + } + + private Long getVlanTagFromInterfaceName(String interfaceName) throws ExecutionException { + Long vlanTag = null; + + if (interfaceName.contains(".")) { + try { + String unitNum = interfaceName.split("\\.")[1]; + if (!unitNum.equals("0")) { + vlanTag = Long.parseLong(unitNum); + } + } catch (Exception e) { + s_logger.error(e); + throw new ExecutionException("Unable to parse VLAN tag from interface name: " + interfaceName); + } + } + + return vlanTag; + } + + /* + * Proxy ARP + */ + + private boolean manageProxyArp(SrxCommand command, Long publicVlanTag, String publicIp) throws ExecutionException { + String publicInterface = genPublicInterface(publicVlanTag); + String xml; + + switch (command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.PROXY_ARP_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "public-interface-name", publicInterface); + xml = replaceXmlValue(xml, "public-ip-address", publicIp); + return sendRequestAndCheckResponse(command, xml, "name", publicIp + "/32"); + + case CHECK_IF_IN_USE: + // Check if any static or destination NAT rules use this proxy ARP entry + List staticAndDestNatRules = getAllStaticAndDestNatRules(); + + for (String[] rule : staticAndDestNatRules) { + String rulePublicIp = rule[0]; + if (publicIp.equals(rulePublicIp)) { + return true; + } + } + + return false; + + case ADD: + if (manageProxyArp(SrxCommand.CHECK_IF_EXISTS, publicVlanTag, publicIp)) { + return true; + } + + xml = SrxXml.PROXY_ARP_ADD.getXml(); + xml = replaceXmlValue(xml, "public-interface-name", publicInterface); + xml = replaceXmlValue(xml, "public-ip-address", publicIp); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add proxy ARP entry for public IP " + publicIp); + } else { + return true; + } + + case DELETE: + if (!manageProxyArp(SrxCommand.CHECK_IF_EXISTS, publicVlanTag, publicIp)) { + return true; + } + + if (manageProxyArp(SrxCommand.CHECK_IF_IN_USE, publicVlanTag, publicIp)) { + return true; + } + + xml = SrxXml.PROXY_ARP_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "public-interface-name", publicInterface); + xml = replaceXmlValue(xml, "public-ip-address", publicIp); + + if (!sendRequestAndCheckResponse(command, xml, "name", publicIp)) { + throw new ExecutionException("Failed to delete proxy ARP entry for public IP " + publicIp); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + + } + + } + + private Map getPublicVlanTagsForPublicIps(List publicIps) throws ExecutionException { + Map publicVlanTags = new HashMap(); + + List interfaceNames = new ArrayList(); + + String xmlRequest = SrxXml.PROXY_ARP_GETALL.getXml(); + xmlRequest = replaceXmlValue(xmlRequest, "interface-name", ""); + String xmlResponse = sendRequest(xmlRequest); + + Document doc = getDocument(xmlResponse); + NodeList interfaces = doc.getElementsByTagName("interface"); + for (int i = 0; i < interfaces.getLength(); i++) { + String interfaceName = null; + NodeList interfaceEntries = interfaces.item(i).getChildNodes(); + for (int j = 0; j < interfaceEntries.getLength(); j++) { + Node interfaceEntry = interfaceEntries.item(j); + if (interfaceEntry.getNodeName().equals("name")) { + interfaceName = interfaceEntry.getFirstChild().getNodeValue(); + break; + } + } + + if (interfaceName != null) { + interfaceNames.add(interfaceName); + } + } + + if (interfaceNames.size() == 1) { + populatePublicVlanTagsMap(xmlResponse, interfaceNames.get(0), publicIps, publicVlanTags); + } else if (interfaceNames.size() > 1) { + for (String interfaceName : interfaceNames) { + xmlRequest = SrxXml.PROXY_ARP_GETALL.getXml(); + xmlRequest = replaceXmlValue(xmlRequest, "interface-name", interfaceName); + xmlResponse = sendRequest(xmlRequest); + populatePublicVlanTagsMap(xmlResponse, interfaceName, publicIps, publicVlanTags); + } + } + + return publicVlanTags; + } + + private void populatePublicVlanTagsMap(String xmlResponse, String interfaceName, List publicIps, Map publicVlanTags) throws ExecutionException { + Long publicVlanTag = getVlanTagFromInterfaceName(interfaceName); + if (publicVlanTag != null) { + for (String publicIp : publicIps) { + if (xmlResponse.contains(publicIp)) { + publicVlanTags.put(publicIp, publicVlanTag); + } + } + } + } + + private Map getPublicVlanTagsForNatRules(List natRules) throws ExecutionException { + List publicIps = new ArrayList(); + addPublicIpsToList(natRules, publicIps); + return getPublicVlanTagsForPublicIps(publicIps); + } + + private void addPublicIpsToList(List natRules, List publicIps) { + for (String[] natRule : natRules) { + if (!publicIps.contains(natRule[0])) { + publicIps.add(natRule[0]); + } + } + } + + private String genPublicInterface(Long vlanTag) { + String publicInterface = _publicInterface; + + if (!publicInterface.contains(".")) { + if (vlanTag == null) { + publicInterface += ".0"; + } else { + publicInterface += "." + vlanTag; + } + } + + return publicInterface; + } + + /* + * Zone interfaces + */ + + private String genZoneInterfaceName(long vlanTag) { + return _privateInterface + "." + String.valueOf(vlanTag); + } + + private boolean manageZoneInterface(SrxCommand command, long vlanTag) throws ExecutionException { + String zoneInterfaceName = genZoneInterfaceName(vlanTag); + String xml; + + switch (command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.ZONE_INTERFACE_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "private-zone-name", _privateZone); + xml = replaceXmlValue(xml, "zone-interface-name", zoneInterfaceName); + return sendRequestAndCheckResponse(command, xml, "name", zoneInterfaceName); + + case ADD: + if (manageZoneInterface(SrxCommand.CHECK_IF_EXISTS, vlanTag)) { + return true; + } + + xml = SrxXml.ZONE_INTERFACE_ADD.getXml(); + xml = replaceXmlValue(xml, "private-zone-name", _privateZone); + xml = replaceXmlValue(xml, "zone-interface-name", zoneInterfaceName); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add interface " + zoneInterfaceName + " to zone " + _privateZone); + } else { + return true; + } + + case DELETE: + if (!manageZoneInterface(SrxCommand.CHECK_IF_EXISTS, vlanTag)) { + return true; + } + + xml = SrxXml.ZONE_INTERFACE_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "private-zone-name", _privateZone); + xml = replaceXmlValue(xml, "zone-interface-name", zoneInterfaceName); + + if (!sendRequestAndCheckResponse(command, xml, "name", zoneInterfaceName)) { + throw new ExecutionException("Failed to delete interface " + zoneInterfaceName + " from zone " + _privateZone); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + /* + * Static NAT rules + */ + + private String genStaticNatRuleName(String publicIp, String privateIp) { + return genObjectName(genIpIdentifier(publicIp), genIpIdentifier(privateIp)); + } + + private boolean manageStaticNatRule(SrxCommand command, String publicIp, String privateIp) throws ExecutionException { + String ruleName = genStaticNatRuleName(publicIp, privateIp); + String xml; + + switch (command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.STATIC_NAT_RULE_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "rule-set", _publicZone); + xml = replaceXmlValue(xml, "from-zone", _publicZone); + xml = replaceXmlValue(xml, "rule-name", ruleName); + return sendRequestAndCheckResponse(command, xml, "name", ruleName); + + case ADD: + if (manageStaticNatRule(SrxCommand.CHECK_IF_EXISTS, publicIp, privateIp)) { + return true; + } + + xml = SrxXml.STATIC_NAT_RULE_ADD.getXml(); + xml = replaceXmlValue(xml, "rule-set", _publicZone); + xml = replaceXmlValue(xml, "from-zone", _publicZone); + xml = replaceXmlValue(xml, "rule-name", ruleName); + xml = replaceXmlValue(xml, "original-ip", publicIp); + xml = replaceXmlValue(xml, "translated-ip", privateIp); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add static NAT rule from public IP " + publicIp + " to private IP " + privateIp); + } else { + return true; + } + + case DELETE: + if (!manageStaticNatRule(SrxCommand.CHECK_IF_EXISTS, publicIp, privateIp)) { + return true; + } + + xml = SrxXml.STATIC_NAT_RULE_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "rule-set", _publicZone); + xml = replaceXmlValue(xml, "from-zone", _publicZone); + xml = replaceXmlValue(xml, "rule-name", ruleName); + + if (!sendRequestAndCheckResponse(command, xml, "name", ruleName)) { + throw new ExecutionException("Failed to delete static NAT rule from public IP " + publicIp + " to private IP " + privateIp); + } else { + return true; + } + + default: + throw new ExecutionException("Unrecognized command."); + + } + } + + private List getStaticNatRules(RuleMatchCondition condition, String privateGateway, Long privateCidrSize) throws ExecutionException { + List staticNatRules = new ArrayList(); + + String xmlRequest = SrxXml.STATIC_NAT_RULE_GETALL.getXml(); + String xmlResponse = sendRequest(xmlRequest); + Document doc = getDocument(xmlResponse); + NodeList rules = doc.getElementsByTagName("rule"); + for (int i = 0; i < rules.getLength(); i++) { + NodeList ruleEntries = rules.item(i).getChildNodes(); + for (int j = 0; j < ruleEntries.getLength(); j++) { + Node ruleEntry = ruleEntries.item(j); + if (ruleEntry.getNodeName().equals("name")) { + String name = ruleEntry.getFirstChild().getNodeValue(); + String[] nameContents = name.split("-"); + + if (nameContents.length != 8) { + continue; + } + + String rulePublicIp = nameContents[0] + "." + nameContents[1] + "." + nameContents[2] + "." + nameContents[3]; + String rulePrivateIp = nameContents[4] + "." + nameContents[5] + "." + nameContents[6] + "." + nameContents[7]; + + boolean addToList = false; + if (condition.equals(RuleMatchCondition.ALL)) { + addToList = true; + } else if (condition.equals(RuleMatchCondition.PRIVATE_SUBNET)) { + assert (privateGateway != null && privateCidrSize != null); + addToList = NetUtils.sameSubnetCIDR(rulePrivateIp, privateGateway, privateCidrSize); + } else { + s_logger.error("Invalid rule match condition."); + assert false; + } + + if (addToList) { + staticNatRules.add(new String[]{rulePublicIp, rulePrivateIp}); + } + } + } + } + + return staticNatRules; + } + + /* + * Destination NAT pools + */ + + private String genDestinationNatPoolName(String privateIp, long destPort) { + return genObjectName(genIpIdentifier(privateIp), String.valueOf(destPort)); + } + + private boolean manageDestinationNatPool(SrxCommand command, String privateIp, long destPort) throws ExecutionException { + String poolName = genDestinationNatPoolName(privateIp, destPort); + String xml; + + switch (command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.DEST_NAT_POOL_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "pool-name", poolName); + return sendRequestAndCheckResponse(command, xml, "name", poolName); + + case CHECK_IF_IN_USE: + // Check if any destination nat rules refer to this pool + xml = SrxXml.DEST_NAT_RULE_GETALL.getXml(); + xml = replaceXmlValue(xml, "rule-set", _publicZone); + return sendRequestAndCheckResponse(command, xml, "pool-name", poolName); + + case ADD: + if (manageDestinationNatPool(SrxCommand.CHECK_IF_EXISTS, privateIp, destPort)) { + return true; + } + + xml = SrxXml.DEST_NAT_POOL_ADD.getXml(); + xml = replaceXmlValue(xml, "pool-name", poolName); + xml = replaceXmlValue(xml, "private-address", privateIp + "/32"); + xml = replaceXmlValue(xml, "dest-port", String.valueOf(destPort)); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add destination NAT pool for private IP " + privateIp + " and private port " + destPort); + } else { + return true; + } + + case DELETE: + if (!manageDestinationNatPool(SrxCommand.CHECK_IF_EXISTS, privateIp, destPort)) { + return true; + } + + if (manageDestinationNatPool(SrxCommand.CHECK_IF_IN_USE, privateIp, destPort)) { + return true; + } + + xml = SrxXml.DEST_NAT_POOL_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "pool-name", poolName); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to delete destination NAT pool for private IP " + privateIp + " and private port " + destPort); + } else { + return true; + } + + default: + throw new ExecutionException("Unrecognized command."); + } + } + + /* + * Destination NAT rules + */ + + private String genDestinationNatRuleName(String publicIp, String privateIp, long srcPort, long destPort) { + return "destnatrule-" + String.valueOf(genObjectName(publicIp, privateIp, String.valueOf(srcPort), String.valueOf(destPort)).hashCode()).replaceAll("[^a-zA-Z0-9]", ""); + } + + private boolean manageDestinationNatRule(SrxCommand command, String publicIp, String privateIp, long srcPort, long destPort) throws ExecutionException { + String ruleName = genDestinationNatRuleName(publicIp, privateIp, srcPort, destPort); + String poolName = genDestinationNatPoolName(privateIp, destPort); + String xml; + + switch (command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.DEST_NAT_RULE_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "rule-set", _publicZone); + xml = replaceXmlValue(xml, "from-zone", _publicZone); + xml = replaceXmlValue(xml, "rule-name", ruleName); + return sendRequestAndCheckResponse(command, xml, "name", ruleName); + + case ADD: + if (manageDestinationNatRule(SrxCommand.CHECK_IF_EXISTS, publicIp, privateIp, srcPort, destPort)) { + return true; + } + + if (!manageDestinationNatPool(SrxCommand.CHECK_IF_EXISTS, privateIp, destPort)) { + throw new ExecutionException("The destination NAT pool corresponding to private IP: " + privateIp + " and destination port: " + destPort + " does not exist."); + } + + xml = SrxXml.DEST_NAT_RULE_ADD.getXml(); + xml = replaceXmlValue(xml, "rule-set", _publicZone); + xml = replaceXmlValue(xml, "from-zone", _publicZone); + xml = replaceXmlValue(xml, "rule-name", ruleName); + xml = replaceXmlValue(xml, "public-address", publicIp); + xml = replaceXmlValue(xml, "src-port", String.valueOf(srcPort)); + xml = replaceXmlValue(xml, "pool-name", poolName); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add destination NAT rule from public IP " + publicIp + ", public port " + srcPort + ", private IP " + privateIp + ", and private port " + destPort); + } else { + return true; + } + + case DELETE: + if (!manageDestinationNatRule(SrxCommand.CHECK_IF_EXISTS, publicIp, privateIp, srcPort, destPort)) { + return true; + } + + xml = SrxXml.DEST_NAT_RULE_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "rule-set", _publicZone); + xml = replaceXmlValue(xml, "from-zone", _publicZone); + xml = replaceXmlValue(xml, "rule-name", ruleName); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to delete destination NAT rule from public IP " + publicIp + ", public port " + srcPort + ", private IP " + privateIp + ", and private port " + destPort); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + } + + } + + private List getDestNatRules(RuleMatchCondition condition, String publicIp, String privateIp, String privateGateway, Long privateCidrSize) throws ExecutionException { + List destNatRules = new ArrayList(); + + String xmlRequest = SrxXml.DEST_NAT_RULE_GETALL.getXml(); + xmlRequest = replaceXmlValue(xmlRequest, "rule-set", _publicZone); + String xmlResponse = sendRequest(xmlRequest); + Document doc = getDocument(xmlResponse); + NodeList rules = doc.getElementsByTagName("rule"); + for (int ruleIndex = 0; ruleIndex < rules.getLength(); ruleIndex++) { + String rulePublicIp = null; + String rulePrivateIp = null; + String ruleSrcPort = null; + String ruleDestPort = null; + NodeList ruleEntries = rules.item(ruleIndex).getChildNodes(); + for (int ruleEntryIndex = 0; ruleEntryIndex < ruleEntries.getLength(); ruleEntryIndex++) { + Node ruleEntry = ruleEntries.item(ruleEntryIndex); + if (ruleEntry.getNodeName().equals("dest-nat-rule-match")) { + NodeList ruleMatchEntries = ruleEntry.getChildNodes(); + for (int ruleMatchIndex = 0; ruleMatchIndex < ruleMatchEntries.getLength(); ruleMatchIndex++) { + Node ruleMatchEntry = ruleMatchEntries.item(ruleMatchIndex); + if (ruleMatchEntry.getNodeName().equals("destination-address")) { + NodeList destAddressEntries = ruleMatchEntry.getChildNodes(); + for (int destAddressIndex = 0; destAddressIndex < destAddressEntries.getLength(); destAddressIndex++) { + Node destAddressEntry = destAddressEntries.item(destAddressIndex); + if (destAddressEntry.getNodeName().equals("dst-addr")) { + rulePublicIp = destAddressEntry.getFirstChild().getNodeValue().split("/")[0]; + } + } + } else if (ruleMatchEntry.getNodeName().equals("destination-port")) { + NodeList destPortEntries = ruleMatchEntry.getChildNodes(); + for (int destPortIndex = 0; destPortIndex < destPortEntries.getLength(); destPortIndex++) { + Node destPortEntry = destPortEntries.item(destPortIndex); + if (destPortEntry.getNodeName().equals("dst-port")) { + ruleSrcPort = destPortEntry.getFirstChild().getNodeValue(); + } + } + } + } + } else if (ruleEntry.getNodeName().equals("then")) { + NodeList ruleThenEntries = ruleEntry.getChildNodes(); + for (int ruleThenIndex = 0; ruleThenIndex < ruleThenEntries.getLength(); ruleThenIndex++) { + Node ruleThenEntry = ruleThenEntries.item(ruleThenIndex); + if (ruleThenEntry.getNodeName().equals("destination-nat")) { + NodeList destNatEntries = ruleThenEntry.getChildNodes(); + for (int destNatIndex = 0; destNatIndex < destNatEntries.getLength(); destNatIndex++) { + Node destNatEntry = destNatEntries.item(destNatIndex); + if (destNatEntry.getNodeName().equals("pool")) { + NodeList poolEntries = destNatEntry.getChildNodes(); + for (int poolIndex = 0; poolIndex < poolEntries.getLength(); poolIndex++) { + Node poolEntry = poolEntries.item(poolIndex); + if (poolEntry.getNodeName().equals("pool-name")) { + String[] poolName = poolEntry.getFirstChild().getNodeValue().split("-"); + if (poolName.length == 5) { + rulePrivateIp = poolName[0] + "." + poolName[1] + "." + poolName[2] + "." + poolName[3]; + ruleDestPort = poolName[4]; + } + } + } + } + } + } + } + } + } + + if (rulePublicIp == null || rulePrivateIp == null || ruleSrcPort == null || ruleDestPort == null) { + continue; + } + + boolean addToList = false; + if (condition.equals(RuleMatchCondition.ALL)) { + addToList = true; + } else if (condition.equals(RuleMatchCondition.PUBLIC_PRIVATE_IPS)) { + assert (publicIp != null && privateIp != null); + addToList = publicIp.equals(rulePublicIp) && privateIp.equals(rulePrivateIp); + } else if (condition.equals(RuleMatchCondition.PRIVATE_SUBNET)) { + assert (privateGateway != null && privateCidrSize != null); + addToList = NetUtils.sameSubnetCIDR(rulePrivateIp, privateGateway, privateCidrSize); + } + + if (addToList) { + destNatRules.add(new String[]{rulePublicIp, rulePrivateIp, ruleSrcPort, ruleDestPort}); + } + } + + return destNatRules; + } + + /* + * Source NAT pools + */ + + private String genSourceNatPoolName(String publicIp) { + return genObjectName(genIpIdentifier(publicIp)); + } + + private boolean manageSourceNatPool(SrxCommand command, String publicIp) throws ExecutionException { + String poolName = genSourceNatPoolName(publicIp); + String xml; + + switch (command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.SRC_NAT_POOL_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "pool-name", poolName); + return sendRequestAndCheckResponse(command, xml, "name", poolName); + + case CHECK_IF_IN_USE: + // Check if any source nat rules refer to this pool + xml = SrxXml.SRC_NAT_RULE_GETALL.getXml(); + return sendRequestAndCheckResponse(command, xml, "pool-name", poolName); + + case ADD: + if (manageSourceNatPool(SrxCommand.CHECK_IF_EXISTS, publicIp)) { + return true; + } + + xml = SrxXml.SRC_NAT_POOL_ADD.getXml(); + xml = replaceXmlValue(xml, "pool-name", poolName); + xml = replaceXmlValue(xml, "address", publicIp + "/32"); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add source NAT pool for public IP " + publicIp); + } else { + return true; + } + + case DELETE: + if (!manageSourceNatPool(SrxCommand.CHECK_IF_EXISTS, publicIp)) { + return true; + } + + if (manageSourceNatPool(SrxCommand.CHECK_IF_IN_USE, publicIp)) { + return true; + } + + xml = SrxXml.SRC_NAT_POOL_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "pool-name", poolName); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to delete source NAT pool for public IP " + publicIp); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + /* + * Source NAT rules + */ + + private String genSourceNatRuleName(String publicIp, String privateSubnet) { + return genObjectName(genIpIdentifier(publicIp), genIpIdentifier(privateSubnet)); + } + + private boolean manageSourceNatRule(SrxCommand command, String publicIp, String privateSubnet) throws ExecutionException { + String ruleName = genSourceNatRuleName(publicIp, privateSubnet); + String poolName = genSourceNatPoolName(publicIp); + String xml; + + switch (command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.SRC_NAT_RULE_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "rule-set", _privateZone); + xml = replaceXmlValue(xml, "from-zone", _privateZone); + xml = replaceXmlValue(xml, "rule-name", ruleName); + return sendRequestAndCheckResponse(command, xml, "name", ruleName); + + case ADD: + if (manageSourceNatRule(SrxCommand.CHECK_IF_EXISTS, publicIp, privateSubnet)) { + return true; + } + + if (!manageSourceNatPool(SrxCommand.CHECK_IF_EXISTS, publicIp)) { + throw new ExecutionException("The source NAT pool corresponding to " + publicIp + " does not exist."); + } + + xml = SrxXml.SRC_NAT_RULE_ADD.getXml(); + xml = replaceXmlValue(xml, "rule-set", _privateZone); + xml = replaceXmlValue(xml, "from-zone", _privateZone); + xml = replaceXmlValue(xml, "to-zone", _publicZone); + xml = replaceXmlValue(xml, "rule-name", ruleName); + xml = replaceXmlValue(xml, "private-subnet", privateSubnet); + xml = replaceXmlValue(xml, "pool-name", poolName); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add source NAT rule for public IP " + publicIp + " and private subnet " + privateSubnet); + } else { + return true; + } + + case DELETE: + if (!manageSourceNatRule(SrxCommand.CHECK_IF_EXISTS, publicIp, privateSubnet)) { + return true; + } + + xml = SrxXml.SRC_NAT_RULE_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "rule-set", _privateZone); + xml = replaceXmlValue(xml, "from-zone", _privateZone); + xml = replaceXmlValue(xml, "rule-name", ruleName); + + if (!sendRequestAndCheckResponse(command, xml, "name", ruleName)) { + throw new ExecutionException("Failed to delete source NAT rule for public IP " + publicIp + " and private subnet " + privateSubnet); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + } + + } + + /* + * Address book entries + */ + + private String genAddressBookEntryName(String ip) { + if (ip == null) { + return "any"; + } else { + return genIpIdentifier(ip); + } + } + + private boolean manageAddressBookEntry(SrxCommand command, String zone, String ip, String entryName) throws ExecutionException { + if (!zone.equals(_publicZone) && !zone.equals(_privateZone)) { + throw new ExecutionException("Invalid zone."); + } + + if (entryName == null) { + entryName = genAddressBookEntryName(ip); + } + + String xml; + + switch (command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.ADDRESS_BOOK_ENTRY_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "zone", zone); + xml = replaceXmlValue(xml, "entry-name", entryName); + return sendRequestAndCheckResponse(command, xml, "name", entryName); + + case CHECK_IF_IN_USE: + // Check if any security policies refer to this address book entry + xml = SrxXml.SECURITY_POLICY_GETALL.getXml(); + String fromZone = zone.equals(_publicZone) ? _privateZone : _publicZone; + xml = replaceXmlValue(xml, "from-zone", fromZone); + xml = replaceXmlValue(xml, "to-zone", zone); + return sendRequestAndCheckResponse(command, xml, "destination-address", entryName); + + case ADD: + if (manageAddressBookEntry(SrxCommand.CHECK_IF_EXISTS, zone, ip, entryName)) { + return true; + } + + xml = SrxXml.ADDRESS_BOOK_ENTRY_ADD.getXml(); + xml = replaceXmlValue(xml, "zone", zone); + xml = replaceXmlValue(xml, "entry-name", entryName); + xml = replaceXmlValue(xml, "ip", ip); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add address book entry for IP " + ip + " in zone " + zone); + } else { + return true; + } + + case DELETE: + if (!manageAddressBookEntry(SrxCommand.CHECK_IF_EXISTS, zone, ip, entryName)) { + return true; + } + + if (manageAddressBookEntry(SrxCommand.CHECK_IF_IN_USE, zone, ip, entryName)) { + return true; + } + + xml = SrxXml.ADDRESS_BOOK_ENTRY_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "zone", zone); + xml = replaceXmlValue(xml, "entry-name", entryName); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to delete address book entry for IP " + ip + " in zone " + zone); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + + } + + } + + /* + * Applications + */ + + private String genApplicationName(Protocol protocol, int startPort, int endPort) { + if (protocol.equals(Protocol.any)) { + return Protocol.any.toString(); + } else { + return genObjectName(protocol.toString(), String.valueOf(startPort), String.valueOf(endPort)); + } + } + + private Object[] parseApplicationName(String applicationName) throws ExecutionException { + String errorMsg = "Invalid application: " + applicationName; + String[] applicationComponents = applicationName.split("-"); + + Protocol protocol; + Integer startPort; + Integer endPort; + try { + protocol = getProtocol(applicationComponents[0]); + startPort = Integer.parseInt(applicationComponents[1]); + endPort = Integer.parseInt(applicationComponents[2]); + } catch (Exception e) { + throw new ExecutionException(errorMsg); + } + + return new Object[]{protocol, startPort, endPort}; + } + + private boolean manageApplication(SrxCommand command, Protocol protocol, int startPort, int endPort) throws ExecutionException { + if (protocol.equals(Protocol.any)) { + return true; + } + + String applicationName = genApplicationName(protocol, startPort, endPort); + String xml; + + switch (command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.APPLICATION_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "name", applicationName); + return sendRequestAndCheckResponse(command, xml, "name", applicationName); + + case ADD: + if (manageApplication(SrxCommand.CHECK_IF_EXISTS, protocol, startPort, endPort)) { + return true; + } + + xml = SrxXml.APPLICATION_ADD.getXml(); + xml = replaceXmlValue(xml, "name", applicationName); + xml = replaceXmlValue(xml, "protocol", protocol.toString()); + + String destPort; + if (startPort == endPort) { + destPort = String.valueOf(startPort); + } else { + destPort = startPort + "-" + endPort; + } + + xml = replaceXmlValue(xml, "dest-port", destPort); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add application " + applicationName); + } else { + return true; + } + + case DELETE: + if (!manageApplication(SrxCommand.CHECK_IF_EXISTS, protocol, startPort, endPort)) { + return true; + } + + xml = SrxXml.APPLICATION_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "name", applicationName); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to delete application " + applicationName); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + } + + } + + private List getUnusedApplications(List applications) throws ExecutionException { + List unusedApplications = new ArrayList(); + + // Check if any of the applications are unused by existing security policies + String xml = SrxXml.SECURITY_POLICY_GETALL.getXml(); + xml = replaceXmlValue(xml, "from-zone", _publicZone); + xml = replaceXmlValue(xml, "to-zone", _privateZone); + String allPolicies = sendRequest(xml); + + for (String application : applications) { + if (!application.equals(Protocol.any.toString()) && !allPolicies.contains(application)) { + unusedApplications.add(application); + } + } + + return unusedApplications; + } + + private List getApplicationsForSecurityPolicy(SecurityPolicyType type, String privateIp) throws ExecutionException { + String fromZone = _publicZone; + String toZone = _privateZone; + String policyName = genSecurityPolicyName(type, null, null, fromZone, toZone, privateIp); + String xml = SrxXml.SECURITY_POLICY_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "from-zone", fromZone); + xml = replaceXmlValue(xml, "to-zone", toZone); + xml = replaceXmlValue(xml, "policy-name", policyName); + String policy = sendRequest(xml); + + Document doc = getDocument(policy); + + List policyApplications = new ArrayList(); + NodeList applicationNodes = doc.getElementsByTagName("application"); + + for (int i = 0; i < applicationNodes.getLength(); i++) { + Node applicationNode = applicationNodes.item(i); + policyApplications.add(applicationNode.getFirstChild().getNodeValue()); + } + + return policyApplications; + } + + private List extractApplications(List rules) throws ExecutionException { + List applications = new ArrayList(); + + for (FirewallRuleTO rule : rules) { + Object[] application = new Object[3]; + application[0] = getProtocol(rule.getProtocol()); + application[1] = rule.getSrcPortRange()[0]; + application[2] = rule.getSrcPortRange()[1]; + applications.add(application); + } + + return applications; + } + + /* + * Security policies + */ + + private String genSecurityPolicyName(SecurityPolicyType type, Long accountId, String username, String fromZone, String toZone, String translatedIp) { + if (type.equals(SecurityPolicyType.VPN)) { + return genObjectName(_vpnObjectPrefix, String.valueOf(accountId), username); + } else { + return genObjectName(type.getIdentifier(), fromZone, toZone, genIpIdentifier(translatedIp)); + } + } + + private boolean manageSecurityPolicy(SecurityPolicyType type, SrxCommand command, Long accountId, String username, String privateIp, List applicationNames, String ipsecVpnName) throws ExecutionException { + String fromZone = _publicZone; + String toZone = _privateZone; + + String securityPolicyName; + String addressBookEntryName; + + if (type.equals(SecurityPolicyType.VPN) && ipsecVpnName != null) { + securityPolicyName = ipsecVpnName; + addressBookEntryName = ipsecVpnName; + } else { + securityPolicyName = genSecurityPolicyName(type, accountId, username, fromZone, toZone, privateIp); + addressBookEntryName = genAddressBookEntryName(privateIp); + } + + String xml; + + switch (command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.SECURITY_POLICY_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "from-zone", fromZone); + xml = replaceXmlValue(xml, "to-zone", toZone); + xml = replaceXmlValue(xml, "policy-name", securityPolicyName); + + return sendRequestAndCheckResponse(command, xml, "name", securityPolicyName); + + case CHECK_IF_IN_USE: + List rulesToCheck = null; + if (type.equals(SecurityPolicyType.STATIC_NAT)) { + // Check if any static NAT rules rely on this security policy + rulesToCheck = getStaticNatRules(RuleMatchCondition.ALL, null, null); + } else if (type.equals(SecurityPolicyType.DESTINATION_NAT)) { + // Check if any destination NAT rules rely on this security policy + rulesToCheck = getDestNatRules(RuleMatchCondition.ALL, null, null, null, null); + } else { + return false; + } + + for (String[] rule : rulesToCheck) { + String rulePrivateIp = rule[1]; + if (privateIp.equals(rulePrivateIp)) { + return true; + } + } + + return false; + + case ADD: + if (!manageAddressBookEntry(SrxCommand.CHECK_IF_EXISTS, toZone, privateIp, ipsecVpnName)) { + throw new ExecutionException("No address book entry for policy: " + securityPolicyName); + } + + xml = SrxXml.SECURITY_POLICY_ADD.getXml(); + xml = replaceXmlValue(xml, "from-zone", fromZone); + xml = replaceXmlValue(xml, "to-zone", toZone); + xml = replaceXmlValue(xml, "policy-name", securityPolicyName); + xml = replaceXmlValue(xml, "src-address", "any"); + xml = replaceXmlValue(xml, "dest-address", addressBookEntryName); + + if (type.equals(SecurityPolicyType.VPN) && ipsecVpnName != null) { + xml = replaceXmlValue(xml, "tunnel", "" + ipsecVpnName + ""); + } else { + xml = replaceXmlValue(xml, "tunnel", ""); + } + + String applications; + if (applicationNames == null) { + applications = "any"; + } else { + applications = ""; + for (String applicationName : applicationNames) { + applications += "" + applicationName + ""; + } + } + + xml = replaceXmlValue(xml, "applications", applications); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add security policy for privateIp " + privateIp + " and applications " + applicationNames); + } else { + return true; + } + + case DELETE: + if (!manageSecurityPolicy(type, SrxCommand.CHECK_IF_EXISTS, null, null, privateIp, applicationNames, ipsecVpnName)) { + return true; + } + + if (manageSecurityPolicy(type, SrxCommand.CHECK_IF_IN_USE, null, null, privateIp, applicationNames, ipsecVpnName)) { + return true; + } + + xml = SrxXml.SECURITY_POLICY_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "from-zone", fromZone); + xml = replaceXmlValue(xml, "to-zone", toZone); + xml = replaceXmlValue(xml, "policy-name", securityPolicyName); + + boolean success = sendRequestAndCheckResponse(command, xml); + + if (success) { + xml = SrxXml.SECURITY_POLICY_GETALL.getXml(); + xml = replaceXmlValue(xml, "from-zone", fromZone); + xml = replaceXmlValue(xml, "to-zone", toZone); + String getAllResponseXml = sendRequest(xml); + + if (getAllResponseXml == null) { + throw new ExecutionException("Deleted security policy, but failed to delete security policy group."); + } + + if (!getAllResponseXml.contains(fromZone) || !getAllResponseXml.contains(toZone)) { + return true; + } else if (!getAllResponseXml.contains("match") && !getAllResponseXml.contains("then")) { + xml = SrxXml.SECURITY_POLICY_GROUP.getXml(); + xml = replaceXmlValue(xml, "from-zone", fromZone); + xml = replaceXmlValue(xml, "to-zone", toZone); + xml = setDelete(xml, true); + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Deleted security policy, but failed to delete security policy group."); + } else { + return true; + } + } else { + return true; + } + } else { + throw new ExecutionException("Failed to delete security policy for privateIp " + privateIp + " and applications " + applicationNames); + } + + default: + s_logger.debug("Unrecognized command."); + return false; + + } + } + + private boolean addSecurityPolicyAndApplications(SecurityPolicyType type, String privateIp, List applications) throws ExecutionException { + // Add all necessary applications + List applicationNames = new ArrayList(); + for (Object[] application : applications) { + Protocol protocol = (Protocol) application[0]; + int startPort = application[1] != null ? ((Integer) application[1]) : -1; + int endPort = application[2] != null ? ((Integer) application[2]) : -1; + + String applicationName = genApplicationName(protocol, startPort, endPort); + if (!applicationNames.contains(applicationName)) { + applicationNames.add(applicationName); + } + + manageApplication(SrxCommand.ADD, protocol, startPort, endPort); + } + + // Add a new security policy + manageSecurityPolicy(type, SrxCommand.ADD, null, null, privateIp, applicationNames, null); + + return true; + } + + private boolean removeSecurityPolicyAndApplications(SecurityPolicyType type, String privateIp) throws ExecutionException { + if (!manageSecurityPolicy(type, SrxCommand.CHECK_IF_EXISTS, null, null, privateIp, null, null)) { + return true; + } + + if (manageSecurityPolicy(type, SrxCommand.CHECK_IF_IN_USE, null, null, privateIp, null, null)) { + return true; + } + + // Get a list of applications for this security policy + List applications = getApplicationsForSecurityPolicy(type, privateIp); + + // Remove the security policy + manageSecurityPolicy(type, SrxCommand.DELETE, null, null, privateIp, null, null); + + // Remove any applications for the removed security policy that are no longer in use + List unusedApplications = getUnusedApplications(applications); + for (String application : unusedApplications) { + Object[] applicationComponents; + + try { + applicationComponents = parseApplicationName(application); + } catch (ExecutionException e) { + s_logger.error("Found an invalid application: " + application + ". Not attempting to clean up."); + continue; + } + + Protocol protocol = (Protocol) applicationComponents[0]; + Integer startPort = (Integer) applicationComponents[1]; + Integer endPort = (Integer) applicationComponents[2]; + manageApplication(SrxCommand.DELETE, protocol, startPort, endPort); + } + + return true; + } + + /* + * Filter terms + */ + + private String genIpFilterTermName(String ipAddress) { + return genIpIdentifier(ipAddress); + } + + private boolean manageUsageFilter(SrxCommand command, UsageFilter filter, String ip, Long guestVlanTag, String filterTermName) throws ExecutionException { + String filterName; + String filterDescription; + String xml; + + if (filter.equals(UsageFilter.IP_INPUT) || filter.equals(UsageFilter.IP_OUTPUT)) { + assert (ip != null && guestVlanTag == null); + filterName = filter.getName(); + filterDescription = filter.toString() + ", public IP = " + ip; + xml = SrxXml.PUBLIC_IP_FILTER_TERM_ADD.getXml(); + } else if (filter.equals(UsageFilter.VLAN_INPUT) || filter.equals(UsageFilter.VLAN_OUTPUT)) { + assert (ip == null && guestVlanTag != null); + filterName = filter.getName() + "-" + guestVlanTag; + filterDescription = filter.toString() + ", guest VLAN tag = " + guestVlanTag; + filterTermName = filterName; + xml = SrxXml.GUEST_VLAN_FILTER_TERM_ADD.getXml(); + } else { + return false; + } + + switch(command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.FILTER_TERM_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "filter-name", filterName); + xml = replaceXmlValue(xml, "term-name", filterTermName); + return sendRequestAndCheckResponse(command, xml, "name", filterTermName); + + case ADD: + if (manageUsageFilter(SrxCommand.CHECK_IF_EXISTS, filter, ip, guestVlanTag, filterTermName)) { + return true; + } + + xml = replaceXmlValue(xml, "filter-name", filterName); + xml = replaceXmlValue(xml, "term-name", filterTermName); + + if (filter.equals(UsageFilter.IP_INPUT) || filter.equals(UsageFilter.IP_OUTPUT)) { + xml = replaceXmlValue(xml, "ip-address", ip); + xml = replaceXmlValue(xml, "address-type", filter.getAddressType()); + } + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add usage filter: " + filterDescription); + } else { + return true; + } + + case DELETE: + if (!manageUsageFilter(SrxCommand.CHECK_IF_EXISTS, filter, ip, guestVlanTag, filterTermName)) { + return true; + } + + boolean deleteFilter = filter.equals(UsageFilter.VLAN_INPUT) || filter.equals(UsageFilter.VLAN_OUTPUT); + xml = deleteFilter ? SrxXml.FILTER_GETONE.getXml() : SrxXml.FILTER_TERM_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "filter-name", filterName); + xml = !deleteFilter ? replaceXmlValue(xml, "term-name", filterTermName) : xml; + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to delete usage filter: " + filterDescription); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + + } + } + + /* + * Usage + */ + + private ExternalNetworkResourceUsageAnswer getUsageAnswer(ExternalNetworkResourceUsageCommand cmd) throws ExecutionException { + try { + ExternalNetworkResourceUsageAnswer answer = new ExternalNetworkResourceUsageAnswer(cmd); + + String xml = SrxXml.FIREWALL_FILTER_BYTES_GETALL.getXml(); + String rawUsageData = sendRequest(xml); + Document doc = getDocument(rawUsageData); + + NodeList counters = doc.getElementsByTagName("counter"); + for (int i = 0; i < counters.getLength(); i++) { + Node n = counters.item(i); + if (n.getNodeName().equals("counter")) { + NodeList counterInfoList = n.getChildNodes(); + String counterName = null; + long byteCount = 0; + + for (int j = 0; j < counterInfoList.getLength(); j++) { + Node counterInfo = counterInfoList.item(j); + if (counterInfo.getNodeName().equals("counter-name")) { + counterName = counterInfo.getFirstChild().getNodeValue(); + } else if (counterInfo.getNodeName().equals("byte-count")) { + try { + byteCount = Long.parseLong(counterInfo.getFirstChild().getNodeValue()); + } catch (Exception e) { + s_logger.debug(e); + byteCount = 0; + } + } + } + + if (byteCount >= 0) { + updateUsageAnswer(answer, counterName, byteCount); + } + } + } + + return answer; + } catch (Exception e) { + throw new ExecutionException(e.getMessage()); + } + + } + + private void updateBytesMap(Map bytesMap, UsageFilter filter, String usageAnswerKey, long additionalBytes) { + long[] bytesSentAndReceived = bytesMap.get(usageAnswerKey); + if (bytesSentAndReceived == null) { + bytesSentAndReceived = new long[]{0,0}; + } + + int index = 0; + if (filter.equals(UsageFilter.VLAN_OUTPUT) || filter.equals(UsageFilter.IP_INPUT)) { + index = 1; + } + + bytesSentAndReceived[index] += additionalBytes; + bytesMap.put(usageAnswerKey, bytesSentAndReceived); + } + + private String getIpAddress(String counterName) { + String[] counterNameArray = counterName.split("-"); + + if (counterNameArray.length < 4) { + return null; + } else { + return counterNameArray[0] + "." + counterNameArray[1] + "." + counterNameArray[2] + "." + counterNameArray[3]; + } + } + + private String getGuestVlanTag(String counterName) { + String[] counterNameArray = counterName.split("-"); + + if (counterNameArray.length != 3) { + return null; + } else { + return counterNameArray[2]; + } + } + + private UsageFilter getUsageFilter(String counterName) { + for (UsageFilter filter : UsageFilter.values()) { + if (counterName.contains(filter.getCounterIdentifier())) { + return filter; + } + } + + return null; + } + + private String getUsageAnswerKey(UsageFilter filter, String counterName) { + if (filter.equals(UsageFilter.VLAN_INPUT) || filter.equals(UsageFilter.VLAN_OUTPUT)) { + return getGuestVlanTag(counterName); + } else if (filter.equals(UsageFilter.IP_INPUT) || filter.equals(UsageFilter.IP_OUTPUT)) { + return getIpAddress(counterName); + } else { + return null; + } + } + + private Map getBytesMap(ExternalNetworkResourceUsageAnswer answer, UsageFilter filter, String usageAnswerKey) { + if (filter.equals(UsageFilter.VLAN_INPUT) || filter.equals(UsageFilter.VLAN_OUTPUT)) { + return answer.guestVlanBytes; + } else if (filter.equals(UsageFilter.IP_INPUT) || filter.equals(UsageFilter.IP_OUTPUT)) { + return answer.ipBytes; + } else { + return null; + } + } + + private void updateUsageAnswer(ExternalNetworkResourceUsageAnswer answer, String counterName, long byteCount) { + if (counterName == null || byteCount <= 0) { + return; + } + + UsageFilter filter = getUsageFilter(counterName); + String usageAnswerKey = getUsageAnswerKey(filter, counterName); + Map bytesMap = getBytesMap(answer, filter, usageAnswerKey); + updateBytesMap(bytesMap, filter, usageAnswerKey, byteCount); + } + + /* + * XML API commands + */ + + private String sendRequest(String xmlRequest) throws ExecutionException { + if (!xmlRequest.contains("request-login")) { + s_logger.debug("Sending request: " + xmlRequest); + } else { + s_logger.debug("Sending login request"); + } + + boolean timedOut = false; + StringBuffer xmlResponseBuffer = new StringBuffer(""); + try { + _toSrx.write(xmlRequest); + _toSrx.flush(); + + String line = ""; + while ((line = _fromSrx.readLine()) != null) { + xmlResponseBuffer.append(line); + if (line.contains("")) { + break; + } + } + + } catch (SocketTimeoutException e) { + s_logger.debug(e); + timedOut = true; + } catch (IOException e) { + s_logger.debug(e); + return null; + } + + String xmlResponse = xmlResponseBuffer.toString(); + String errorMsg = null; + + if (timedOut) { + errorMsg = "Timed out on XML request: " + xmlRequest; + } else if (xmlResponse.isEmpty()) { + errorMsg = "Received an empty XML response."; + } else if (xmlResponse.contains("Unexpected XML tag type")) { + errorMsg = "Sent a command without being logged in."; + } else if (!xmlResponse.contains("")) { + errorMsg = "Didn't find the rpc-reply tag in the XML response."; + } + + if (errorMsg == null) { + return xmlResponse; + } else { + s_logger.error(errorMsg); + throw new ExecutionException(errorMsg); + } + } + + private boolean checkResponse(String xmlResponse, boolean errorKeyAndValue, String key, String value) { + if (!xmlResponse.contains("authentication-response")) { + s_logger.debug("Checking response: " + xmlResponse); + } else { + s_logger.debug("Checking login response"); + } + + String textToSearchFor = key; + if (value != null) { + textToSearchFor = "<" + key + ">" + value + ""; + } + + if ((errorKeyAndValue && !xmlResponse.contains(textToSearchFor)) || + (!errorKeyAndValue && xmlResponse.contains(textToSearchFor))) { + return true; + } + + + String responseMessage = extractXml(xmlResponse, "message"); + if (responseMessage != null) { + s_logger.error("Request failed due to: " + responseMessage); + } else { + if (errorKeyAndValue) { + s_logger.error("Found error (" + textToSearchFor + ") in response."); + } else { + s_logger.debug("Didn't find " + textToSearchFor + " in response."); + } + } + + return false; + } + + private boolean sendRequestAndCheckResponse(SrxCommand command, String xmlRequest, String... keyAndValue) throws ExecutionException { + boolean errorKeyAndValue = false; + String key; + String value; + + switch (command) { + + case LOGIN: + key = "status"; + value = "success"; + break; + + case OPEN_CONFIGURATION: + case CLOSE_CONFIGURATION: + errorKeyAndValue = true; + key = "error"; + value = null; + break; + + case COMMIT: + key = "commit-success"; + value = null; + break; + + case CHECK_IF_EXISTS: + case CHECK_IF_IN_USE: + assert (keyAndValue != null && keyAndValue.length == 2) : "If the SrxCommand is " + command + ", both a key and value must be specified."; + + key = keyAndValue[0]; + value = keyAndValue[1]; + break; + + default: + key = "load-success"; + value = null; + break; + + } + + String xmlResponse = sendRequest(xmlRequest); + return checkResponse(xmlResponse, errorKeyAndValue, key, value); + } + + /* + * XML utils + */ + + private String replaceXmlTag(String xml, String oldTag, String newTag) { + return xml.replaceAll(oldTag, newTag); + } + + private String replaceXmlValue(String xml, String marker, String value) { + marker = "\\s*%" + marker + "%\\s*"; + + if (value == null) { + value = ""; + } + + return xml.replaceAll(marker, value); + } + + private String extractXml(String xml, String marker) { + String startMarker = "<" + marker + ">"; + String endMarker = ""; + if (xml.contains(startMarker) && xml.contains(endMarker)) { + return xml.substring(xml.indexOf(startMarker) + startMarker.length(), xml.indexOf(endMarker)); + } else { + return null; + } + + } + + private String setDelete(String xml, boolean delete) { + if (delete) { + String deleteMarker = " delete=\"delete\""; + xml = replaceXmlTag(xml, "get-configuration", "load-configuration"); + xml = replaceXmlValue(xml, "delete", deleteMarker); + } else { + xml = replaceXmlTag(xml, "load-configuration", "get-configuration"); + xml = replaceXmlValue(xml, "delete", ""); + } + + return xml; + } + + /* + * Misc + */ + + private Long getVlanTag(String vlan) throws ExecutionException { + Long publicVlanTag = null; + if (!vlan.equals("untagged")) { + try { + publicVlanTag = Long.parseLong(vlan); + } catch (Exception e) { + throw new ExecutionException("Unable to parse VLAN tag: " + vlan); + } + } + + return publicVlanTag; + } + + private String genObjectName(String... args) { + String objectName = ""; + + for (int i = 0; i < args.length; i++) { + objectName += args[i]; + if (i != args.length -1) { + objectName += _objectNameWordSep; + } + } + + return objectName; + } + + + private String genIpIdentifier(String ip) { + return ip.replace('.', '-').replace('/', '-'); + } + + private Protocol getProtocol(String protocolName) throws ExecutionException { + protocolName = protocolName.toLowerCase(); + + try { + return Protocol.valueOf(protocolName); + } catch (Exception e) { + throw new ExecutionException("Invalid protocol: " + protocolName); + } + } + + private Document getDocument(String xml) throws ExecutionException { + StringReader srcNatRuleReader = new StringReader(xml); + InputSource srcNatRuleSource = new InputSource(srcNatRuleReader); + Document doc = null; + + try { + doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(srcNatRuleSource); + } catch (Exception e) { + s_logger.error(e); + throw new ExecutionException(e.getMessage()); + } + + if (doc == null) { + throw new ExecutionException("Failed to parse xml " + xml); + } else { + return doc; + } + } + +} \ No newline at end of file diff --git a/core/src/com/cloud/network/resource/TrafficSentinelResource.java b/core/src/com/cloud/network/resource/TrafficSentinelResource.java new file mode 100644 index 00000000000..23ce64b0def --- /dev/null +++ b/core/src/com/cloud/network/resource/TrafficSentinelResource.java @@ -0,0 +1,294 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.network.resource; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLEncoder; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.DirectNetworkUsageAnswer; +import com.cloud.agent.api.DirectNetworkUsageCommand; +import com.cloud.agent.api.MaintainAnswer; +import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.RecurringNetworkUsageAnswer; +import com.cloud.agent.api.RecurringNetworkUsageCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupTrafficMonitorCommand; +import com.cloud.host.Host; +import com.cloud.resource.ServerResource; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.exception.ExecutionException; + +public class TrafficSentinelResource implements ServerResource { + + private String _name; + private String _zoneId; + private String _ip; + private String _guid; + private String _url; + private static Integer _numRetries; + private static Integer _timeoutInSeconds; + + + private static final Logger s_logger = Logger.getLogger(TrafficSentinelResource.class); + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + try { + + _name = name; + + _zoneId = (String) params.get("zone"); + if (_zoneId == null) { + throw new ConfigurationException("Unable to find zone"); + } + + _ip = (String) params.get("ipaddress"); + if (_ip == null) { + throw new ConfigurationException("Unable to find IP"); + } + + _guid = (String)params.get("guid"); + if (_guid == null) { + throw new ConfigurationException("Unable to find the guid"); + } + + _url = (String)params.get("url"); + if (_url == null) { + throw new ConfigurationException("Unable to find url"); + } + + _numRetries = NumbersUtil.parseInt((String) params.get("numRetries"), 1); + + _timeoutInSeconds = NumbersUtil.parseInt((String) params.get("timeoutInSeconds"), 300); + + return true; + } catch (Exception e) { + throw new ConfigurationException(e.getMessage()); + } + + } + + @Override + public StartupCommand[] initialize() { + StartupTrafficMonitorCommand cmd = new StartupTrafficMonitorCommand(); + cmd.setName(_name); + cmd.setDataCenter(_zoneId); + cmd.setPod(""); + cmd.setPrivateIpAddress(_ip); + cmd.setStorageIpAddress(""); + cmd.setVersion(""); + cmd.setGuid(_guid); + return new StartupCommand[]{cmd}; + } + + @Override + public Host.Type getType() { + return Host.Type.TrafficMonitor; + } + + @Override + public String getName() { + return _name; + } + + @Override + public PingCommand getCurrentStatus(final long id) { + return new PingCommand(Host.Type.TrafficMonitor, id); + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public void disconnected() { + return; + } + + @Override + public IAgentControl getAgentControl() { + return null; + } + + @Override + public void setAgentControl(IAgentControl agentControl) { + return; + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof ReadyCommand) { + return execute((ReadyCommand) cmd); + } else if (cmd instanceof MaintainCommand) { + return execute((MaintainCommand) cmd); + } else if (cmd instanceof DirectNetworkUsageCommand) { + return execute((DirectNetworkUsageCommand) cmd); + } else if (cmd instanceof RecurringNetworkUsageCommand) { + return execute((RecurringNetworkUsageCommand) cmd); + } else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } + + private Answer execute(ReadyCommand cmd) { + return new ReadyAnswer(cmd); + } + + private synchronized RecurringNetworkUsageAnswer execute(RecurringNetworkUsageCommand cmd) { + return new RecurringNetworkUsageAnswer(cmd); + } + + private synchronized DirectNetworkUsageAnswer execute(DirectNetworkUsageCommand cmd) { + try { + return getPublicIpBytesSentAndReceived(cmd); + } catch (ExecutionException e) { + return new DirectNetworkUsageAnswer(cmd, e); + } + } + + private Answer execute(MaintainCommand cmd) { + return new MaintainAnswer(cmd); + } + + + private DirectNetworkUsageAnswer getPublicIpBytesSentAndReceived(DirectNetworkUsageCommand cmd) throws ExecutionException { + DirectNetworkUsageAnswer answer = new DirectNetworkUsageAnswer(cmd); + + try { + //Direct Network Usage + URL trafficSentinel; + try { + //Query traffic Sentinel + trafficSentinel = new URL(_url+"/inmsf/Query?script="+URLEncoder.encode(getScript(cmd.getPublicIps(), cmd.getStart(), cmd.getEnd()),"UTF-8") + +"&authenticate=basic&resultFormat=txt"); + + BufferedReader in = new BufferedReader( + new InputStreamReader(trafficSentinel.openStream())); + + String inputLine; + + while ((inputLine = in.readLine()) != null){ + //Parse the script output + StringTokenizer st = new StringTokenizer(inputLine, ","); + if(st.countTokens() == 3){ + String publicIp = st.nextToken(); + Long bytesSent = new Long(st.nextToken()); + Long bytesRcvd = new Long(st.nextToken()); + if(bytesSent == null || bytesRcvd == null){ + s_logger.debug("Incorrect bytes for IP: "+publicIp); + } + long[] bytesSentAndReceived = new long[2]; + bytesSentAndReceived[0] = bytesSent; + bytesSentAndReceived[1] = bytesRcvd; + answer.put(publicIp, bytesSentAndReceived); + } + } + in.close(); + } catch (MalformedURLException e1) { + s_logger.info("Invalid Traffic Sentinel URL",e1); + } catch (IOException e) { + s_logger.debug("Error in direct network usage accounting",e); + } + } catch (Exception e) { + s_logger.error(e); + throw new ExecutionException(e.getMessage()); + } + return answer; + } + + private String getScript(List Ips, Date start, Date end){ + String IpAddresses = ""; + for(int i=0; i"%(clzName, funcName)) + rs = getattr(clz, funcName)(*params) + logger.debug(OvmDispatch, "Exited %s.%s <==="%(clzName, funcName)) + return rs diff --git a/ovm/scripts/vm/hypervisor/ovm/OvmDontTouchOCFS2ClusterWhenAgentStart.patch b/ovm/scripts/vm/hypervisor/ovm/OvmDontTouchOCFS2ClusterWhenAgentStart.patch new file mode 100644 index 00000000000..dfe8462d072 --- /dev/null +++ b/ovm/scripts/vm/hypervisor/ovm/OvmDontTouchOCFS2ClusterWhenAgentStart.patch @@ -0,0 +1,13 @@ +diff --git a/OVSAgentAutoStart.py b/OVSAgentAutoStart.py +index 88fa18c..794a363 100755 +--- a/OVSAgentAutoStart.py ++++ b/OVSAgentAutoStart.py +@@ -111,8 +111,6 @@ def prepare_cluster_heartbeat(): + JOBS = [ + ["command", join(dirname(__file__), "utils/upgrade_agent.py")], + ["function", "get_agent_version", ()], +- ["function", "prepare_cluster_root", ()], +- ["function", "prepare_cluster_heartbeat", ()], + ["command", join(dirname(__file__), "utils/upgrade.py")], + ["command_bg", join(dirname(__file__), "OVSRemasterServer.py")], + ["command_bg", join(dirname(__file__), "OVSMonitorServer.py")], diff --git a/ovm/scripts/vm/hypervisor/ovm/OvmFaultConstants.py b/ovm/scripts/vm/hypervisor/ovm/OvmFaultConstants.py new file mode 100755 index 00000000000..8efeda3ed7a --- /dev/null +++ b/ovm/scripts/vm/hypervisor/ovm/OvmFaultConstants.py @@ -0,0 +1,67 @@ +OvmDispatcherStub = 0 +OvmHostErrCodeStub = 1000 +OvmVmErrCodeStub = 2000 +OvmStoragePoolErrCodeStub = 3000 +OvmNetworkErrCodeStub = 4000 +OvmVolumeErrCodeStub = 5000 + +class NoVmFoundException(Exception): + pass + +errCode = { + # OvmDispatch is not class, these error codes are reserved + "OvmDispatch.InvalidCallMethodFormat":OvmDispatcherStub+1, + "OvmDispatch.InvaildClass":OvmDispatcherStub+2, + "OvmDispatch.InvaildFunction":OvmDispatcherStub+3, + "OvmVm.reboot":OvmDispatcherStub+4, + + "OvmHost.registerAsMaster":OvmHostErrCodeStub+1, + "OvmHost.registerAsVmServer":OvmHostErrCodeStub+2, + "OvmHost.ping":OvmHostErrCodeStub+3, + "OvmHost.getDetails":OvmHostErrCodeStub+4, + "OvmHost.getPerformanceStats":OvmHostErrCodeStub+5, + "OvmHost.getAllVms":OvmHostErrCodeStub+6, + "OvmHost.fence":OvmHostErrCodeStub+7, + "OvmHost.setupHeartBeat":OvmHostErrCodeStub+8, + "OvmHost.pingAnotherHost":OvmHostErrCodeStub+9, + + "OvmVm.create":OvmVmErrCodeStub+1, + "OvmVm.stop":OvmVmErrCodeStub+2, + "OvmVm.getDetails":OvmVmErrCodeStub+3, + "OvmVm.getVmStats":OvmVmErrCodeStub+4, + "OvmVm.migrate":OvmVmErrCodeStub+5, + "OvmVm.register":OvmVmErrCodeStub+6, + "OvmVm.getVncPort":OvmVmErrCodeStub+7, + "OvmVm.detachOrAttachIso":OvmVmErrCodeStub+8, + + "OvmStoragePool.create":OvmStoragePoolErrCodeStub+1, + "OvmStoragePool.getDetailsByUuid":OvmStoragePoolErrCodeStub+2, + "OvmStoragePool.downloadTemplate":OvmStoragePoolErrCodeStub+3, + "OvmStoragePool.prepareOCFS2Nodes":OvmStoragePoolErrCodeStub+4, + + "OvmNetwork.createBridge":OvmNetworkErrCodeStub+1, + "OvmNetwork.deleteBridge":OvmNetworkErrCodeStub+2, + "OvmNetwork.createVlan":OvmNetworkErrCodeStub+3, + "OvmNetwork.deleteVlan":OvmNetworkErrCodeStub+4, + "OvmNetwork.getAllBridges":OvmNetworkErrCodeStub+5, + "OvmNetwork.getBridgeByIp":OvmNetworkErrCodeStub+6, + "OvmNetwork.createVlanBridge":OvmNetworkErrCodeStub+7, + "OvmNetwork.deleteVlanBridge":OvmNetworkErrCodeStub+8, + + "OvmVolume.createDataDisk":OvmVolumeErrCodeStub+1, + "OvmVolume.createFromTemplate":OvmVolumeErrCodeStub+2, + "OvmVolume.destroy":OvmVolumeErrCodeStub+3, +} + + +def toErrCode(clz, func): + global errCode + if not callable(func): raise Exception("%s is not a callable, cannot get error code"%func) + name = clz.__name__ + '.' + func.__name__ + if name not in errCode.keys(): return -1 + return errCode[name] + +def dispatchErrCode(funcName): + name = "OvmDispatch." + funcName + if name not in errCode.keys(): return -1 + return errCode[name] diff --git a/ovm/scripts/vm/hypervisor/ovm/OvmHaHeartBeatModule.py b/ovm/scripts/vm/hypervisor/ovm/OvmHaHeartBeatModule.py new file mode 100644 index 00000000000..93fb96047cc --- /dev/null +++ b/ovm/scripts/vm/hypervisor/ovm/OvmHaHeartBeatModule.py @@ -0,0 +1,90 @@ +''' +Created on Jun 6, 2011 + +@author: frank +''' +from OvmCommonModule import * +try: + from multiprocessing import Process, Manager +except ImportError: + from processing import Process, Manager +import signal + +logger = OvmLogger("OvmHaHeartBeat") + +class OvmHaHeartBeat(object): + ''' + classdocs + ''' + def __init__(self, mountPoint, ip): + self.mountPoint = mountPoint + self.ip = ip + + def mark(self, file): + timestamp = HEARTBEAT_TIMESTAMP_FORMAT % time.time() + try: + fd = open(file, 'w') + fd.write(timestamp) + fd.close() + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmHaHeartBeat.mark, errmsg) + + def run(self): + ''' + Constructor + ''' + heartBeatDir = join(self.mountPoint, HEARTBEAT_DIR) + if not exists(heartBeatDir): + os.makedirs(heartBeatDir) + hearBeatFile = join(heartBeatDir, ipToHeartBeatFileName(self.ip)) + while True: + self.mark(hearBeatFile) + time.sleep(120) + + @staticmethod + def start(poolPath, ip): + pidFile = join(PID_DIR, "heartbeat.pid") + + def isLive(): + if exists(pidFile): + f = open(pidFile) + pid = f.read().strip() + f.close() + if isdir("/proc/%s" % pid): + return long(pid) + return None + + def stopOldHeartBeat(pid): + os.kill(pid, signal.SIGTERM) + time.sleep(5) + pid = isLive() + if pid != None: + logger.debug(OvmHaHeartBeat.start, "SIGTERM cannot stop heartbeat process %s, will try SIGKILL"%pid) + os.kill(pid, signal.SIGKILL) + time.sleep(5) + pid = isLive() + if pid != None: + raise Exception("Cannot stop old heartbeat process %s, setup heart beat failed"%pid) + + def heartBeat(hb): + hb.run() + + def setupHeartBeat(): + hb = OvmHaHeartBeat(poolPath, ip) + p = Process(target=heartBeat, args=(hb,)) + p.start() + pid = p.pid + if not isdir(PID_DIR): + os.makedirs(PID_DIR) + pidFd = open(pidFile, 'w') + pidFd.write(str(pid)) + pidFd.close() + logger.info(OvmHaHeartBeat.start, "Set up heart beat successfully, pid is %s" % pid) + + pid = isLive() + if pid != None: + stopOldHeartBeat(pid) + + setupHeartBeat() + diff --git a/ovm/scripts/vm/hypervisor/ovm/OvmHostModule.py b/ovm/scripts/vm/hypervisor/ovm/OvmHostModule.py new file mode 100644 index 00000000000..6c4708aee34 --- /dev/null +++ b/ovm/scripts/vm/hypervisor/ovm/OvmHostModule.py @@ -0,0 +1,260 @@ +#/usr/bin/python + +from OvmCommonModule import * +from OVSSiteRMServer import get_master_ip, register_server +from OVSCommons import * +from OVSXMonitor import xen_get_xm_info +from OVSXSysInfo import get_agent_version +from OVSSiteRMServer import get_srv_agent_status +from OVSXMonitor import sys_perf_info +from OVSDB import db_get_vm +from OvmStoragePoolModule import OvmStoragePool +from OvmHaHeartBeatModule import OvmHaHeartBeat +import re + +logger = OvmLogger('OvmHost') + +class OvmHostEncoder(json.JSONEncoder): + def default(self, obj): + if not isinstance(obj, OvmHost): raise Exception("%s is not instance of OvmHost"%type(obj)) + dct = {} + safeDictSet(obj, dct, 'masterIp') + safeDictSet(obj, dct, 'cpuNum') + safeDictSet(obj, dct, 'cpuSpeed') + safeDictSet(obj, dct, 'totalMemory') + safeDictSet(obj, dct, 'freeMemory') + safeDictSet(obj, dct, 'dom0Memory') + safeDictSet(obj, dct, 'agentVersion') + safeDictSet(obj, dct, 'name') + safeDictSet(obj, dct, 'dom0KernelVersion') + safeDictSet(obj, dct, 'hypervisorVersion') + return dct + + +def fromOvmHost(host): + return normalizeToGson(json.dumps(host, cls=OvmHostEncoder)) + +class OvmHost(OvmObject): + masterIp = '' + cpuNum = 0 + cpuSpeed = 0 + totalMemory = 0 + freeMemory = 0 + dom0Memory = 0 + agentVersion = '' + name = '' + dom0KernelVersion = '' + hypervisorVersion = '' + + def _getVmPathFromPrimaryStorage(self, vmName): + ''' + we don't have a database to store vm states, so there is no way to retrieve information of a vm + when it was already stopped. The trick is to try to find the vm path in primary storage then we + can read information from its configure file. + ''' + mps = OvmStoragePool()._getAllMountPoints() + vmPath = None + for p in mps: + vmPath = join(p, 'running_pool', vmName) + if exists(vmPath): break + if not vmPath: + logger.error(self._getVmPathFromPrimaryStorage, "Cannot find link for %s in any primary storage, the vm was really gone!"%vmName) + raise Exception("Cannot find link for %s in any primary storage, the vm was really gone!"%vmName) + return vmPath + + def _vmNameToPath(self, vmName): + return successToMap(xen_get_vm_path(vmName))['path'] + + def _getAllDomains(self): + stdout = timeout_command(["xm", "list"]) + l = [ line.split()[:2] for line in stdout.splitlines() ] + l = [ (name, id) for (name, id) in l if name not in ("Name", "Domain-0") ] + return l + + def _getDomainIdByName(self, vmName): + l = self._getAllDomains() + for name, id in l: + if vmName == name: return id + raise NoVmFoundException("No domain id for %s found"%vmName) + + @staticmethod + def registerAsMaster(hostname, username="oracle", password="password", port=8899, isSsl=False): + try: + logger.debug(OvmHost.registerAsMaster, "ip=%s, username=%s, password=%s, port=%s, isSsl=%s"%(hostname, username, password, port, isSsl)) + exceptionIfNoSuccess(register_server(hostname, 'site', False, username, password, port, isSsl), + "Register %s as site failed"%hostname) + exceptionIfNoSuccess(register_server(hostname, 'utility', False, username, password, port, isSsl), + "Register %s as utility failed"%hostname) + rs = SUCC() + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmHost.registerAsMaster, errmsg) + raise XmlRpcFault(toErrCode(OvmHost, OvmHost.registerAsMaster), errmsg) + + @staticmethod + def registerAsVmServer(hostname, username="oracle", password="password", port=8899, isSsl=False): + try: + logger.debug(OvmHost.registerAsVmServer, "ip=%s, username=%s, password=%s, port=%s, isSsl=%s"%(hostname, username, password, port, isSsl)) + exceptionIfNoSuccess(register_server(hostname, 'xen', False, username, password, port, isSsl), + "Register %s as site failed"%hostname) + rs = SUCC() + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmHost.registerAsVmServer, errmsg) + raise XmlRpcFault(toErrCode(OvmHost, OvmHost.registerAsVmServer), errmsg) + + @staticmethod + def ping(hostname): + try: + logger.debug(OvmHost.ping, "ping %s"%hostname) + exceptionIfNoSuccess(get_srv_agent_status(hostname), "Ovs agent is down") + rs = SUCC() + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmHost.ping, errmsg) + raise XmlRpcFault(toErrCode(OvmHost, OvmHost.ping, errmsg)) + + @staticmethod + def getDetails(): + try: + obj = OvmHost() + masterIp = successToMap(get_master_ip()) + safeSetAttr(obj, 'masterIp', masterIp['ip']) + xmInfo = successToMap(xen_get_xm_info()) + totalMemory = MtoBytes(long(xmInfo['total_memory'])) + safeSetAttr(obj, 'totalMemory', totalMemory) + freeMemory = MtoBytes(long(xmInfo['free_memory'])) + safeSetAttr(obj, 'freeMemory', freeMemory) + dom0Memory = totalMemory - freeMemory + safeSetAttr(obj, 'dom0Memory', dom0Memory) + cpuNum = int(xmInfo['nr_cpus']) + safeSetAttr(obj, 'cpuNum', cpuNum) + cpuSpeed = int(xmInfo['cpu_mhz']) + safeSetAttr(obj, 'cpuSpeed', cpuSpeed) + name = xmInfo['host'] + safeSetAttr(obj, 'name', name) + dom0KernelVersion = xmInfo['release'] + safeSetAttr(obj, 'dom0KernelVersion', dom0KernelVersion) + hypervisorVersion = xmInfo['xen_major'] + '.' + xmInfo['xen_minor'] + xmInfo['xen_extra'] + safeSetAttr(obj, 'hypervisorVersion', hypervisorVersion) + agtVersion = successToMap(get_agent_version()) + safeSetAttr(obj, 'agentVersion', agtVersion['agent_version']) + res = fromOvmHost(obj) + logger.debug(OvmHost.getDetails, res) + return res + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmHost.getDetails, errmsg) + raise XmlRpcFault(toErrCode(OvmHost, OvmHost.getDetails), errmsg) + + @staticmethod + def getPerformanceStats(bridgeName): + try: + rxBytesPath = join("/sys/class/net/", bridgeName, "statistics/rx_bytes") + txBytesPath = join("/sys/class/net/", bridgeName, "statistics/tx_bytes") + if not exists(rxBytesPath): raise Exception("Cannot find %s"%rxBytesPath) + if not exists(txBytesPath): raise Exception("Cannot find %s"%txBytesPath) + rxBytes = long(doCmd(['cat', rxBytesPath])) / 1000 + txBytes = long(doCmd(['cat', txBytesPath])) / 1000 + sysPerf = successToMap(sys_perf_info()) + cpuUtil = float(100 - float(sysPerf['cpu_idle']) * 100) + freeMemory = MtoBytes(long(sysPerf['mem_free'])) + xmInfo = successToMap(xen_get_xm_info()) + totalMemory = MtoBytes(long(xmInfo['total_memory'])) + rs = toGson({"cpuUtil":cpuUtil, "totalMemory":totalMemory, "freeMemory":freeMemory, "rxBytes":rxBytes, "txBytes":txBytes}) + logger.info(OvmHost.getPerformanceStats, rs) + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmHost.getPerformanceStats, errmsg) + raise XmlRpcFault(toErrCode(OvmHost, OvmHost.getPerformanceStats), errmsg) + + @staticmethod + def getAllVms(): + try: + l = OvmHost()._getAllDomains() + dct = {} + host = OvmHost() + for name, id in l: + vmPath = host._getVmPathFromPrimaryStorage(name) + vmStatus = db_get_vm(vmPath) + dct[name] = vmStatus['status'] + rs = toGson(dct) + logger.info(OvmHost.getAllVms, rs) + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmHost.getAllVms, errmsg) + raise XmlRpcFault(toErrCode(OvmHost, OvmHost.getAllVms), errmsg) + + @staticmethod + def fence(ip): + # try 3 times to avoid race condition that read when heartbeat file is being written + def getTimeStamp(hbFile): + for i in range(1, 3): + f = open(hbFile, 'r') + str = f.readline() + items = re.findall(HEARTBEAT_TIMESTAMP_PATTERN, str) + if len(items) == 0: + logger.debug(OvmHost.fence, "Get an incorrect heartbeat data %s, will retry %s times" % (str, 3-i)) + f.close() + time.sleep(5) + else: + f.close() + timestamp = items[0] + return timestamp.lstrip('').rstrip('') + + # totally check in 6 mins, the update frequency is 2 mins + def check(hbFile): + for i in range(1, 6): + ts = getTimeStamp(hbFile) + time.sleep(60) + nts = getTimeStamp(hbFile) + if ts != nts: return True + else: logger.debug(OvmHost.fence, '%s is not updated, old value=%s, will retry %s times'%(hbFile, ts, 6-i)) + return False + + try: + mountpoints = OvmStoragePool()._getAllMountPoints() + hbFile = None + for m in mountpoints: + p = join(m, HEARTBEAT_DIR, ipToHeartBeatFileName(ip)) + if exists(p): + hbFile = p + break + + if not hbFile: raise Exception('Can not find heartbeat file for %s in pools %s'%(ip, mountpoints)) + rs = toGson({"isLive":check(hbFile)}) + logger.debug(OvmHost.fence, rs) + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmHost.fence, errmsg) + raise XmlRpcFault(toErrCode(OvmHost, OvmHost.fence), errmsg) + + @staticmethod + def setupHeartBeat(poolUuid, ip): + try: + sr = OvmStoragePool()._getSrByNameLable(poolUuid) + OvmHaHeartBeat.start(sr.mountpoint, ip) + return SUCC() + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmHost.setupHeartBeat, errmsg) + raise XmlRpcFault(toErrCode(OvmHost, OvmHost.setupHeartBeat), errmsg) + + @staticmethod + def pingAnotherHost(ip): + try: + doCmd(['ping', '-c', '1', '-n', '-q', ip]) + return SUCC() + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmHost.pingAnotherHost, errmsg) + raise XmlRpcFault(toErrCode(OvmHost, OvmHost.pingAnotherHost), errmsg) + +if __name__ == "__main__": + print OvmHost.getAllVms() \ No newline at end of file diff --git a/ovm/scripts/vm/hypervisor/ovm/OvmLoggerModule.py b/ovm/scripts/vm/hypervisor/ovm/OvmLoggerModule.py new file mode 100644 index 00000000000..d0c5e3a3bac --- /dev/null +++ b/ovm/scripts/vm/hypervisor/ovm/OvmLoggerModule.py @@ -0,0 +1,39 @@ +''' +Created on May 19, 2011 + +@author: frank +''' +import logging + +class OvmLogger(object): + ''' + classdocs + ''' + + + def __init__(self, className): + ''' + Constructor + ''' + self.className = className + self.logger = logging.getLogger(className) + + def info(self, func, msg=None): + assert callable(func), "%s is not a function"%func + fmt = "[%s.%s]: "%(self.className, func.__name__) + self.logger.info("%s%s"%(fmt,msg)) + + def debug(self, func, msg=None): + assert callable(func), "%s is not a function"%func + fmt = "[%s.%s]: "%(self.className, func.__name__) + self.logger.debug("%s%s"%(fmt,msg)) + + def error(self, func, msg=None): + assert callable(func), "%s is not a function"%func + fmt = "[%s.%s]: "%(self.className, func.__name__) + self.logger.error("%s%s"%(fmt,msg)) + + def warning(self, func, msg=None): + assert callable(func), "%s is not a function"%func + fmt = "[%s.%s]: "%(self.className, func.__name__) + self.logger.warning("%s%s"%(fmt,msg)) \ No newline at end of file diff --git a/ovm/scripts/vm/hypervisor/ovm/OvmNetworkModule.py b/ovm/scripts/vm/hypervisor/ovm/OvmNetworkModule.py new file mode 100644 index 00000000000..2efb75c530c --- /dev/null +++ b/ovm/scripts/vm/hypervisor/ovm/OvmNetworkModule.py @@ -0,0 +1,416 @@ +from OvmCommonModule import * +import traceback +import time +import re + +logger = OvmLogger("OvmNetwork") + +class Filter: + class Network: + IFNAME_LO = r'(lo)' + IFNAME_PIF = r'(eth\d+$)' + IFNAME_VLAN = r'(eth\d+.\d+$)' + IFNAME_BRIDGE = r'(xenbr\d+|vlan\d+)' + +class Parser(object): + ''' + classdocs + ''' + def findall(self, pattern, samples): + """ + @param pattern: search pattern + @param result: Parser line execution result + @return : list of search + find result of Parser which has same pattern + findall Parser find all pattern in a string + """ + result = [] + for line in samples: + items = re.findall(pattern, line) + for item in items: + result.append(item) + return result + + def checkPattern(self, pattern, cmd_result): + """ + @param pattern: search pattern + @param cmd_result: Parser line execution result + @return : True (if pattern is occurred) + """ + for line in cmd_result: + items = re.findall(pattern, line) + if len(items) > 0: + return True + return False + + def search(self, cmd_result, pattern): + return None + +class OvmVlanDecoder(json.JSONDecoder): + def decode(self, jStr): + deDict = asciiLoads(jStr) + vlan = OvmVlan() + setAttrFromDict(vlan, 'vid', deDict, int) + setAttrFromDict(vlan, 'pif', deDict) + return vlan + +class OvmVlanEncoder(json.JSONEncoder): + def default(self, obj): + if not isinstance(obj, OvmVlan): raise Exception("%s is not instance of OvmVlan"%type(obj)) + dct = {} + safeDictSet(obj, dct, 'name') + safeDictSet(obj, dct, 'vid') + safeDictSet(obj, dct, 'pif') + return dct + +def toOvmVlan(jStr): + return json.loads(jStr, cls=OvmVlanDecoder) + +def fromOvmVlan(vlan): + return normalizeToGson(json.dumps(vlan, cls=OvmVlanEncoder)) + +class OvmBridgeDecoder(json.JSONDecoder): + def decode(self, jStr): + deDic = asciiLoads(jStr) + bridge = OvmBridge() + setAttrFromDict(bridge, 'name', deDic) + setAttrFromDict(bridge, 'attach', deDic) + return bridge + +class OvmBridgeEncoder(json.JSONEncoder): + def default(self, obj): + if not isinstance(obj, OvmBridge): raise Exception("%s is not instance of OvmBridge"%type(obj)) + dct = {} + safeDictSet(obj, dct, 'name') + safeDictSet(obj, dct, 'attach') + safeDictSet(obj, dct, 'interfaces') + return dct + +def toOvmBridge(jStr): + return json.loads(jStr, cls=OvmBridgeDecoder) + +def fromOvmBridge(bridge): + return normalizeToGson(json.dumps(bridge, cls=OvmBridgeEncoder)) + +class OvmInterface(OvmObject): + name = '' + +class OvmVlan(OvmInterface): + vid = 0 + pif = '' + +class OvmBridge(OvmInterface): + attach = '' + interfaces = [] + + +class OvmNetwork(OvmObject): + ''' + Network + ''' + + @property + def pifs(self): + return self._getInterfaces("pif") + + @property + def vlans(self): + return self._getInterfaces("vlan") + + @property + def bridges(self): + return self._getInterfaces("bridge") + + def __init__(self): + self.Parser = Parser() + + def _createVlan(self, vlan): + """ + @param jsonString : parameter from client side + @return : succ xxxxx + ex. jsonString => {vid:100, pif:eth0} + ex. return => + """ + + #Pre-condition + #check Physical Interface Name + if vlan.pif not in self.pifs.keys(): + msg = "Physical Interface(%s) does not exist" % vlan.pif + logger.debug(self._createVlan, msg) + raise Exception(msg) + + #Pre-condition + #check Vlan Interface Name + ifName = "%s.%s" % (vlan.pif, vlan.vid) + if ifName in self.vlans.keys(): + msg = "Vlan Interface(%s) already exist, return it" % ifName + logger.debug(self._createVlan, msg) + return self.vlans[ifName] + + doCmd(['vconfig', 'add', vlan.pif, vlan.vid]) + self.bringUP(ifName) + logger.debug(self._createVlan, "Create vlan %s successfully"%ifName) + return self.vlans[ifName] + + def _deleteVlan(self, name): + if name not in self.vlans.keys(): + raise Exception("No vlan device %s found"%name) + + vlan = self.vlans[name] + self.bringDown(vlan.name) + doCmd(['vconfig', 'rem', vlan.name]) + logger.debug(self._deleteVlan, "Delete vlan %s successfully"%vlan.name) + + + def _createBridge(self, bridge): + """ + @return : success + ex. {bridge:xapi100, attach:eth0.100} + create bridge interface, and attached it + cmd 1: brctl addbr bridge + cmd 2: brctl addif brdige attach + """ + + if "xenbr" not in bridge.name and "vlan" not in bridge.name: + raise Exception("Invalid bridge name %s. Bridge name must be in partten xenbr/vlan, e.g. xenbr0"%bridge.name) + + #pre-condition + #check Bridge Interface Name + if bridge.name in self.bridges.keys(): + msg = "Bridge(%s) already exist, return it" % bridge.name + logger.debug(self._createBridge, msg) + return self.bridges[bridge.name] + + #pre-condition + #check attach must exist + #possible to attach in PIF or VLAN + if bridge.attach not in self.vlans.keys() and bridge.attach not in self.pifs.keys(): + msg = "%s is not either pif or vlan" % bridge.attach + logger.error(self._createBridge, msg) + raise Exception(msg) + + doCmd(['brctl', 'addbr', bridge.name]) + doCmd(['brctl', 'addif', bridge.name, bridge.attach]) + self.bringUP(bridge.name) + logger.debug(self._createBridge, "Create bridge %s on %s successfully"%(bridge.name, bridge.attach)) + return self.bridges[bridge.name] + + def _getBridges(self): + return self.bridges.keys() + + def _getVlans(self): + return self.vlans.keys() + + def _deleteBridge(self, name): + if name not in self.bridges.keys(): + raise Exception("Can not find bridge %s"%name) + + bridge = self.bridges[name] + if bridge.attach in bridge.interfaces: bridge.interfaces.remove(bridge.attach) + if len(bridge.interfaces) != 0: + logger.debug(self._deleteBridge, "There are still some interfaces(%s) on bridge %s"%(bridge.interfaces, bridge.name)) + return False + self.bringDown(bridge.name) + doCmd(['brctl', 'delbr', bridge.name]) + logger.debug(self._deleteBridge, "Delete bridge %s successfully"%bridge.name) + return True + + def _getInterfaces(self, type): + """ + @param type : ["pif", "bridge", "tap"] + @return : dictionary of Interface Objects + get All Interfaces based on type + """ + devices = os.listdir('/sys/class/net') + ifs = {} + if type == "pif": + devs = self.Parser.findall(Filter.Network.IFNAME_PIF, devices) + for dev in set(devs): + ifInst = OvmInterface() + ifInst.name = dev + ifs[dev] = ifInst + + elif type == "vlan": + devs = self.Parser.findall(Filter.Network.IFNAME_VLAN, devices) + for dev in set(devs): + ifInst = OvmVlan() + ifInst.name = dev + (pif, vid) = dev.split('.') + ifInst.pif = pif + ifInst.vid = vid + ifs[dev] = ifInst + + elif type == "bridge": + devs = self.Parser.findall(Filter.Network.IFNAME_BRIDGE, devices) + for dev in set(devs): + ifInst = OvmBridge() + ifInst.name = dev + devs = os.listdir(join('/sys/class/net', dev, 'brif')) + ifInst.interfaces = devs + attches = self.Parser.findall(Filter.Network.IFNAME_PIF, devs) + self.Parser.findall(Filter.Network.IFNAME_VLAN, devs) + if len(attches) > 1: raise Exception("Multiple PIF on bridge %s (%s)"%(dev, attches)) + elif len(attches) == 0: ifInst.attach = "null" + elif len(attches) == 1: ifInst.attach = attches[0] + ifs[dev] = ifInst + + return ifs + + def bringUP(self, ifName): + doCmd(['ifconfig', ifName, 'up']) + + def bringDown(self, ifName): + doCmd(['ifconfig', ifName, 'down']) + + @staticmethod + def createBridge(jStr): + try: + network = OvmNetwork() + network._createBridge(toOvmBridge(jStr)) + return SUCC() + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmNetwork.createBridge, errmsg) + raise XmlRpcFault(toErrCode(OvmNetwork, OvmNetwork.createBridge), errmsg) + + @staticmethod + def deleteBridge(name): + try: + network = OvmNetwork() + network._deleteBridge(name) + return SUCC() + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmNetwork.deleteBridge, errmsg) + raise XmlRpcFault(toErrCode(OvmNetwork, OvmNetwork.deleteBridge), errmsg) + + @staticmethod + def getAllBridges(): + try: + network = OvmNetwork() + rs = toGson(network._getBridges()) + logger.debug(OvmNetwork.getAllBridges, rs) + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmNetwork.getAllBridges, errmsg) + raise XmlRpcFault(toErrCode(OvmNetwork, OvmNetwork.getAllBridges), errmsg) + + @staticmethod + def getBridgeByIp(ip): + try: + routes = doCmd(['ip', 'route']).split('\n') + brName = None + for r in routes: + if ip in r and "xenbr" in r or "vlan" in r: + brName = r.split(' ')[2] + break + if not brName: raise Exception("Cannot find bridge with IP %s"%ip) + logger.debug(OvmNetwork.getBridgeByIp, "bridge:%s, ip:%s"%(brName, ip)) + return toGson({"bridge":brName}) + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmNetwork.getBridgeByIp, errmsg) + raise XmlRpcFault(toErrCode(OvmNetwork, OvmNetwork.getBridgeByIp), errmsg) + + @staticmethod + def getVlans(): + try: + network = OvmNetwork() + rs = toGson(network._getVlans()) + logger.debug(OvmNetwork.getVlans, rs) + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmNetwork.getVlans, errmsg) + raise XmlRpcFault(toErrCode(OvmNetwork, OvmNetwork.getVlans), errmsg) + + @staticmethod + def createVlan(jStr): + try: + network = OvmNetwork() + vlan = network._createVlan(toOvmVlan(jStr)) + rs = fromOvmVlan(vlan) + logger.debug(OvmNetwork.createVlan, rs) + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmNetwork.createVlan, errmsg) + raise XmlRpcFault(toErrCode(OvmNetwork, OvmNetwork.createVlan), errmsg) + + @staticmethod + def createVlanBridge(bridgeDetails, vlanDetails): + try: + network = OvmNetwork() + v = toOvmVlan(vlanDetails) + b = toOvmBridge(bridgeDetails) + vlan = network._createVlan(v) + b.attach = vlan.name + network._createBridge(b) + return SUCC() + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmNetwork.createVlanBridge, errmsg) + raise XmlRpcFault(toErrCode(OvmNetwork, OvmNetwork.createVlanBridge), errmsg) + + @staticmethod + def deleteVlanBridge(name): + try: + network = OvmNetwork() + if name not in network.bridges.keys(): + logger.debug(OvmNetwork.deleteVlanBridge, "No bridge %s found"%name) + return SUCC() + + bridge = network.bridges[name] + vlanName = bridge.attach + if network._deleteBridge(name): + if vlanName != "null": + network._deleteVlan(vlanName) + else: + logger.warning(OvmNetwork.deleteVlanBridge, "Bridge %s has no vlan device"%name) + return SUCC() + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmNetwork.deleteVlanBridge, errmsg) + raise XmlRpcFault(toErrCode(OvmNetwork, OvmNetwork.deleteVlanBridge), errmsg) + + @staticmethod + def getBridgeDetails(name): + try: + network = OvmNetwork() + if name not in network.bridges.keys(): + raise Exception("No bridge %s found"%name) + bridge = network.bridges[name] + rs = fromOvmBridge(bridge) + logger.debug(OvmNetwork.getBridgeDetails, rs) + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmNetwork.getBridgeDetails, errmsg) + raise XmlRpcFault(toErrCode(OvmNetwork, OvmNetwork.getBridgeDetails), errmsg) + + @staticmethod + def deleteVlan(name): + try: + network = OvmNetwork() + network._deleteVlan(name) + return SUCC() + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmNetwork.deleteVlan, errmsg) + raise XmlRpcFault(toErrCode(OvmNetwork, OvmNetwork.deleteVlan), errmsg) + +if __name__ == "__main__": + try: + OvmNetwork.getBridgeDetails(sys.argv[1]) + #======================================================================= + # txt = json.dumps({"vid":104, "pif":"eth0"}) + # txt2 = json.dumps({"name":"xapi3", "attach":"eth0.104"}) + # print nw.createVlan(txt) + # print nw.createBridge(txt2) + # + # nw.deleteBridge("xapi3") + # nw.deleteVlan("eth0.104") + #======================================================================= + + except Exception, e: + print e diff --git a/ovm/scripts/vm/hypervisor/ovm/OvmObjectModule.py b/ovm/scripts/vm/hypervisor/ovm/OvmObjectModule.py new file mode 100644 index 00000000000..421595de42b --- /dev/null +++ b/ovm/scripts/vm/hypervisor/ovm/OvmObjectModule.py @@ -0,0 +1,8 @@ +''' +Created on May 17, 2011 + +@author: frank +''' + +class OvmObject(object): + pass \ No newline at end of file diff --git a/ovm/scripts/vm/hypervisor/ovm/OvmPatch.patch b/ovm/scripts/vm/hypervisor/ovm/OvmPatch.patch new file mode 100644 index 00000000000..9d5adceed49 --- /dev/null +++ b/ovm/scripts/vm/hypervisor/ovm/OvmPatch.patch @@ -0,0 +1,23 @@ +*** OVSServices.py 2011-06-06 12:31:23.279919998 -0700 +--- /tmp/OVSServices.py 2011-06-06 12:32:13.275919997 -0700 +*************** +*** 68,73 **** +--- 68,75 ---- + import OVSVMXParser #pylint: disable-msg=W0611 + import OVSVMDKParser #pylint: disable-msg=W0611 + ++ import OvmDispatcher ++ + from OVSWrappers import D + from OVSCommons import exposed + +*************** +*** 113,118 **** +--- 115,121 ---- + self.sleep = sleep + + self._load_modules() ++ OvmDispatcher.InitOvmDispacther() + + # #xenapi + # import xen.xend.XendAPI as XendAPI diff --git a/ovm/scripts/vm/hypervisor/ovm/OvmSecurityGroupModule.py b/ovm/scripts/vm/hypervisor/ovm/OvmSecurityGroupModule.py new file mode 100644 index 00000000000..d0cc32b138e --- /dev/null +++ b/ovm/scripts/vm/hypervisor/ovm/OvmSecurityGroupModule.py @@ -0,0 +1,466 @@ +from OvmCommonModule import * +from ConfigFileOps import * +import os +import logging + +class OvmSecurityGroup(OvmObject): + + @staticmethod + def can_bridge_firewall(): + try: + execute("which iptables") + except: + print "iptables was not found on the host" + return False + + try: + execute("which ebtables") + except: + print "ebtables was not found on the host" + return False + + if not os.path.exists('/var/run/cloud'): + os.makedirs('/var/run/cloud') + + return OvmSecurityGroup.cleanup_rules() + + @staticmethod + def cleanup_rules(): + try: + chainscmd = "iptables-save | grep '^:' | grep -v '.*-def' | awk '{print $1}' | cut -d':' -f2" + chains = execute(chainscmd).split('\n') + cleaned = 0 + cleanup = [] + for chain in chains: + if 1 in [ chain.startswith(c) for c in ['r-', 'i-', 's-', 'v-'] ]: + vm_name = chain + else: + continue + + cmd = "xm list | grep " + vm_name + try: + result = execute(cmd) + except: + result = None + + if result == None or len(result) == 0: + logging.debug("chain " + chain + " does not correspond to a vm, cleaning up") + cleanup.append(vm_name) + + for vm_name in cleanup: + OvmSecurityGroup.delete_all_network_rules_for_vm(vm_name) + + logging.debug("Cleaned up rules for " + str(len(cleanup)) + " chains") + return True + except: + logging.debug("Failed to cleanup rules !") + return False + + @staticmethod + def add_fw_framework(bridge_name): + try: + cfo = ConfigFileOps("/etc/sysctl.conf") + cfo.addEntry("net.bridge.bridge-nf-call-arptables", "1") + cfo.addEntry("net.bridge.bridge-nf-call-iptables", "1") + cfo.addEntry("net.bridge.bridge-nf-call-ip6tables", "1") + cfo.save() + + execute("sysctl -p /etc/sysctl.conf") + except: + logging.debug("failed to turn on bridge netfilter") + return False + + brfw = "BF-" + bridge_name + try: + execute("iptables -L " + brfw) + except: + execute("iptables -N " + brfw) + + brfwout = brfw + "-OUT" + try: + execute("iptables -L " + brfwout) + except: + execute("iptables -N " + brfwout) + + brfwin = brfw + "-IN" + try: + execute("iptables -L " + brfwin) + except: + execute("iptables -N " + brfwin) + + try: + refs = execute("iptables -n -L " + brfw + " |grep " + brfw + " | cut -d \( -f2 | awk '{print $1}'").strip() + if refs == "0": + execute("iptables -I FORWARD -i " + bridge_name + " -j DROP") + execute("iptables -I FORWARD -o " + bridge_name + " -j DROP") + execute("iptables -I FORWARD -i " + bridge_name + " -m physdev --physdev-is-bridged -j " + brfw) + execute("iptables -I FORWARD -o " + bridge_name + " -m physdev --physdev-is-bridged -j " + brfw) + phydev = execute("brctl show |grep " + bridge_name + " | awk '{print $4}'").strip() + execute("iptables -A " + brfw + " -m physdev --physdev-is-bridged --physdev-out " + phydev + " -j ACCEPT") + execute("iptables -A " + brfw + " -m state --state RELATED,ESTABLISHED -j ACCEPT") + execute("iptables -A " + brfw + " -m physdev --physdev-is-bridged --physdev-is-out -j " + brfwout) + execute("iptables -A " + brfw + " -m physdev --physdev-is-bridged --physdev-is-in -j " + brfwin) + + return True + except: + try: + execute("iptables -F " + brfw) + except: + return False + + return False + + @staticmethod + def default_network_rules_user_vm(vm_name, vm_id, vm_ip, vm_mac, vif, bridge_name): + if not OvmSecurityGroup.add_fw_framework(bridge_name): + return False + + OvmSecurityGroup.delete_iptables_rules_for_vm(vm_name) + OvmSecurityGroup.delete_ebtables_rules_for_vm(vm_name) + + bridge_firewall_chain = "BF-" + bridge_name + vm_chain = vm_name + default_vm_chain = '-'.join(vm_chain.split('-')[:-1]) + "-def" + dom_id = getDomId(vm_name) + + try: + execute("iptables -N " + vm_chain) + except: + execute("iptables -F " + vm_chain) + + try: + execute("iptables -N " + default_vm_chain) + except: + execute("iptables -F " + default_vm_chain) + + try: + execute("iptables -A " + bridge_firewall_chain + "-OUT" + " -m physdev --physdev-is-bridged --physdev-out " + vif + " -j " + default_vm_chain) + execute("iptables -A " + bridge_firewall_chain + "-IN" + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -j " + default_vm_chain) + execute("iptables -A " + default_vm_chain + " -m state --state RELATED,ESTABLISHED -j ACCEPT") + + # Allow DHCP + execute("iptables -A " + default_vm_chain + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -p udp --dport 67 --sport 68 -j ACCEPT") + execute("iptables -A " + default_vm_chain + " -m physdev --physdev-is-bridged --physdev-out " + vif + " -p udp --dport 68 --sport 67 -j ACCEPT") + + # Don't let a VM spoof its ip address + if vm_ip is not None: + execute("iptables -A " + default_vm_chain + " -m physdev --physdev-is-bridged --physdev-in " + vif + " --source " + vm_ip + " -j ACCEPT") + + execute("iptables -A " + default_vm_chain + " -j " + vm_chain) + execute("iptables -A " + vm_chain + " -j DROP") + except: + logging.debug("Failed to program default rules for vm " + vm_name) + return False + + OvmSecurityGroup.default_ebtables_rules(vm_chain, vm_ip, vm_mac, vif) + + if vm_ip is not None: + if (OvmSecurityGroup.write_rule_log_for_vm(vm_name, vm_id, vm_ip, dom_id, '_initial_', '-1') == False): + logging.debug("Failed to log default network rules, ignoring") + + logging.debug("Programmed default rules for vm " + vm_name) + return True + + @staticmethod + def default_ebtables_rules(vm_name, vm_ip, vm_mac, vif): + vm_chain_in = vm_name + "-in" + vm_chain_out = vm_name + "-out" + + for chain in [vm_chain_in, vm_chain_out]: + try: + execute("ebtables -t nat -N " + chain) + except: + execute("ebtables -t nat -F " + chain) + + try: + execute("ebtables -t nat -A PREROUTING -i " + vif + " -j " + vm_chain_in) + execute("ebtables -t nat -A POSTROUTING -o " + vif + " -j " + vm_chain_out) + except: + logging.debug("Failed to program default rules") + return False + + try: + execute("ebtables -t nat -A " + vm_chain_in + " -s ! " + vm_mac + " -j DROP") + execute("ebtables -t nat -A " + vm_chain_in + " -p ARP -s ! " + vm_mac + " -j DROP") + execute("ebtables -t nat -A " + vm_chain_in + " -p ARP --arp-mac-src ! " + vm_mac + " -j DROP") + if vm_ip is not None: + execute("ebtables -t nat -A " + vm_chain_in + " -p ARP --arp-ip-src ! " + vm_ip + " -j DROP") + execute("ebtables -t nat -A " + vm_chain_in + " -p ARP --arp-op Request -j ACCEPT") + execute("ebtables -t nat -A " + vm_chain_in + " -p ARP --arp-op Reply -j ACCEPT") + execute("ebtables -t nat -A " + vm_chain_in + " -p ARP -j DROP") + except: + logging.exception("Failed to program default ebtables IN rules") + return False + + try: + execute("ebtables -t nat -A " + vm_chain_out + " -p ARP --arp-op Reply --arp-mac-dst ! " + vm_mac + " -j DROP") + if vm_ip is not None: + execute("ebtables -t nat -A " + vm_chain_out + " -p ARP --arp-ip-dst ! " + vm_ip + " -j DROP") + execute("ebtables -t nat -A " + vm_chain_out + " -p ARP --arp-op Request -j ACCEPT") + execute("ebtables -t nat -A " + vm_chain_out + " -p ARP --arp-op Reply -j ACCEPT") + execute("ebtables -t nat -A " + vm_chain_out + " -p ARP -j DROP") + except: + logging.debug("Failed to program default ebtables OUT rules") + return False + + return True + + @staticmethod + def add_network_rules(vm_name, vm_id, vm_ip, signature, seqno, vm_mac, rules, vif, bridge_name): + try: + vm_chain = vm_name + dom_id = getDomId(vm_name) + + changes = [] + changes = OvmSecurityGroup.check_rule_log_for_vm(vm_name, vm_id, vm_ip, dom_id, signature, seqno) + + if not 1 in changes: + logging.debug("Rules already programmed for vm " + vm_name) + return True + + if changes[0] or changes[1] or changes[2] or changes[3]: + if not OvmSecurityGroup.default_network_rules(vm_name, vm_id, vm_ip, vm_mac, vif, bridge_name): + return False + + if rules == "" or rules == None: + lines = [] + else: + lines = rules.split(';')[:-1] + + logging.debug("Programming network rules for IP: " + vm_ip + " vmname=" + vm_name) + execute("iptables -F " + vm_chain) + + for line in lines: + tokens = line.split(':') + if len(tokens) != 4: + continue + protocol = tokens[0] + start = tokens[1] + end = tokens[2] + cidrs = tokens.pop(); + ips = cidrs.split(",") + ips.pop() + allow_any = False + if '0.0.0.0/0' in ips: + i = ips.index('0.0.0.0/0') + del ips[i] + allow_any = True + + port_range = start + ":" + end + if ips: + if protocol == 'all': + for ip in ips: + execute("iptables -I " + vm_chain + " -m state --state NEW -s " + ip + " -j ACCEPT") + elif protocol != 'icmp': + for ip in ips: + execute("iptables -I " + vm_chain + " -p " + protocol + " -m " + protocol + " --dport " + port_range + " -m state --state NEW -s " + ip + " -j ACCEPT") + else: + port_range = start + "/" + end + if start == "-1": + port_range = "any" + for ip in ips: + execute("iptables -I " + vm_chain + " -p icmp --icmp-type " + port_range + " -s " + ip + " -j ACCEPT") + + if allow_any and protocol != 'all': + if protocol != 'icmp': + execute("iptables -I " + vm_chain + " -p " + protocol + " -m " + protocol + " --dport " + port_range + " -m state --state NEW -j ACCEPT") + else: + port_range = start + "/" + end + if start == "-1": + port_range = "any" + execute("iptables -I " + vm_chain + " -p icmp --icmp-type " + port_range + " -j ACCEPT") + + iptables = "iptables -A " + vm_chain + " -j DROP" + execute(iptables) + + return OvmSecurityGroup.write_rule_log_for_vm(vm_name, vm_id, vm_ip, dom_id, signature, seqno) + except: + logging.debug("Failed to network rule !: " + sys.exc_type) + return False + + @staticmethod + def delete_all_network_rules_for_vm(vm_name, vif = None): + OvmSecurityGroup.delete_iptables_rules_for_vm(vm_name) + OvmSecurityGroup.delete_ebtables_rules_for_vm(vm_name) + + vm_chain = vm_name + default_vm_chain = None + if vm_name.startswith('i-') or vm_name.startswith('r-'): + default_vm_chain = '-'.join(vm_name.split('-')[:-1]) + "-def" + + try: + if default_vm_chain != None: + execute("iptables -F " + default_vm_chain) + except: + logging.debug("Ignoring failure to delete chain " + default_vm_chain) + + try: + if default_vm_chain != None: + execute("iptables -X " + vmchain_default) + except: + logging.debug("Ignoring failure to delete chain " + default_vm_chain) + + try: + execute("iptables -F " + vm_chain) + except: + logging.debug("Ignoring failure to delete chain " + vm_chain) + + try: + execute("iptables -X " + vm_chain) + except: + logging.debug("Ignoring failure to delete chain " + vm_chain) + + if vif is not None: + try: + dnats = execute("iptables-save -t nat | grep " + vif + " | sed 's/-A/-D/'").split("\n") + for dnat in dnats: + try: + execute("iptables -t nat " + dnat) + except: + logging.debug("Igoring failure to delete dnat: " + dnat) + except: + pass + + OvmSecurityGroup.remove_rule_log_for_vm(vm_name) + + if 1 in [ vm_name.startswith(c) for c in ['r-', 's-', 'v-'] ]: + return True + + return True + + @staticmethod + def delete_iptables_rules_for_vm(vm_name): + vm_name = OvmSecurityGroup.truncate_vm_name(vm_name) + vm_chain = vm_name + query = "iptables-save | grep " + vm_chain + " | grep physdev-is-bridged | sed 's/-A/-D/'" + delete_cmds = execute(query).split('\n') + delete_cmds.pop() + + for cmd in delete_cmds: + try: + execute("iptables " + cmd) + except: + logging.exception("Ignoring failure to delete rules for vm " + vm_name) + + @staticmethod + def delete_ebtables_rules_for_vm(vm_name): + vm_name = OvmSecurityGroup.truncate_vm_name(vm_name) + query = "ebtables -t nat -L --Lx | grep ROUTING | grep " + vm_name + " | sed 's/-A/-D/'" + delete_cmds = execute(query).split('\n') + delete_cmds.pop() + + for cmd in delete_cmds: + try: + execute(cmd) + except: + logging.debug("Ignoring failure to delete ebtables rules for vm " + vm_name) + + chains = [vm_name + "-in", vm_name + "-out"] + + for chain in chains: + try: + execute("ebtables -t nat -F " + chain) + execute("ebtables -t nat -X " + chain) + except: + logging.debug("Ignoring failure to delete ebtables chain for vm " + vm_name) + + @staticmethod + def truncate_vm_name(vm_name): + if vm_name.startswith('i-') or vm_name.startswith('r-'): + truncated_vm_name = '-'.join(vm_name.split('-')[:-1]) + else: + truncated_vm_name = vm_name + return truncated_vm_name + + @staticmethod + def write_rule_log_for_vm(vm_name, vm_id, vm_ip, dom_id, signature, seqno): + log_file_name = "/var/run/cloud/" + vm_name + ".log" + logging.debug("Writing log to " + log_file_name) + logf = open(log_file_name, 'w') + output = ','.join([vm_name, vm_id, vm_ip, dom_id, signature, seqno]) + + result = True + try: + logf.write(output) + logf.write('\n') + except: + logging.debug("Failed to write to rule log file " + log_file_name) + result = False + + logf.close() + return result + + @staticmethod + def remove_rule_log_for_vm(vm_name): + log_file_name = "/var/run/cloud/" + vm_name +".log" + + result = True + try: + os.remove(log_file_name) + except: + logging.debug("Failed to delete rule log file " + log_file_name) + result = False + + return result + + @staticmethod + def check_rule_log_for_vm(vm_name, vm_id, vm_ip, dom_id, signature, seqno): + log_file_name = "/var/run/cloud/" + vm_name + ".log" + if not os.path.exists(log_file_name): + return [True, True, True, True, True, True] + + try: + lines = (line.rstrip() for line in open(log_file_name)) + except: + logging.debug("failed to open " + log_file_name) + return [True, True, True, True, True, True] + + [_vm_name, _vm_id, _vm_ip, _dom_id, _signature, _seqno] = ['_', '-1', '_', '-1', '_', '-1'] + try: + for line in lines: + [_vm_name, _vm_id, _vm_ip, _dom_id, _signature, _seqno] = line.split(',') + break + except: + logging.debug("Failed to parse log file for vm " + vm_name) + remove_rule_log_for_vm(vm_name) + return [True, True, True, True, True, True] + + return [(vm_name != _vm_name), (vm_id != _vm_id), (vm_ip != _vm_ip), (dom_id != _dom_id), (signature != _signature), (seqno != _seqno)] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ovm/scripts/vm/hypervisor/ovm/OvmStoragePoolModule.py b/ovm/scripts/vm/hypervisor/ovm/OvmStoragePoolModule.py new file mode 100755 index 00000000000..22eb9ad908e --- /dev/null +++ b/ovm/scripts/vm/hypervisor/ovm/OvmStoragePoolModule.py @@ -0,0 +1,289 @@ +from OvmCommonModule import * +from OVSSiteSR import sp_create, sr_create, sr_do +from OVSParser import parse_ocfs2_cluster_conf +from OVSXCluster import clusterm_set_ocfs2_cluster_conf, clusterm_start_o2cb_service +import re + +class OvmStoragePoolDecoder(json.JSONDecoder): + def decode(self, jStr): + dct = asciiLoads(jStr) + pool = OvmStoragePool() + setAttrFromDict(pool, 'uuid', dct) + setAttrFromDict(pool, 'type', dct) + setAttrFromDict(pool, 'path', dct) + return pool + +class OvmStoragePoolEncoder(json.JSONEncoder): + def default(self, obj): + if not isinstance(obj, OvmStoragePool): raise Exception("%s is not instance of OvmStoragePool"%type(obj)) + dct = {} + safeDictSet(obj, dct, 'uuid') + safeDictSet(obj, dct, 'type') + safeDictSet(obj, dct, 'path') + safeDictSet(obj, dct, 'mountPoint') + safeDictSet(obj, dct, 'totalSpace') + safeDictSet(obj, dct, 'freeSpace') + safeDictSet(obj, dct, 'usedSpace') + return dct + +def fromOvmStoragePool(pool): + return normalizeToGson(json.dumps(pool, cls=OvmStoragePoolEncoder)) + +def toOvmStoragePool(jStr): + return json.loads(jStr, cls=OvmStoragePoolDecoder) + +logger = OvmLogger('OvmStoragePool') +class OvmStoragePool(OvmObject): + uuid = '' + type = '' + path = '' + mountPoint = '' + totalSpace = 0 + freeSpace = 0 + usedSpace = 0 + + def _getSrByNameLable(self, poolUuid): + d = db_dump('sr') + for uuid, sr in d.items(): + if sr.name_label == poolUuid: + return sr + + raise Exception("No SR matching to %s" % poolUuid) + + def _getSpaceinfoOfDir(self, dir): + stat = os.statvfs(dir) + freeSpace = stat.f_frsize * stat.f_bavail; + totalSpace = stat.f_blocks * stat.f_frsize; + return (totalSpace, freeSpace) + + def _checkDirSizeForImage(self, dir, image): + (x, free_storage_size) = OvmStoragePool()._getSpaceinfoOfDir(dir) + image_size = os.path.getsize(image) + if image_size > (free_storage_size + 1024 * 1024 * 1024): + raise Exception("No space on dir %s (free storage:%s, vm size:%s)"%(dir, free_storage_size, image_size)) + + def _getAllMountPoints(self): + mps = [] + d = db_dump('sr') + for uuid, sr in d.items(): + mps.append(sr.mountpoint) + return mps + + def _isMounted(self, path): + res = doCmd(['mount']) + return (path in res) + + def _mount(self, target, mountpoint): + if not exists(mountpoint): + os.makedirs(mountpoint) + + if not OvmStoragePool()._isMounted(mountpoint): + doCmd(['mount', target, mountpoint, '-r']) + + @staticmethod + def create(jStr): + try: + pool = toOvmStoragePool(jStr) + logger.debug(OvmStoragePool.create, fromOvmStoragePool(pool)) + spUuid = jsonSuccessToMap(sp_create(pool.type, pool.path))['uuid'] + srUuid = jsonSuccessToMap(sr_create(spUuid, name_label=pool.uuid))['uuid'] + sr_do(srUuid, "initialize") + rs = SUCC() + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmStoragePool.create, errmsg) + raise XmlRpcFault(toErrCode(OvmStoragePool, OvmStoragePool.create), errmsg) + + @staticmethod + def getDetailsByUuid(uuid): + try: + sr = OvmStoragePool()._getSrByNameLable(uuid) + pool = OvmStoragePool() + safeSetAttr(pool, 'uuid', uuid) + #Note: the sr.sp.fs_type is not mapped to its class name which we use in mgmt server + safeSetAttr(pool, 'type', sr.sp.__class__.__name__) + safeSetAttr(pool, 'path', sr.sp.get_fs_spec()) + safeSetAttr(pool, 'mountPoint', sr.mountpoint) + (totalSpace, freeSpace) = OvmStoragePool()._getSpaceinfoOfDir(sr.mountpoint) + safeSetAttr(pool, 'totalSpace', totalSpace) + safeSetAttr(pool, 'freeSpace', freeSpace) + safeSetAttr(pool, 'usedSpace', totalSpace - freeSpace) + res = fromOvmStoragePool(pool) + logger.debug(OvmStoragePool.getDetailsByUuid, res) + return res + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmStoragePool.getDetailsByUuid, errmsg) + raise XmlRpcFault(toErrCode(OvmStoragePool, OvmStoragePool.getDetailsByUuid), errmsg) + + @staticmethod + def downloadTemplate(uuid, secPath): + secMountPoint = None + try: + logger.debug(OvmStoragePool.downloadTemplate, "download %s to pool %s"%(secPath, uuid)) + try: + tmpUuid = get_uuid() + secMountPoint = join("/var/cloud/", tmpUuid) + if not exists(secMountPoint): + os.makedirs(secMountPoint) + + templateFile = None + if secPath.endswith("raw"): + secPathDir = os.path.dirname(secPath) + templateFile = os.path.basename(secPath) + else: + secPathDir = secPath + + # mount as read-only + mountCmd = ['mount.nfs', secPathDir, secMountPoint, '-r'] + doCmd(mountCmd) + + if not templateFile: + for f in os.listdir(secMountPoint): + if isfile(join(secMountPoint, f)) and f.endswith('raw'): + templateFile = f + break + + if not templateFile: + raise Exception("Can not find raw template in secondary storage") + templateSecPath = join(secMountPoint, templateFile) + + sr = OvmStoragePool()._getSrByNameLable(uuid) + priStorageMountPoint = sr.mountpoint + # Although mgmt server will check the size, we check again for safety + OvmStoragePool()._checkDirSizeForImage(priStorageMountPoint, templateSecPath) + seedDir = join(priStorageMountPoint, 'seed_pool', tmpUuid) + if exists(seedDir): + raise Exception("%s already here, cannot override existing template" % seedDir) + os.makedirs(seedDir) + + tgt = join(seedDir, templateFile) + cpTemplateCmd = ['cp', templateSecPath, tgt] + logger.info(OvmStoragePool.downloadTemplate, " ".join(cpTemplateCmd)) + doCmd(cpTemplateCmd) + templateSize = os.path.getsize(tgt) + logger.info(OvmStoragePool.downloadTemplate, "primary_storage_download success:installPath:%s, templateSize:%s"%(tgt,templateSize)) + rs = toGson({"installPath":tgt, "templateSize":templateSize}) + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmStoragePool.downloadTemplate, errmsg) + raise XmlRpcFault(toErrCode(OvmStoragePool, OvmStoragePool.downloadTemplate), errmsg) + finally: + if exists(secMountPoint): + try: + umountCmd = ['umount', '-f', secMountPoint] + doCmd(umountCmd) + ls = os.listdir(secMountPoint) + if len(ls) == 0: + rmDirCmd = ['rm', '-r', secMountPoint] + doCmd(rmDirCmd) + else: + logger.warning(OvmStoragePool.downloadTemplate, "Something wrong when umount %s, there are still files in directory:%s", secMountPoint, " ".join(ls)) + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmStoragePool.downloadTemplate, 'unmount secondary storage at %s failed, %s'%(secMountPoint, errmsg)) + + @staticmethod + def prepareOCFS2Nodes(clusterName, nodeString): + def compareClusterConfig(nodes): + def sortNodes(nodes): + ns = [] + for n in nodes: + ns.insert(int(n["number"]), n) + return ns + + def compareNodes(ns1, ns2): + if len(ns1) != len(ns2): + return False + + for i in range(0, len(ns1)): + n1 = ns1[i] + n2 = ns2[i] + if n1["ip_address"] != n2["ip_address"] or n1["number"] != n2["number"] \ + or n1["name"] != n2["name"]: + return False + return True + + if exists(OCFS2_CLUSTER_CONF): + oldConf = parse_ocfs2_cluster_conf() + cluster = oldConf["cluster"] + nodesNum = cluster["node_count"] + if len(nodes) != nodesNum: + return False + + new = sortNodes(nodes) + old = sortNodes(oldConf["nodes"]) + return compareNodes(new, old) + else: + return False + + def configureEtcHosts(nodes): + if not exists(ETC_HOSTS): + orignalConf = "" + else: + fd = open(ETC_HOSTS, "r") + orignalConf = fd.read() + fd.close() + + pattern = r"(.*%s.*)|(.*%s.*)" + newlines = [] + for n in nodes: + p = pattern % (n["ip_address"], n["name"]) + orignalConf = re.sub(p, "", orignalConf) + newlines.append("%s\t%s\n"%(n["ip_address"], n["name"])) + + orignalConf = orignalConf + "".join(newlines) + # remove extra empty lines + orignalConf = re.sub(r"\n\s*\n*", "\n", orignalConf) + logger.debug(OvmStoragePool.prepareOCFS2Nodes, "Configure /etc/hosts:%s\n"%orignalConf) + fd = open(ETC_HOSTS, "w") + fd.write(orignalConf) + fd.close() + + try: + nodeString = nodeString.strip(";") + nodes = [] + for n in nodeString.split(";"): + params = n.split(":") + if len(params) != 3: raise Exception("Wrong parameter(%s) in node string(%s)"%(n, nodeString)) + dict = {"number":params[0], "ip_address":params[1], "name":params[2]} + nodes.append(dict) + + if len(nodes) > 255: + raise Exception("%s nodes beyond maximum 255 allowed by OCFS2"%len(nodes)) + + if compareClusterConfig(nodes): + logger.debug(OvmStoragePool.prepareOCFS2Nodes, "Nodes configure are the same, return") + rs = SUCC() + return rs + + lines = [] + for n in nodes: + lines.append("node:\n") + lines.append("\tip_port = %s\n" % "7777") + lines.append("\tip_address = %s\n" % n["ip_address"]) + lines.append("\tnumber = %s\n" % n["number"]) + lines.append("\tname = %s\n" % n["name"]) + lines.append("\tcluster = %s\n" % clusterName) + lines.append("\n") + lines.append("cluster:\n") + lines.append("\tnode_count = %d\n" % len(nodes)) + lines.append("\tname = %s\n" % clusterName) + lines.append("\n") + conf = "".join(lines) + clusterm_set_ocfs2_cluster_conf(conf) + clusterm_start_o2cb_service() + logger.debug(OvmStoragePool.prepareOCFS2Nodes, "Configure cluster.conf to:\n%s"%conf) + configureEtcHosts(nodes) + rs = SUCC() + return rs + + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmStoragePool.prepareOCFS2Nodes, errmsg) + raise XmlRpcFault(toErrCode(OvmStoragePool, OvmStoragePool.prepareOCFS2Nodes), errmsg) + + + \ No newline at end of file diff --git a/ovm/scripts/vm/hypervisor/ovm/OvmVifModule.py b/ovm/scripts/vm/hypervisor/ovm/OvmVifModule.py new file mode 100644 index 00000000000..97b5db6d7f2 --- /dev/null +++ b/ovm/scripts/vm/hypervisor/ovm/OvmVifModule.py @@ -0,0 +1,50 @@ +''' +Created on May 17, 2011 + +@author: frank +''' +from OvmCommonModule import * + +class OvmVifDecoder(json.JSONDecoder): + def decode(self, jStr): + deDict = asciiLoads(jStr) + vif = OvmVif() + vif.mac = deDict['mac'] + vif.bridge = deDict['bridge'] + return vif + +class OvmVifEncoder(json.JSONEncoder): + def default(self, obj): + if not isinstance(obj, OvmVif): raise Exception("%s is not instance of OvmVif"%type(obj)) + dct = {} + safeDictSet(obj, dct, 'mac') + safeDictSet(obj, dct, 'bridge') + safeDictSet(obj, dct, 'type') + safeDictSet(obj, dct, 'name') + return dct + +def fromOvmVif(vif): + return normalizeToGson(json.dumps(vif, cls=OvmVifEncoder)) + +def fromOvmVifList(vifList): + return [fromOvmVif(v) for v in vifList] + +def toOvmVif(jStr): + return json.loads(jStr, cls=OvmVifDecoder) + +def toOvmVifList(jStr): + vifs = [] + for i in jStr: + vif = toOvmVif(i) + vifs.append(vif) + return vifs + +class OvmVif(OvmObject): + name = '' + mac = '' + bridge = '' + type = '' + mode = '' + + def toXenString(self): + return "%s,%s,%s"%(self.mac, self.bridge, self.type) diff --git a/ovm/scripts/vm/hypervisor/ovm/OvmVmModule.py b/ovm/scripts/vm/hypervisor/ovm/OvmVmModule.py new file mode 100644 index 00000000000..38ac5b56dbd --- /dev/null +++ b/ovm/scripts/vm/hypervisor/ovm/OvmVmModule.py @@ -0,0 +1,497 @@ +''' +Created on May 17, 2011 + +@author: frank +''' +from OvmCommonModule import * +from OvmDiskModule import * +from OvmVifModule import * +from OvmHostModule import OvmHost +from string import Template +from OVSXXenVMConfig import * +from OVSSiteVM import start_vm, stop_vm, reset_vm +from OVSSiteCluster import * +from OvmStoragePoolModule import OvmStoragePool +from OVSXXenStore import xen_get_vm_path, xen_get_vnc_port +from OVSDB import db_get_vm +from OVSXMonitor import xen_get_vm_perf_metrics, xen_get_xm_info +from OVSXXenVM import xen_migrate_vm +from OVSSiteRMVM import unregister_vm, register_vm +from OVSSiteVMInstall import install_vm_hvm +from OVSSiteRMServer import get_master_ip +from OVSXXenVMInstall import xen_change_vm_cdrom +from OVSXAPIUtil import XenAPIObject, session_login, session_logout + + +logger = OvmLogger("OvmVm") + +class OvmVmDecoder(json.JSONDecoder): + def decode(self, jStr): + deDict = asciiLoads(jStr) + vm = OvmVm() + setAttrFromDict(vm, 'cpuNum', deDict, int) + setAttrFromDict(vm, 'memory', deDict, long) + setattr(vm, 'rootDisk', toOvmDisk(deDict['rootDisk'])) + setattr(vm, 'vifs', toOvmVifList(deDict['vifs'])) + setattr(vm, 'disks', toOvmDiskList(deDict['disks'])) + setAttrFromDict(vm, 'name', deDict) + setAttrFromDict(vm, 'uuid', deDict) + setAttrFromDict(vm, 'bootDev', deDict) + setAttrFromDict(vm, 'type', deDict) + return vm + +class OvmVmEncoder(json.JSONEncoder): + def default(self, obj): + if not isinstance(obj, OvmVm): raise Exception("%s is not instance of OvmVm"%type(obj)) + dct = {} + safeDictSet(obj, dct, 'cpuNum') + safeDictSet(obj, dct, 'memory') + safeDictSet(obj, dct, 'powerState') + safeDictSet(obj, dct, 'name') + safeDictSet(obj, dct, 'type') + vifs = fromOvmVifList(obj.vifs) + dct['vifs'] = vifs + rootDisk = fromOvmDisk(obj.rootDisk) + dct['rootDisk'] = rootDisk + disks = fromOvmDiskList(obj.disks) + dct['disks'] = disks + return dct + +def toOvmVm(jStr): + return json.loads(jStr, cls=OvmVmDecoder) + +def fromOvmVm(vm): + return normalizeToGson(json.dumps(vm, cls=OvmVmEncoder)) + +class OvmVm(OvmObject): + cpuNum = 0 + memory = 0 + rootDisk = None + vifs = [] + disks = [] + powerState = '' + name = '' + bootDev = '' + type = '' + + def _getVifs(self, vmName): + vmPath = OvmHost()._vmNameToPath(vmName) + domId = OvmHost()._getDomainIdByName(vmName) + vifs = successToMap(xen_get_vifs(vmPath)) + lst = [] + for k in vifs: + v = vifs[k] + vifName = 'vif' + domId + '.' + k[len('vif'):] + vif = OvmVif() + (mac, bridge, type) = v.split(',') + safeSetAttr(vif, 'name', vifName) + safeSetAttr(vif, 'mac', mac) + safeSetAttr(vif, 'bridge', bridge) + safeSetAttr(vif, 'type', type) + lst.append(vif) + + return lst + + def _getVifsFromConfig(self, vmPath): + vifs = successToMap(xen_get_vifs(vmPath)) + lst = [] + for k in vifs: + v = vifs[k] + vif = OvmVif() + (mac, bridge, type) = v.split(',') + safeSetAttr(vif, 'name', k) + safeSetAttr(vif, 'mac', mac) + safeSetAttr(vif, 'bridge', bridge) + safeSetAttr(vif, 'type', type) + lst.append(vif) + return lst + + def _getIsoMountPath(self, vmPath): + vmName = basename(vmPath) + priStoragePath = vmPath.rstrip(join('running_pool', vmName)) + return join(priStoragePath, 'iso_pool', vmName) + + def _getVmTypeFromConfigFile(self, vmPath): + vmType = successToMap(xen_get_vm_type(vmPath))['type'] + return vmType.replace('hvm', 'HVM').replace('para', 'PV') + + @staticmethod + def create(jsonString): + def dumpCfg(vmName, cfgPath): + cfgFd = open(cfgPath, 'r') + cfg = cfgFd.readlines() + cfgFd.close() + logger.info(OvmVm.create, "Start %s with configure:\n\n%s\n"%(vmName, "".join(cfg))) + + def setVifsType(vifs, type): + for vif in vifs: + vif.type = type + + def hddBoot(vm, vmPath): + vmType = vm.type + if vmType == "FROMCONFIGFILE": + vmType = OvmVm()._getVmTypeFromConfigFile(vmPath) + + cfgDict = {} + if vmType == "HVM": + cfgDict['builder'] = "'hvm'" + cfgDict['acpi'] = "1" + cfgDict['apic'] = "1" + cfgDict['device_model'] = "'/usr/lib/xen/bin/qemu-dm'" + cfgDict['kernel'] = "'/usr/lib/xen/boot/hvmloader'" + vifType = 'ioemu' + else: + cfgDict['bootloader'] = "'/usr/bin/pygrub'" + vifType = 'netfront' + + cfgDict['name'] = "'%s'"%vm.name + cfgDict['disk'] = "[]" + cfgDict['vcpus'] = "''" + cfgDict['memory'] = "''" + cfgDict['on_crash'] = "'destroy'" + cfgDict['on_reboot'] = "'restart'" + cfgDict['vif'] = "[]" + + items = [] + for k in cfgDict.keys(): + item = " = ".join([k, cfgDict[k]]) + items.append(item) + vmSpec = "\n".join(items) + + vmCfg = open(join(vmPath, 'vm.cfg'), 'w') + vmCfg.write(vmSpec) + vmCfg.close() + + setVifsType(vm.vifs, vifType) + raiseExceptionIfFail(xen_set_vcpus(vmPath, vm.cpuNum)) + raiseExceptionIfFail(xen_set_memory(vmPath, BytesToM(vm.memory))) + raiseExceptionIfFail(xen_add_disk(vmPath, vm.rootDisk.path, mode=vm.rootDisk.type)) + vifs = [OvmVif.toXenString(v) for v in vm.vifs] + for vif in vifs: + raiseExceptionIfFail(xen_set_vifs(vmPath, vif)) + + for disk in vm.disks: + raiseExceptionIfFail(xen_add_disk(vmPath, disk.path, mode=disk.type)) + + raiseExceptionIfFail(xen_set_vm_vnc_password(vmPath, "")) + cfgFile = join(vmPath, 'vm.cfg') + # only HVM supports attaching cdrom + if vmType == 'HVM': + # Add an empty "hdc:cdrom" entry in config. Fisrt we set boot order to 'd' that is cdrom boot, + # then 'hdc:cdrom' entry will be in disk list. Second, change boot order to 'c' which + # is harddisk boot. VM can not start with an empty 'hdc:cdrom' when boot order is 'd'. + # it's tricky ! + raiseExceptionIfFail(xen_config_boot_sequence(vmPath, 'd')) + raiseExceptionIfFail(xen_config_boot_sequence(vmPath, 'c')) + + raiseExceptionIfFail(xen_correct_cfg(cfgFile, vmPath)) + xen_correct_qos_cfg(cfgFile) + dumpCfg(vm.name, cfgFile) + server = successToMap(get_master_ip())['ip'] + raiseExceptionIfFail(start_vm(vmPath, server)) + rs = SUCC() + return rs + + def cdBoot(vm, vmPath): + isoMountPath = None + try: + cdrom = None + for disk in vm.disks: + if disk.isIso == True: + cdrom = disk + break + if not cdrom: raise Exception("Cannot find Iso in disks") + + isoOnSecStorage = dirname(cdrom.path) + isoName = basename(cdrom.path) + isoMountPath = OvmVm()._getIsoMountPath(vmPath) + OvmStoragePool()._mount(isoOnSecStorage, isoMountPath) + isoPath = join(isoMountPath, isoName) + if not exists(isoPath): + raise Exception("Cannot found iso %s at %s which mounts to %s"%(isoName, isoOnSecStorage, isoMountPath)) + + stdout = run_cmd(args=['file', isoPath]) + if not stdout.strip().endswith("(bootable)"): raise Exception("ISO %s is not bootable"%cdrom.path) + + #now alter cdrom to correct path + cdrom.path = isoPath + if len(vm.vifs) != 0: + vif = vm.vifs[0] + #ISO boot must be HVM + vifCfg = ','.join([vif.mac, vif.bridge, 'ioemu']) + else: + vifCfg = '' + + rootDiskSize = os.path.getsize(vm.rootDisk.path) + rooDiskCfg = ':'.join([join(vmPath, basename(vm.rootDisk.path)), str(BytesToG(rootDiskSize)), 'True']) + disks = [rooDiskCfg] + for d in vm.disks: + if d.isIso: continue + size = os.path.getsize(d.path) + cfg = ':'.join([d.path, str(BytesToG(size)), 'True']) + disks.append(cfg) + disksCfg = ','.join(disks) + server = successToMap(get_master_ip())['ip'] + + raiseExceptionIfFail(install_vm_hvm(vmPath, BytesToM(vm.memory), vm.cpuNum, vifCfg, disksCfg, cdrom.path, vncpassword='', dedicated_server=server)) + rs = SUCC() + return rs + except Exception, e: + if isoMountPath and OvmStoragePool()._isMounted(isoMountPath): + doCmd(['umount', '-f', isoMountPath]) + errmsg = fmt_err_msg(e) + raise Exception(errmsg) + + try: + vm = toOvmVm(jsonString) + logger.debug(OvmVm.create, "creating vm, spec:%s"%jsonString) + rootDiskPath = vm.rootDisk.path + if not exists(rootDiskPath): raise Exception("Cannot find root disk %s"%rootDiskPath) + + rootDiskDir = dirname(rootDiskPath) + vmPath = join(dirname(rootDiskDir), vm.name) + if not exists(vmPath): + doCmd(['ln', '-s', rootDiskDir, vmPath]) + vmNameFile = open(join(rootDiskDir, 'vmName'), 'w') + vmNameFile.write(vm.name) + vmNameFile.close() + + if vm.bootDev == "HDD": + return hddBoot(vm, vmPath) + elif vm.bootDev == "CD": + return cdBoot(vm, vmPath) + else: + raise Exception("Unkown bootdev %s for %s"%(vm.bootDev, vm.name)) + + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmVm.create, errmsg) + raise XmlRpcFault(toErrCode(OvmVm, OvmVm.create), errmsg) + + @staticmethod + def stop(vmName): + try: + try: + OvmHost()._getDomainIdByName(vmName) + except NoVmFoundException, e: + logger.info(OvmVm.stop, "vm %s is already stopped"%vmName) + return SUCC() + + logger.info(OvmVm.stop, "Stop vm %s"%vmName) + vmPath = OvmHost()._vmNameToPath(vmName) + raiseExceptionIfFail(stop_vm(vmPath)) + return SUCC() + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmVm.stop, errmsg) + raise XmlRpcFault(toErrCode(OvmVm, OvmVm.stop), errmsg) + + @staticmethod + def reboot(vmName): + try: + #=================================================================== + # Xend has a bug of reboot. If reboot vm too quick, xend return success + # but actually it refused reboot (seen from log) + # vmPath = successToMap(xen_get_vm_path(vmName))['path'] + # raiseExceptionIfFail(reset_vm(vmPath)) + #=================================================================== + vmPath = OvmHost()._vmNameToPath(vmName) + OvmVm.stop(vmName) + raiseExceptionIfFail(start_vm(vmPath)) + vncPort= successToMap(xen_get_vnc_port(vmName))['vnc_port'] + logger.info(OvmVm.stop, "reboot vm %s, new vncPort is %s"%(vmName, vncPort)) + return toGson({"vncPort":str(vncPort)}) + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmVm.reboot, errmsg) + raise XmlRpcFault(toErrCode(OvmVm, OvmVm.reboot), errmsg) + + @staticmethod + def getDetails(vmName): + try: + vm = OvmVm() + + try: + OvmHost()._getDomainIdByName(vmName) + vmPath = OvmHost()._vmNameToPath(vmName) + vifsFromConfig = False + except NoVmFoundException, e: + vmPath = OvmHost()._getVmPathFromPrimaryStorage(vmName) + vifsFromConfig = True + + + if not isdir(vmPath): + # The case is, when vm starting was not completed at primaryStroageDownload or createVolume(e.g. mgmt server stop), the mgmt + # server will keep vm state in staring, then a stop command will be sent. The stop command will delete bridges that vm attaches, + # by retriving birdge info by OvmVm.getDetails(). In this case, the vm doesn't exists, so returns a fake object here. + fakeDisk = OvmDisk() + vm.rootDisk = fakeDisk + else: + if vifsFromConfig: + vm.vifs.extend(vm._getVifsFromConfig(vmPath)) + else: + vm.vifs.extend(vm._getVifs(vmName)) + + safeSetAttr(vm, 'name', vmName) + disks = successToMap(xen_get_vdisks(vmPath))['vdisks'].split(',') + rootDisk = None + #BUG: there is no way to get type of disk, assume all are "w" + for d in disks: + if vmName in d: + rootDisk = OvmDisk() + safeSetAttr(rootDisk, 'path', d) + safeSetAttr(rootDisk, 'type', "w") + continue + disk = OvmDisk() + safeSetAttr(disk, 'path', d) + safeSetAttr(disk, 'type', "w") + vm.disks.append(disk) + if not rootDisk: raise Exception("Cannot find root disk for vm %s"%vmName) + safeSetAttr(vm, 'rootDisk', rootDisk) + vcpus = int(successToMap(xen_get_vcpus(vmPath))['vcpus']) + safeSetAttr(vm, 'cpuNum', vcpus) + memory = MtoBytes(int(successToMap(xen_get_memory(vmPath))['memory'])) + safeSetAttr(vm, 'memory', memory) + vmStatus = db_get_vm(vmPath) + safeSetAttr(vm, 'powerState', vmStatus['status']) + vmType = successToMap(xen_get_vm_type(vmPath))['type'].replace('hvm', 'HVM').replace('para', 'PV') + safeSetAttr(vm, 'type', vmType) + + rs = fromOvmVm(vm) + logger.info(OvmVm.getDetails, rs) + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmVm.getDetails, errmsg) + raise XmlRpcFault(toErrCode(OvmVm, OvmVm.getDetails), errmsg) + + @staticmethod + def getVmStats(vmName): + def getVcpuNumAndUtils(): + try: + session = session_login() + refs = session.xenapi.VM.get_by_name_label(vmName) + if len(refs) == 0: + raise Exception("No ref for %s found in xenapi VM objects"%vmName) + vm = XenAPIObject('VM', session, refs[0]) + VM_metrics = XenAPIObject("VM_metrics", session, vm.get_metrics()) + items = VM_metrics.get_VCPUs_utilisation().items() + nvCpus = len(items) + if nvCpus == 0: + raise Exception("vm %s has 0 vcpus !!!"%vmName) + + xmInfo = successToMap(xen_get_xm_info()) + nCpus = int(xmInfo['nr_cpus']) + totalUtils = 0.0 + # CPU utlization of VM = (total cpu utilization of each vcpu) / number of physical cpu + for num, util in items: + totalUtils += float(util) + avgUtils = float(totalUtils/nCpus) * 100 + return (nvCpus, avgUtils) + finally: + session_logout() + + + try: + try: + OvmHost()._getDomainIdByName(vmName) + vmPath = OvmHost()._vmNameToPath(vmName) + (nvcpus, avgUtils) = getVcpuNumAndUtils() + vifs = successToMap(xen_get_vifs(vmPath)) + rxBytes = 0 + txBytes = 0 + vifs = OvmVm()._getVifs(vmName) + for vif in vifs: + rxp = join('/sys/class/net', vif.name, 'statistics/rx_bytes') + txp = join("/sys/class/net/", vif.name, "statistics/tx_bytes") + if not exists(rxp): raise Exception('can not find %s'%rxp) + if not exists(txp): raise Exception('can not find %s'%txp) + rxBytes += long(doCmd(['cat', rxp])) / 1000 + txBytes += long(doCmd(['cat', txp])) / 1000 + except NoVmFoundException, e: + vmPath = OvmHost()._getVmPathFromPrimaryStorage(vmName) + nvcpus = int(successToMap(xen_get_vcpus(vmPath))['vcpus']) + avgUtils = 0 + rxBytes = 0 + txBytes = 0 + + rs = toGson({"cpuNum":nvcpus, "cpuUtil":avgUtils, "rxBytes":rxBytes, "txBytes":txBytes}) + logger.debug(OvmVm.getVmStats, rs) + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmVm.getVmStats, errmsg) + raise XmlRpcFault(toErrCode(OvmVm, OvmVm.getVmStats), errmsg) + + @staticmethod + def migrate(vmName, targetHost): + try: + vmPath = OvmHost()._vmNameToPath(vmName) + raiseExceptionIfFail(xen_migrate_vm(vmPath, targetHost)) + unregister_vm(vmPath) + return SUCC() + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmVm.migrate, errmsg) + raise XmlRpcFault(toErrCode(OvmVm, OvmVm.migrate), errmsg) + + @staticmethod + def register(vmName): + try: + vmPath = OvmHost()._vmNameToPath(vmName) + raiseExceptionIfFail(register_vm(vmPath)) + vncPort= successToMap(xen_get_vnc_port(vmName))['vnc_port'] + rs = toGson({"vncPort":str(vncPort)}) + logger.debug(OvmVm.register, rs) + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmVm.register, errmsg) + raise XmlRpcFault(toErrCode(OvmVm, OvmVm.register), errmsg) + + @staticmethod + def getVncPort(vmName): + try: + vncPort= successToMap(xen_get_vnc_port(vmName))['vnc_port'] + rs = toGson({"vncPort":vncPort}) + logger.debug(OvmVm.getVncPort, rs) + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmVm.getVncPort, errmsg) + raise XmlRpcFault(toErrCode(OvmVm, OvmVm.getVncPort), errmsg) + + @staticmethod + def detachOrAttachIso(vmName, iso, isAttach): + try: + if vmName in OvmHost.getAllVms(): + scope = 'both' + vmPath = OvmHost()._vmNameToPath(vmName) + else: + scope = 'cfg' + vmPath = OvmHost()._getVmPathFromPrimaryStorage(vmName) + + vmType = OvmVm()._getVmTypeFromConfigFile(vmPath) + if vmType != 'HVM': + raise Exception("Only HVM supports attaching/detaching ISO") + + if not isAttach: + iso = '' + else: + isoName = basename(iso) + isoMountPoint = OvmVm()._getIsoMountPath(vmPath) + isoOnSecStorage = dirname(iso) + OvmStoragePool()._mount(isoOnSecStorage, isoMountPoint) + iso = join(isoMountPoint, isoName) + + exceptionIfNoSuccess(xen_change_vm_cdrom(vmPath, iso, scope)) + return SUCC() + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmVm.detachOrAttachIso, errmsg) + raise XmlRpcFault(toErrCode(OvmVm, OvmVm.detachOrAttachIso), errmsg) + +if __name__ == "__main__": + import sys + print OvmVm.getDetails(sys.argv[1]) + #print OvmVm.getVmStats(sys.argv[1]) \ No newline at end of file diff --git a/ovm/scripts/vm/hypervisor/ovm/OvmVolumeModule.py b/ovm/scripts/vm/hypervisor/ovm/OvmVolumeModule.py new file mode 100644 index 00000000000..1ac987d1ceb --- /dev/null +++ b/ovm/scripts/vm/hypervisor/ovm/OvmVolumeModule.py @@ -0,0 +1,144 @@ +''' +Created on June 2, 2011 + +@author: frank +''' +from OvmCommonModule import * +from OvmStoragePoolModule import OvmStoragePool +from OVSXUtility import xen_create_disk +from OvmHostModule import OvmHost +import os + +logger = OvmLogger("OvmVolume") + +class OvmVolumeDecoder(json.JSONDecoder): + def decode(self, jStr): + deDict = asciiLoads(jStr) + vol = OvmVolume() + setAttrFromDict(vol, 'uuid', deDict) + setAttrFromDict(vol, 'size', deDict, long) + setAttrFromDict(vol, 'poolUuid', deDict) + return vol + +class OvmVolumeEncoder(json.JSONEncoder): + def default(self, obj): + if not isinstance(obj, OvmVolume): raise Exception("%s is not instance of OvmVolume"%type(obj)) + dct = {} + safeDictSet(obj, dct, 'name') + safeDictSet(obj, dct, 'uuid') + safeDictSet(obj, dct, 'poolUuid') + safeDictSet(obj, dct, 'path') + safeDictSet(obj, dct, 'size') + return dct + +def toOvmVolume(jStr): + return json.loads(jStr, cls=OvmVolumeDecoder) + +def fromOvmVlolume(vol): + return normalizeToGson(json.dumps(vol, cls=OvmVolumeEncoder)) + +class OvmVolume(OvmObject): + name = '' + uuid = '' + poolUuid = '' + path = '' + size = 0 + + @staticmethod + def createDataDisk(poolUuid, size, isRoot): + try: + vol = OvmVolume() + vol.size = long(size) + vol.poolUuid = poolUuid + pool = OvmStoragePool() + sr = pool._getSrByNameLable(vol.poolUuid) + if isRoot: + path = join(sr.mountpoint, 'running_pool', get_uuid()) + else: + path = join(sr.mountpoint, 'shareDisk') + if not exists(path): os.makedirs(path) + freeSpace = pool._getSpaceinfoOfDir(path) + if freeSpace < vol.size: + raise Exception("%s has not enough space (available:%s, required:%s"%(path, freeSpace, vol.size)) + + vol.uuid = get_uuid() + vol.name = vol.uuid + '.raw' + filePath = join(path, vol.name) + exceptionIfNoSuccess(xen_create_disk(filePath, BytesToM(vol.size)), "Create datadisk %s failed"%filePath) + vol.path = filePath + rs = fromOvmVlolume(vol) + logger.debug(OvmVolume.createDataDisk, rs) + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmVolume.createDataDisk, errmsg) + raise XmlRpcFault(toErrCode(OvmVolume, OvmVolume.createDataDisk, errmsg)) + + @staticmethod + def createFromTemplate(poolUuid, templateUrl): + try: + if not exists(templateUrl): + raise Exception("Cannot find template:%s"%templateUrl) + sr = OvmStoragePool()._getSrByNameLable(poolUuid) + volDirUuid = get_uuid() + volUuid = get_uuid() + priStorageMountPoint = sr.mountpoint + volDir = join(priStorageMountPoint, 'running_pool', volDirUuid) + if exists(volDir): + raise Exception("Volume dir %s alreay existed, can not override"%volDir) + os.makedirs(volDir) + OvmStoragePool()._checkDirSizeForImage(volDir, templateUrl) + volName = volUuid + '.raw' + tgt = join(volDir, volName) + cpVolCmd = ['cp', templateUrl, tgt] + doCmd(cpVolCmd) + volSize = os.path.getsize(tgt) + vol = OvmVolume() + vol.name = volName + vol.path = tgt + vol.size = volSize + vol.uuid = volUuid + vol.poolUuid = poolUuid + rs = fromOvmVlolume(vol) + logger.debug(OvmVolume.createFromTemplate, rs) + return rs + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmVolume.createFromTemplate, errmsg) + raise XmlRpcFault(toErrCode(OvmVolume, OvmVolume.createFromTemplate), errmsg) + + @staticmethod + def destroy(poolUuid, path): + try: + OvmStoragePool()._getSrByNameLable(poolUuid) + if not exists(path): raise Exception("Cannot find %s"%path) + dir = dirname(path) + if exists(join(dir, 'vm.cfg')): + # delete root disk + vmNamePath = join(dir, 'vmName') + if exists(vmNamePath): + vmNameFd = open(vmNamePath, 'r') + vmName = vmNameFd.readline() + vmName = vmName.rstrip('\n') + link = join(dirname(dir), vmName) + doCmd(['rm', '-rf', link]) + vmNameFd.close() + else: + logger.warning(OvmVolume.destroy, "Can not find vmName file in %s"%dir) + doCmd(['rm','-rf', dir]) + else: + doCmd(['rm', path]) + return SUCC() + except Exception, e: + errmsg = fmt_err_msg(e) + logger.error(OvmVolume.destroy, errmsg) + raise XmlRpcFault(toErrCode(OvmVolume, OvmVolume.destroy), errmsg) + + + +if __name__ == "__main__": + print OvmVolume.detachOrAttachIso(sys.argv[1], '', False) + + + + \ No newline at end of file diff --git a/ovm/scripts/vm/hypervisor/ovm/configureOvm.sh b/ovm/scripts/vm/hypervisor/ovm/configureOvm.sh new file mode 100755 index 00000000000..2ad45ead698 --- /dev/null +++ b/ovm/scripts/vm/hypervisor/ovm/configureOvm.sh @@ -0,0 +1,112 @@ +#!/bin/sh + +errExit() { + echo $@ + exit 1 +} + +stopHeartbeat() { + pidFile="/var/run/ovs-agent/heartbeat.pid" + if [ -f $pidFile ]; then + pid=`cat $pidFile` + ps -p $pid &>/dev/null + if [ $? -eq 0 ]; then + kill $pid &>/dev/null + fi + fi +} + +openPortOnIptables() { + port="$1" + protocol="$2" + chkconfig --list iptables | grep "on" + if [ $? -eq 0 ]; then + iptables-save | grep "A INPUT -p $protocol -m $protocol --dport $port -j ACCEPT" >/dev/null + if [ $? -ne 0 ]; then + iptables -I INPUT 1 -p $protocol --dport $port -j ACCEPT + if [ $? -ne 0 ]; then + exit_with_error "iptables -I INPUT 1 -p $protocol --dport $port -j ACCEPT failed" + fi + echo "iptables:Open $protocol port $port for DHCP" + fi + fi +} + +applyPatch() { + patchFile="$1" + level="$2" + + [ ! -f $patchFile ] && errExit "Can not find $patchFile" + + if [ $? -ne 0 ]; then + pushd /opt/ovs-agent-latest &>/dev/null + test=`patch -p$level --dry-run -N < $patchFile` + if [ $? -ne 0 ]; then + tmp=`mktemp` + echo $test > $tmp + grep "Reversed (or previously applied) patch detected" $tmp &>/dev/null + if [ $? -eq 0 ]; then + # The file has been patched + rm $tmp -f + popd &>/dev/null + return + else + rm $tmp -f + popd &>/dev/null + errExit "Can not apply $patchFile beacuse $test" + fi + fi + patch -p$level < $patchFile + [ $? -ne 0 ] && errExit "Patch to $target failed" + popd &>/dev/null + fi +} + +postSetup() { + openPortOnIptables 7777 tcp + openPortOnIptables 7777 udp + applyPatch "/opt/ovs-agent-latest/OvmPatch.patch" 2 + applyPatch "/opt/ovs-agent-latest/OvmDontTouchOCFS2ClusterWhenAgentStart.patch" 1 + + stopHeartbeat + + /etc/init.d/ovs-agent restart --disable-nowayout + [ $? -ne 0 ] && errExit "Restart ovs agent failed" + exit 0 +} + +preSetup() { + agentConfig="/etc/ovs-agent/agent.ini" + agentInitScript="/etc/init.d/ovs-agent" + + [ ! -f $agentConfig ] && errExit "Can not find $agentConfig" + [ ! -f $agentInitScript ] && errExit "Can not find $agentInitScript" + + version=`grep "version=" $agentInitScript | cut -d "=" -f 2` + [ x"$version" != x"2.3" ] && errExit "The OVS agent version is $version, we only support 2.3 now" + + # disable SSL + sed -i 's/ssl=enable/ssl=disable/g' $agentConfig + [ $? -ne 0 ] && errExit "configure ovs agent to non ssl failed" + + if [ ! -L /opt/ovs-agent-latest ]; then + eval $agentInitScript status | grep 'down' && $agentInitScript start + [ $? -ne 0 ] && errExit "Start ovs agent failed" + [ ! -L /opt/ovs-agent-latest ] && errExit "No link at /opt/ovs-agent-latest" + fi + exit 0 +} + +[ $# -ne 1 ] && errExit "Usage: configureOvm.sh command" + +case "$1" in + preSetup) + preSetup + ;; + postSetup) + postSetup + ;; + *) + errExit "Valid commands: preSetup postSetup" +esac + diff --git a/ovm/src/com/cloud/ovm/hypervisor/OvmDiscoverer.java b/ovm/src/com/cloud/ovm/hypervisor/OvmDiscoverer.java new file mode 100755 index 00000000000..e505dfdba40 --- /dev/null +++ b/ovm/src/com/cloud/ovm/hypervisor/OvmDiscoverer.java @@ -0,0 +1,166 @@ +package com.cloud.ovm.hypervisor; + +import java.net.InetAddress; +import java.net.URI; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; +import org.apache.xmlrpc.XmlRpcException; + +import com.cloud.dc.ClusterVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.exception.DiscoveryException; +import com.cloud.host.HostInfo; +import com.cloud.host.HostVO; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.ovm.object.Connection; +import com.cloud.ovm.object.OvmHost; +import com.cloud.resource.Discoverer; +import com.cloud.resource.DiscovererBase; +import com.cloud.resource.ServerResource; +import com.cloud.utils.component.Inject; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.ssh.SSHCmdHelper; + +@Local(value=Discoverer.class) +public class OvmDiscoverer extends DiscovererBase implements Discoverer { + private static final Logger s_logger = Logger.getLogger(OvmDiscoverer.class); + + @Inject ClusterDao _clusterDao; + + protected OvmDiscoverer() { + + } + + @Override + public Map> find(long dcId, + Long podId, Long clusterId, URI url, String username, + String password, List hostTags) throws DiscoveryException { + Connection conn = null; + + if (!url.getScheme().equals("http")) { + String msg = "urlString is not http so we're not taking care of the discovery for this: " + url; + s_logger.debug(msg); + return null; + } + if (clusterId == null) { + String msg = "must specify cluster Id when add host"; + s_logger.debug(msg); + throw new CloudRuntimeException(msg); + } + + if (podId == null) { + String msg = "must specify pod Id when add host"; + s_logger.debug(msg); + throw new CloudRuntimeException(msg); + } + + ClusterVO cluster = _clusterDao.findById(clusterId); + if(cluster == null || (cluster.getHypervisorType() != HypervisorType.Ovm)) { + if(s_logger.isInfoEnabled()) + s_logger.info("invalid cluster id or cluster is not for Ovm hypervisors"); + return null; + } + + String agentUsername = _params.get("agentusername"); + if (agentUsername == null) { + throw new CloudRuntimeException("Agent user name must be specified"); + } + + String agentPassword = _params.get("agentpassword"); + if (agentPassword == null) { + throw new CloudRuntimeException("Agent password must be specified"); + } + + try { + String hostname = url.getHost(); + InetAddress ia = InetAddress.getByName(hostname); + String hostIp = ia.getHostAddress(); + String guid = UUID.nameUUIDFromBytes(hostIp.getBytes()).toString(); + + + ClusterVO clu = _clusterDao.findById(clusterId); + if (clu.getGuid() == null) { + clu.setGuid("/root"); + _clusterDao.update(clusterId, clu); + } + + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(hostIp, 22); + sshConnection.connect(null, 60000, 60000); + sshConnection = SSHCmdHelper.acquireAuthorizedConnection(hostIp, username, password); + if (sshConnection == null) { + throw new DiscoveryException( + String.format("Cannot connect to ovm host(IP=%1$s, username=%2$s, password=%3$s, discover failed", hostIp, username, password)); + } + + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, "[ -f '/etc/ovs-agent/agent.ini' ]")) { + throw new DiscoveryException("Can not find /etc/ovs-agent/agent.ini " + hostIp); + } + + Map details = new HashMap(); + OvmResourceBase ovmResource = new OvmResourceBase(); + details.put("ip", hostIp); + details.put("username", username); + details.put("password", password); + details.put("zone", Long.toString(dcId)); + details.put("guid", guid); + details.put("pod", Long.toString(podId)); + details.put("cluster", Long.toString(clusterId)); + details.put("agentusername", agentUsername); + details.put("agentpassword", agentPassword); + + Map params = new HashMap(); + params.putAll(details); + ovmResource.configure("Ovm Server", params); + ovmResource.start(); + + conn = new Connection(hostIp, "oracle", agentPassword); + /* After resource start, we are able to execute our agent api*/ + OvmHost.Details d = OvmHost.getDetails(conn); + details.put("agentVersion", d.agentVersion); + details.put(HostInfo.HOST_OS_KERNEL_VERSION, d.dom0KernelVersion); + details.put(HostInfo.HYPERVISOR_VERSION, d.hypervisorVersion); + + Map> resources = new HashMap>(); + resources.put(ovmResource, details); + return resources; + } catch (XmlRpcException e) { + s_logger.debug("XmlRpc exception, Unable to discover OVM: " + url, e); + return null; + } catch (UnknownHostException e) { + s_logger.debug("Host name resolve failed exception, Unable to discover OVM: " + url, e); + return null; + } catch (ConfigurationException e) { + s_logger.debug("Configure resource failed, Unable to discover OVM: " + url, e); + return null; + } catch (Exception e) { + s_logger.debug("Unable to discover OVM: " + url, e); + return null; + } + } + + @Override + public void postDiscovery(List hosts, long msId) + throws DiscoveryException { + // TODO Auto-generated method stub + + } + + @Override + public boolean matchHypervisor(String hypervisor) { + return HypervisorType.Ovm.toString().equalsIgnoreCase(hypervisor); + } + + @Override + public HypervisorType getHypervisorType() { + return HypervisorType.Ovm; + } + +} diff --git a/ovm/src/com/cloud/ovm/hypervisor/OvmGuru.java b/ovm/src/com/cloud/ovm/hypervisor/OvmGuru.java new file mode 100755 index 00000000000..b4d7d6bc478 --- /dev/null +++ b/ovm/src/com/cloud/ovm/hypervisor/OvmGuru.java @@ -0,0 +1,45 @@ +package com.cloud.ovm.hypervisor; + +import javax.ejb.Local; + +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.hypervisor.HypervisorGuru; +import com.cloud.hypervisor.HypervisorGuruBase; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.storage.GuestOSVO; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.utils.component.Inject; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value=HypervisorGuru.class) +public class OvmGuru extends HypervisorGuruBase implements HypervisorGuru { + @Inject GuestOSDao _guestOsDao; + protected OvmGuru() { + super(); + } + + @Override + public HypervisorType getHypervisorType() { + return HypervisorType.Ovm; + } + + @Override + public VirtualMachineTO implement( + VirtualMachineProfile vm) { + VirtualMachineTO to = toVirtualMachineTO(vm); + to.setBootloader(vm.getBootLoaderType()); + + // Determine the VM's OS description + GuestOSVO guestOS = _guestOsDao.findById(vm.getVirtualMachine().getGuestOSId()); + to.setOs(guestOS.getDisplayName()); + + return to; + } + + @Override + public boolean trackVmHostChange() { + return true; + } + +} diff --git a/ovm/src/com/cloud/ovm/hypervisor/OvmHelper.java b/ovm/src/com/cloud/ovm/hypervisor/OvmHelper.java new file mode 100755 index 00000000000..c9af2b83dbc --- /dev/null +++ b/ovm/src/com/cloud/ovm/hypervisor/OvmHelper.java @@ -0,0 +1,42 @@ +package com.cloud.ovm.hypervisor; + +import java.util.HashMap; + +public class OvmHelper { + private static final HashMap _ovmMap = new HashMap(); + + public static final String ORACLE_LINUX = "Oracle Linux"; + public static final String WINDOWS = "Windows"; + + static { + _ovmMap.put("Oracle Enterprise Linux 6.0 (32-bit)", ORACLE_LINUX); + _ovmMap.put("Oracle Enterprise Linux 6.0 (64-bit)", ORACLE_LINUX); + _ovmMap.put("Oracle Enterprise Linux 5.0 (32-bit)", ORACLE_LINUX); + _ovmMap.put("Oracle Enterprise Linux 5.0 (64-bit)", ORACLE_LINUX); + _ovmMap.put("Oracle Enterprise Linux 5.1 (32-bit)", ORACLE_LINUX); + _ovmMap.put("Oracle Enterprise Linux 5.1 (64-bit)", ORACLE_LINUX); + _ovmMap.put("Oracle Enterprise Linux 5.2 (32-bit)", ORACLE_LINUX); + _ovmMap.put("Oracle Enterprise Linux 5.2 (64-bit)", ORACLE_LINUX); + _ovmMap.put("Oracle Enterprise Linux 5.3 (32-bit)", ORACLE_LINUX); + _ovmMap.put("Oracle Enterprise Linux 5.3 (64-bit)", ORACLE_LINUX); + _ovmMap.put("Oracle Enterprise Linux 5.4 (32-bit)", ORACLE_LINUX); + _ovmMap.put("Oracle Enterprise Linux 5.4 (64-bit)", ORACLE_LINUX); + _ovmMap.put("Oracle Enterprise Linux 5.5 (32-bit)", ORACLE_LINUX); + _ovmMap.put("Oracle Enterprise Linux 5.5 (64-bit)", ORACLE_LINUX); + _ovmMap.put("Windows 7 (32-bit)", WINDOWS); + _ovmMap.put("Windows 7 (64-bit)", WINDOWS); + _ovmMap.put("Windows Server 2003 (32-bit)", WINDOWS); + _ovmMap.put("Windows Server 2003 (64-bit)", WINDOWS); + _ovmMap.put("Windows Server 2008 (32-bit)", WINDOWS); + _ovmMap.put("Windows Server 2008 (64-bit)", WINDOWS); + _ovmMap.put("Windows Server 2008 R2 (64-bit)", WINDOWS); + _ovmMap.put("Windows 2000 SP4 (32-bit)", WINDOWS); + _ovmMap.put("Windows Vista (32-bit)", WINDOWS); + _ovmMap.put("Windows XP SP2 (32-bit)", WINDOWS); + _ovmMap.put("Windows XP SP3 (32-bit)", WINDOWS); + } + + public static String getOvmGuestType(String stdType) { + return _ovmMap.get(stdType); + } +} diff --git a/ovm/src/com/cloud/ovm/hypervisor/OvmResourceBase.java b/ovm/src/com/cloud/ovm/hypervisor/OvmResourceBase.java new file mode 100755 index 00000000000..f3c1bfebc9b --- /dev/null +++ b/ovm/src/com/cloud/ovm/hypervisor/OvmResourceBase.java @@ -0,0 +1,1235 @@ +package com.cloud.ovm.hypervisor; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; +import org.apache.xmlrpc.XmlRpcException; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.AttachIsoCommand; +import com.cloud.agent.api.AttachVolumeAnswer; +import com.cloud.agent.api.AttachVolumeCommand; +import com.cloud.agent.api.CheckVirtualMachineAnswer; +import com.cloud.agent.api.CheckVirtualMachineCommand; +import com.cloud.agent.api.CleanupNetworkRulesCmd; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.CreateStoragePoolCommand; +import com.cloud.agent.api.FenceAnswer; +import com.cloud.agent.api.FenceCommand; +import com.cloud.agent.api.GetHostStatsAnswer; +import com.cloud.agent.api.GetHostStatsCommand; +import com.cloud.agent.api.GetStorageStatsAnswer; +import com.cloud.agent.api.GetStorageStatsCommand; +import com.cloud.agent.api.GetVmStatsAnswer; +import com.cloud.agent.api.GetVmStatsCommand; +import com.cloud.agent.api.GetVncPortAnswer; +import com.cloud.agent.api.GetVncPortCommand; +import com.cloud.agent.api.HostStatsEntry; +import com.cloud.agent.api.MaintainAnswer; +import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.MigrateAnswer; +import com.cloud.agent.api.MigrateCommand; +import com.cloud.agent.api.ModifyStoragePoolAnswer; +import com.cloud.agent.api.ModifyStoragePoolCommand; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.PingTestCommand; +import com.cloud.agent.api.PrepareForMigrationAnswer; +import com.cloud.agent.api.PrepareForMigrationCommand; +import com.cloud.agent.api.PrepareOCFS2NodesCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.RebootAnswer; +import com.cloud.agent.api.RebootCommand; +import com.cloud.agent.api.SecurityIngressRuleAnswer; +import com.cloud.agent.api.SecurityIngressRulesCmd; +import com.cloud.agent.api.StartAnswer; +import com.cloud.agent.api.StartCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.agent.api.StopAnswer; +import com.cloud.agent.api.StopCommand; +import com.cloud.agent.api.VmStatsEntry; +import com.cloud.agent.api.storage.CreateAnswer; +import com.cloud.agent.api.storage.CreateCommand; +import com.cloud.agent.api.storage.DestroyCommand; +import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; +import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; +import com.cloud.agent.api.to.NicTO; +import com.cloud.agent.api.to.StorageFilerTO; +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.agent.api.to.VolumeTO; +import com.cloud.host.Host.Type; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.Networks.TrafficType; +import com.cloud.ovm.object.Connection; +import com.cloud.ovm.object.OvmBridge; +import com.cloud.ovm.object.OvmDisk; +import com.cloud.ovm.object.OvmHost; +import com.cloud.ovm.object.OvmSecurityGroup; +import com.cloud.ovm.object.OvmStoragePool; +import com.cloud.ovm.object.OvmVif; +import com.cloud.ovm.object.OvmVlan; +import com.cloud.ovm.object.OvmVm; +import com.cloud.ovm.object.OvmVolume; +import com.cloud.resource.ServerResource; +import com.cloud.resource.hypervisor.HypervisorResource; +import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.storage.Volume; +import com.cloud.storage.template.TemplateInfo; +import com.cloud.template.VirtualMachineTemplate.BootloaderType; +import com.cloud.utils.Pair; +import com.cloud.utils.Ternary; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; +import com.cloud.utils.ssh.SSHCmdHelper; +import com.cloud.vm.DiskProfile; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.State; +import com.trilead.ssh2.SCPClient; + +public class OvmResourceBase implements ServerResource, HypervisorResource { + private static final Logger s_logger = Logger.getLogger(OvmResourceBase.class); + String _name; + Long _zoneId; + Long _podId; + Long _clusterId; + String _ip; + String _username; + String _password; + String _guid; + String _agentUserName = "oracle"; + String _agentPassword; + Connection _conn; + String _privateNetworkName; + String _publicNetworkName; + String _guestNetworkName; + boolean _canBridgeFirewall; + static boolean _isHeartBeat = false; + protected HashMap _vms = new HashMap(50); + static HashMap _stateMaps; + private final Map> _vmNetworkStats= new ConcurrentHashMap>(); + private static String _ovsAgentPath = "/opt/ovs-agent-latest"; + + static { + _stateMaps = new HashMap(); + _stateMaps.put("RUNNING", State.Running); + _stateMaps.put("DOWN", State.Stopped); + _stateMaps.put("ERROR", State.Error); + _stateMaps.put("SUSPEND", State.Stopped); + } + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + _name = name; + + try { + _zoneId = Long.parseLong((String) params.get("zone")); + _podId = Long.parseLong((String) params.get("pod")); + _clusterId = Long.parseLong((String) params.get("cluster")); + _ip = (String) params.get("ip"); + _username = (String) params.get("username"); + _password = (String) params.get("password"); + _guid = (String) params.get("guid"); + _privateNetworkName = (String) params.get("private.network.device"); + _publicNetworkName = (String) params.get("public.network.device"); + _guestNetworkName = (String)params.get("guest.network.device"); + _agentUserName = (String)params.get("agentusername"); + _agentPassword = (String)params.get("agentpassword"); + } catch (Exception e) { + s_logger.debug("Configure " + _name + " failed", e); + throw new ConfigurationException("Configure " + _name + " failed, " + e.toString()); + } + + if (_podId == null) { + throw new ConfigurationException("Unable to get the pod"); + } + + if (_ip == null) { + throw new ConfigurationException("Unable to get the host address"); + } + + if (_username == null) { + throw new ConfigurationException("Unable to get the username"); + } + + if (_password == null) { + throw new ConfigurationException("Unable to get the password"); + } + + if (_guid == null) { + throw new ConfigurationException("Unable to get the guid"); + } + + if (_agentUserName == null) { + throw new ConfigurationException("Unable to get agent user name"); + } + + if (_agentPassword == null) { + throw new ConfigurationException("Unable to get agent password"); + } + + try { + setupServer(); + } catch (Exception e) { + s_logger.debug("Setup server failed, ip " + _ip, e); + throw new ConfigurationException("Unable to setup server"); + } + + _conn = new Connection(_ip, _agentUserName, _agentPassword); + List bridges = null; + try { + OvmHost.registerAsMaster(_conn); + OvmHost.registerAsVmServer(_conn); + bridges = OvmBridge.getAllBridges(_conn); + } catch (XmlRpcException e) { + s_logger.debug("Get bridges failed", e); + throw new ConfigurationException("Cannot get bridges on host " + _ip + "," + e.getMessage()); + } + + if (_privateNetworkName != null && !bridges.contains(_privateNetworkName)) { + throw new ConfigurationException("Cannot find bridge " + _privateNetworkName + " on hsot " + _ip + ", all bridges are:" + bridges); + } + + if (_publicNetworkName != null && !bridges.contains(_publicNetworkName)) { + throw new ConfigurationException("Cannot find bridge " + _publicNetworkName + " on hsot " + _ip + ", all bridges are:" + bridges); + } + + if (_guestNetworkName != null && !bridges.contains(_guestNetworkName)) { + throw new ConfigurationException("Cannot find bridge " + _guestNetworkName + " on hsot " + _ip + ", all bridges are:" + bridges); + } + + /* set to false so each time ModifyStoragePoolCommand will re-setup heartbeat*/ + _isHeartBeat = false; + + try { + _canBridgeFirewall = canBridgeFirewall(); + } catch (XmlRpcException e) { + s_logger.error("Failed to detect whether the host supports security groups.", e); + _canBridgeFirewall = false; + } + + s_logger.debug(_canBridgeFirewall ? "OVM host supports security groups." : "OVM host doesn't support security groups."); + + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public Type getType() { + return Type.Routing; + } + + protected void fillHostInfo(StartupRoutingCommand cmd) { + try { + OvmHost.Details hostDetails = OvmHost.getDetails(_conn); + + cmd.setName(hostDetails.name); + cmd.setSpeed(hostDetails.cpuSpeed); + cmd.setCpus(hostDetails.cpuNum); + cmd.setMemory(hostDetails.freeMemory); + cmd.setDom0MinMemory(hostDetails.dom0Memory); + cmd.setGuid(_guid); + cmd.setDataCenter(_zoneId.toString()); + cmd.setPod(_podId.toString()); + cmd.setCluster(_clusterId.toString()); + cmd.setVersion(OvmResourceBase.class.getPackage().getImplementationVersion()); + cmd.setHypervisorType(HypervisorType.Ovm); + //TODO: introudce PIF + cmd.setPrivateIpAddress(_ip); + cmd.setStorageIpAddress(_ip); + + String defaultBridge = OvmBridge.getBridgeByIp(_conn, _ip); + if (_publicNetworkName == null) { + _publicNetworkName = defaultBridge; + } + if (_privateNetworkName == null) { + _privateNetworkName = _publicNetworkName; + } + if (_guestNetworkName == null) { + _guestNetworkName = _privateNetworkName; + } + Map d = cmd.getHostDetails(); + d.put("public.network.device", _publicNetworkName); + d.put("private.network.device", _privateNetworkName); + d.put("guest.network.device", _guestNetworkName); + cmd.setHostDetails(d); + + s_logger.debug(String.format("Add a OVM host(%s)", hostDetails.toJson())); + } catch (XmlRpcException e) { + s_logger.debug("XML RPC Exception" + e.getMessage(), e); + throw new CloudRuntimeException("XML RPC Exception" + e.getMessage(), e); + } + } + + protected void setupServer() throws IOException { + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); + sshConnection.connect(null, 60000, 60000); + if (!sshConnection.authenticateWithPassword(_username, _password)) { + throw new CloudRuntimeException("Unable to authenticate"); + } + SCPClient scp = new SCPClient(sshConnection); + + String configScriptName = "scripts/vm/hypervisor/ovm/configureOvm.sh"; + String configScriptPath = Script.findScript("" , configScriptName); + if (configScriptPath == null) { + throw new CloudRuntimeException("Unable to find " + configScriptName); + } + scp.put(configScriptPath, "/usr/bin/", "0755"); + + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, "sh /usr/bin/configureOvm.sh preSetup")) { + throw new CloudRuntimeException("Execute configureOvm.sh preSetup failed on " + _ip); + } + + File tmp = new File(configScriptPath); + File scriptDir = new File(tmp.getParent()); + File [] scripts = scriptDir.listFiles(); + for (int i=0; i changes = null; + synchronized (_vms) { + _vms.clear(); + changes = sync(); + } + cmd.setStateChanges(changes); + cmd.setCaps("hvm"); + return new StartupCommand[] {cmd}; + } catch (Exception e) { + s_logger.debug("Ovm resource initializes failed", e); + return null; + } + } + + @Override + public PingCommand getCurrentStatus(long id) { + try { + OvmHost.ping(_conn); + HashMap newStates = sync(); + return new PingRoutingCommand(getType(), id, newStates); + } catch (XmlRpcException e) { + s_logger.debug("Check agent status failed", e); + return null; + } + } + + protected ReadyAnswer execute(ReadyCommand cmd) { + try { + OvmHost.Details d = OvmHost.getDetails(_conn); + //TODO: cleanup halted vm + if (d.masterIp.equalsIgnoreCase(_ip)) { + return new ReadyAnswer(cmd); + } else { + s_logger.debug("Master IP changes to " + d.masterIp + ", it should be " + _ip); + return new ReadyAnswer(cmd, "I am not the master server"); + } + } catch (XmlRpcException e) { + s_logger.debug("XML RPC Exception" + e.getMessage(), e); + throw new CloudRuntimeException("XML RPC Exception" + e.getMessage(), e); + } + + } + + protected void createNfsSr(StorageFilerTO pool) throws XmlRpcException { + String mountPoint = String.format("%1$s:%2$s", pool.getHost(), pool.getPath()); + OvmStoragePool.Details d = new OvmStoragePool.Details(); + d.path = mountPoint; + d.type = OvmStoragePool.NFS; + d.uuid = pool.getUuid(); + OvmStoragePool.create(_conn, d); + s_logger.debug(String.format("Created SR (mount point:%1$s)", mountPoint)); + } + + protected void createOCFS2Sr(StorageFilerTO pool) throws XmlRpcException { + OvmStoragePool.Details d = new OvmStoragePool.Details(); + d.path = pool.getPath(); + d.type = OvmStoragePool.OCFS2; + d.uuid = pool.getUuid(); + OvmStoragePool.create(_conn, d); + s_logger.debug(String.format("Created SR (mount point:%1$s)", d.path)); + } + + private void setupHeartBeat(String poolUuid) { + try { + if (!_isHeartBeat) { + OvmHost.setupHeartBeat(_conn, poolUuid, _ip); + _isHeartBeat = true; + } + } catch (Exception e) { + s_logger.debug("setup heart beat for " + _ip + " failed", e); + _isHeartBeat = false; + } + } + + protected Answer execute(ModifyStoragePoolCommand cmd) { + StorageFilerTO pool = cmd.getPool(); + try { + if (pool.getType() == StoragePoolType.NetworkFilesystem) { + createNfsSr(pool); + } else if (pool.getType() == StoragePoolType.OCFS2) { + createOCFS2Sr(pool); + } else { + return new Answer(cmd, false, "The pool type: " + pool.getType().name() + " is not supported."); + } + + setupHeartBeat(pool.getUuid()); + OvmStoragePool.Details d = OvmStoragePool.getDetailsByUuid(_conn, pool.getUuid()); + Map tInfo = new HashMap(); + ModifyStoragePoolAnswer answer = new ModifyStoragePoolAnswer(cmd, d.totalSpace, d.freeSpace, tInfo); + return answer; + } catch (Exception e) { + s_logger.debug("ModifyStoragePoolCommand failed", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + protected Answer execute(CreateStoragePoolCommand cmd) { + return new Answer(cmd, true, "success"); + } + + protected PrimaryStorageDownloadAnswer execute(final PrimaryStorageDownloadCommand cmd) { + try { + URI uri = new URI(cmd.getUrl()); + String secondaryStoragePath = uri.getHost() + ":" + uri.getPath(); + Pair res = OvmStoragePool.downloadTemplate(_conn, cmd.getPoolUuid(), secondaryStoragePath); + return new PrimaryStorageDownloadAnswer(res.first(), res.second()); + } catch (Exception e) { + s_logger.debug("PrimaryStorageDownloadCommand failed", e); + return new PrimaryStorageDownloadAnswer(e.getMessage()); + } + } + + protected CreateAnswer execute(CreateCommand cmd) { + StorageFilerTO primaryStorage = cmd.getPool(); + DiskProfile disk = cmd.getDiskCharacteristics(); + + try { + OvmVolume.Details vol = null; + if (cmd.getTemplateUrl() != null) { + vol = OvmVolume.createFromTemplate(_conn, primaryStorage.getUuid(), cmd.getTemplateUrl()); + } else { + vol = OvmVolume.createDataDsik(_conn, primaryStorage.getUuid(), Long.toString(disk.getSize()), disk.getType() == Volume.Type.ROOT); + } + + VolumeTO volume = new VolumeTO(cmd.getVolumeId(), disk.getType(), + primaryStorage.getType(), primaryStorage.getUuid(), primaryStorage.getPath(), + vol.name, vol.path, vol.size, null); + return new CreateAnswer(cmd, volume); + } catch (Exception e) { + s_logger.debug("CreateCommand failed", e); + return new CreateAnswer(cmd, e.getMessage()); + } + } + + protected void applySpecToVm(OvmVm.Details vm, VirtualMachineTO spec) { + vm.name = spec.getName(); + vm.memory = spec.getMinRam(); + vm.cpuNum = spec.getCpus(); + vm.uuid = UUID.nameUUIDFromBytes(spec.getName().getBytes()).toString(); + if (spec.getBootloader() == BootloaderType.CD) { + vm.bootDev = OvmVm.CD; + vm.type = OvmVm.HVM; + } else { + vm.bootDev = OvmVm.HDD; + String osType = OvmHelper.getOvmGuestType(spec.getOs()); + if (OvmHelper.ORACLE_LINUX.equals(osType)) { + vm.type = OvmVm.PV; + } else if (OvmHelper.WINDOWS.equals(osType)) { + vm.type = OvmVm.HVM; + } else { + throw new CloudRuntimeException(spec.getOs() + " is not supported" + ",Oracle VM only supports Oracle Linux and Windows"); + } + } + } + + protected void createVbds(OvmVm.Details vm, VirtualMachineTO spec) throws URISyntaxException { + for (VolumeTO volume : spec.getDisks()) { + if (volume.getType() == Volume.Type.ROOT) { + OvmDisk.Details root = new OvmDisk.Details(); + root.path = volume.getPath(); + root.type = OvmDisk.WRITE; + root.isIso = false; + vm.rootDisk = root; + } else if (volume.getType() == Volume.Type.ISO) { + if (volume.getPath() != null) { + OvmDisk.Details iso = new OvmDisk.Details(); + URI path = new URI(volume.getPath()); + iso.path = path.getHost() + ":" + path.getPath(); + iso.type = OvmDisk.READ; + iso.isIso = true; + vm.disks.add(iso); + } + } else if (volume.getType() == Volume.Type.DATADISK){ + OvmDisk.Details data = new OvmDisk.Details(); + data.path = volume.getPath(); + data.type = OvmDisk.SHAREDWRITE; + data.isIso = false; + vm.disks.add(data); + } else { + throw new CloudRuntimeException("Unknow volume type: " + volume.getType()); + } + } + } + + private String createVlanBridge(String networkName, String vlanId) throws XmlRpcException { + OvmBridge.Details brdetails = OvmBridge.getDetails(_conn, networkName); + if (brdetails.attach.equalsIgnoreCase("null")) { + throw new CloudRuntimeException("Bridge " + networkName + " has no PIF"); + } + + OvmVlan.Details vdetails = new OvmVlan.Details(); + vdetails.pif = brdetails.attach; + vdetails.vid = Integer.parseInt(vlanId); + brdetails.name = "vlan" + vlanId; + brdetails.attach = "null"; + OvmBridge.createVlanBridge(_conn, brdetails, vdetails); + return brdetails.name; + } + + + //TODO: complete all network support + protected String getNetwork(NicTO nic) throws XmlRpcException { + String vlanId = null; + String bridgeName = null; + if (nic.getBroadcastType() == BroadcastDomainType.Vlan) { + URI broadcastUri = nic.getBroadcastUri(); + vlanId = broadcastUri.getHost(); + } + + if (nic.getType() == TrafficType.Guest) { + if (nic.getBroadcastType() == BroadcastDomainType.Vlan && !vlanId.equalsIgnoreCase("untagged")){ + bridgeName = createVlanBridge(_guestNetworkName, vlanId); + } else { + bridgeName = _guestNetworkName; + } + } else if (nic.getType() == TrafficType.Control) { + throw new CloudRuntimeException("local link network is not supported"); + } else if (nic.getType() == TrafficType.Public) { + throw new CloudRuntimeException("public network for system vm is not supported"); + } else if (nic.getType() == TrafficType.Management) { + bridgeName = _privateNetworkName; + } else { + throw new CloudRuntimeException("Unkonw network traffic type:" + nic.getType()); + } + + return bridgeName; + } + + protected OvmVif.Details createVif(NicTO nic) throws XmlRpcException { + OvmVif.Details vif = new OvmVif.Details(); + vif.mac = nic.getMac(); + vif.bridge = getNetwork(nic); + return vif; + } + + protected void createVifs(OvmVm.Details vm, VirtualMachineTO spec) throws CloudRuntimeException, XmlRpcException { + NicTO[] nics = spec.getNics(); + List vifs = new ArrayList(nics.length); + for (NicTO nic : nics) { + OvmVif.Details vif = createVif(nic); + vifs.add(nic.getDeviceId(), vif); + } + vm.vifs.addAll(vifs); + } + + protected void startVm(OvmVm.Details vm) throws XmlRpcException { + OvmVm.create(_conn, vm); + } + + + private void cleanupNetwork(List vifs) throws XmlRpcException { + for (OvmVif.Details vif : vifs) { + if (vif.bridge.startsWith("vlan")) { + OvmBridge.deleteVlanBridge(_conn, vif.bridge); + } + } + } + + protected void cleanup(OvmVm.Details vm) { + try { + cleanupNetwork(vm.vifs); + } catch (XmlRpcException e) { + s_logger.debug("Clean up network for " + vm.name + " failed", e); + } + _vmNetworkStats.remove(vm.name); + } + + @Override + public synchronized StartAnswer execute(StartCommand cmd) { + VirtualMachineTO vmSpec = cmd.getVirtualMachine(); + String vmName = vmSpec.getName(); + State state = State.Stopped; + OvmVm.Details vmDetails = null; + try { + synchronized (_vms) { + _vms.put(vmName, State.Starting); + } + + vmDetails = new OvmVm.Details(); + applySpecToVm(vmDetails, vmSpec); + createVbds(vmDetails, vmSpec); + createVifs(vmDetails, vmSpec); + startVm(vmDetails); + + // Add security group rules + NicTO[] nics = vmSpec.getNics(); + for (NicTO nic : nics) { + if (nic.isSecurityGroupEnabled()) { + if (vmSpec.getType().equals(VirtualMachine.Type.User)) { + defaultNetworkRulesForUserVm(vmName, vmSpec.getId(), nic); + } + } + } + + state = State.Running; + return new StartAnswer(cmd); + } catch (Exception e ) { + s_logger.debug("Start vm " + vmName + " failed", e); + cleanup(vmDetails); + return new StartAnswer(cmd, e.getMessage()); + } finally { + synchronized (_vms) { + //FIXME: where to come to Stopped??? + if (state != State.Stopped) { + _vms.put(vmName, state); + } else { + _vms.remove(vmName); + } + } + } + } + + protected Answer execute(GetHostStatsCommand cmd) { + try { + Map res = OvmHost.getPerformanceStats(_conn, _publicNetworkName); + Double cpuUtil = Double.parseDouble(res.get("cpuUtil")); + Double rxBytes = Double.parseDouble(res.get("rxBytes")); + Double txBytes = Double.parseDouble(res.get("txBytes")); + Double totalMemory = Double.parseDouble(res.get("totalMemory")); + Double freeMemory = Double.parseDouble(res.get("freeMemory")); + HostStatsEntry hostStats = new HostStatsEntry(cmd.getHostId(), cpuUtil, rxBytes, + txBytes, "host", totalMemory, freeMemory, 0, 0); + return new GetHostStatsAnswer(cmd, hostStats); + } catch (Exception e) { + s_logger.debug("Get host stats of " + cmd.getHostName() + " failed", e); + return new Answer(cmd, false, e.getMessage()); + } + + } + + @Override + public StopAnswer execute(StopCommand cmd) { + String vmName = cmd.getVmName(); + State state = null; + synchronized(_vms) { + state = _vms.get(vmName); + _vms.put(vmName, State.Stopping); + } + + try { + OvmVm.Details vm = OvmVm.getDetails(_conn, vmName); + deleteAllNetworkRulesForVm(vmName); + OvmVm.stop(_conn, vmName); + cleanup(vm); + + state = State.Stopped; + return new StopAnswer(cmd, "success", 0, 0L, 0L); + } catch (Exception e) { + s_logger.debug("Stop " + vmName + "failed", e); + return new StopAnswer(cmd, e.getMessage()); + } finally { + synchronized(_vms) { + if (state != null) { + _vms.put(vmName, state); + } else { + _vms.remove(vmName); + } + } + } + } + + @Override + public RebootAnswer execute(RebootCommand cmd) { + String vmName = cmd.getVmName(); + synchronized(_vms) { + _vms.put(vmName, State.Starting); + } + + try { + Map res = OvmVm.reboot(_conn, vmName); + Integer vncPort = Integer.parseInt(res.get("vncPort")); + return new RebootAnswer(cmd, null, null, null, vncPort); + } catch (Exception e) { + s_logger.debug("Reboot " + vmName + " failed", e); + return new RebootAnswer(cmd, e.getMessage()); + } finally { + synchronized(_vms) { + _vms.put(cmd.getVmName(), State.Running); + } + } + } + + private State toState(String vmName, String s) { + State state = _stateMaps.get(s); + if (state == null) { + s_logger.debug("Unkown state " + s + " for " + vmName); + state = State.Unknown; + } + return state; + } + + protected HashMap getAllVms() throws XmlRpcException { + final HashMap vmStates = new HashMap(); + Map vms = OvmHost.getAllVms(_conn); + for (final Map.Entry entry : vms.entrySet()) { + State state = toState(entry.getKey(), entry.getValue()); + vmStates.put(entry.getKey(), state); + } + return vmStates; + } + + protected HashMap sync() { + HashMap newStates; + HashMap oldStates = null; + + try { + final HashMap changes = new HashMap(); + newStates = getAllVms(); + if (newStates == null) { + s_logger.debug("Unable to get the vm states so no state sync at this point."); + return null; + } + + synchronized (_vms) { + oldStates = new HashMap(_vms.size()); + oldStates.putAll(_vms); + + for (final Map.Entry entry : newStates + .entrySet()) { + final String vm = entry.getKey(); + + State newState = entry.getValue(); + final State oldState = oldStates.remove(vm); + + if (s_logger.isTraceEnabled()) { + s_logger.trace("VM " + vm + ": ovm has state " + newState + " and we have state " + (oldState != null ? oldState.toString() : "null")); + } + + /* + * TODO: what is migrating ??? if + * (vm.startsWith("migrating")) { + * s_logger.debug("Migrating from xen detected. Skipping"); + * continue; } + */ + + if (oldState == null) { + _vms.put(vm, newState); + s_logger.debug("Detecting a new state but couldn't find a old state so adding it to the changes: " + + vm); + changes.put(vm, newState); + } else if (oldState == State.Starting) { + if (newState == State.Running) { + _vms.put(vm, newState); + } else if (newState == State.Stopped) { + s_logger.debug("Ignoring vm " + vm + + " because of a lag in starting the vm."); + } + } else if (oldState == State.Migrating) { + if (newState == State.Running) { + s_logger.debug("Detected that an migrating VM is now running: " + + vm); + _vms.put(vm, newState); + } + } else if (oldState == State.Stopping) { + if (newState == State.Stopped) { + _vms.put(vm, newState); + } else if (newState == State.Running) { + s_logger.debug("Ignoring vm " + vm + + " because of a lag in stopping the vm. "); + } + } else if (oldState != newState) { + _vms.put(vm, newState); + if (newState == State.Stopped) { + //TODO: need anything here? + } + changes.put(vm, newState); + } + } + + for (final Map.Entry entry : oldStates.entrySet()) { + final String vm = entry.getKey(); + final State oldState = entry.getValue(); + + if (s_logger.isTraceEnabled()) { + s_logger.trace("VM " + vm + " is now missing from ovm server so reporting stopped"); + } + + if (oldState == State.Stopping) { + s_logger.debug("Ignoring VM " + vm + " in transition state stopping."); + _vms.remove(vm); + } else if (oldState == State.Starting) { + s_logger.debug("Ignoring VM " + vm + " in transition state starting."); + } else if (oldState == State.Stopped) { + _vms.remove(vm); + } else if (oldState == State.Migrating) { + s_logger.debug("Ignoring VM " + vm + " in migrating state."); + } else { + _vms.remove(vm); + State state = State.Stopped; + // TODO: need anything here??? + changes.put(entry.getKey(), state); + } + } + } + + return changes; + } catch (Exception e) { + s_logger.debug("Ovm full sync failed", e); + return null; + } + } + + protected GetStorageStatsAnswer execute(final GetStorageStatsCommand cmd) { + try { + OvmStoragePool.Details d = OvmStoragePool.getDetailsByUuid(_conn, cmd.getStorageId()); + return new GetStorageStatsAnswer(cmd, d.totalSpace, d.usedSpace); + } catch (Exception e) { + s_logger.debug("GetStorageStatsCommand on pool " + cmd.getStorageId() + " failed", e); + return new GetStorageStatsAnswer(cmd, e.getMessage()); + } + } + + private VmStatsEntry getVmStat(String vmName) throws XmlRpcException { + Map vmStat = OvmVm.getVmStats(_conn, vmName); + int nvcpus = Integer.parseInt(vmStat.get("cpuNum")); + float cpuUtil = Float.parseFloat(vmStat.get("cpuUtil")); + long rxBytes = Long.parseLong(vmStat.get("rxBytes")); + long txBytes = Long.parseLong(vmStat.get("txBytes")); + Pair oldNetworkStat = _vmNetworkStats.get(vmName); + + long rx = rxBytes; + long tx = txBytes; + if (oldNetworkStat != null) { + rx -= oldNetworkStat.first(); + tx -= oldNetworkStat.second(); + oldNetworkStat.set(rxBytes, txBytes); + } else { + oldNetworkStat = new Pair(rx, tx); + } + _vmNetworkStats.put(vmName, oldNetworkStat); + + VmStatsEntry e = new VmStatsEntry(); + e.setCPUUtilization(cpuUtil); + e.setNumCPUs(nvcpus); + e.setNetworkReadKBs(rx); + e.setNetworkWriteKBs(tx); + e.setEntityType("vm"); + return e; + } + + protected GetVmStatsAnswer execute(GetVmStatsCommand cmd) { + List vmNames = cmd.getVmNames(); + HashMap vmStatsNameMap = new HashMap(); + for (String vmName : vmNames) { + try { + VmStatsEntry e = getVmStat(vmName); + vmStatsNameMap.put(vmName, e); + } catch (XmlRpcException e) { + s_logger.debug("Get vm stat for " + vmName + " failed", e); + continue; + } + } + return new GetVmStatsAnswer(cmd, vmStatsNameMap); + } + + protected AttachVolumeAnswer execute(AttachVolumeCommand cmd) { + return new AttachVolumeAnswer(cmd, "You must stop " + cmd.getVmName() + " first, OVM doesn't support hotplug datadisk"); + } + + public Answer execute(DestroyCommand cmd) { + try { + OvmVolume.destroy(_conn, cmd.getVolume().getPoolUuid(), cmd.getVolume().getPath()); + return new Answer(cmd, true, "Success"); + } catch (Exception e) { + s_logger.debug("Destroy volume " + cmd.getVolume().getName() + " failed", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + protected PrepareForMigrationAnswer execute(PrepareForMigrationCommand cmd) { + VirtualMachineTO vm = cmd.getVirtualMachine(); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Preparing host for migrating " + vm); + } + + NicTO[] nics = vm.getNics(); + try { + for (NicTO nic : nics) { + getNetwork(nic); + } + + synchronized (_vms) { + _vms.put(vm.getName(), State.Migrating); + } + return new PrepareForMigrationAnswer(cmd); + } catch (Exception e) { + s_logger.warn("Catch Exception " + e.getClass().getName() + " prepare for migration failed due to " + e.toString(), e); + return new PrepareForMigrationAnswer(cmd, e); + } + } + + protected MigrateAnswer execute(final MigrateCommand cmd) { + final String vmName = cmd.getVmName(); + State state = null; + + synchronized (_vms) { + state = _vms.get(vmName); + _vms.put(vmName, State.Stopping); + } + + try { + OvmVm.Details vm = OvmVm.getDetails(_conn, vmName); + String destIp = cmd.getDestinationIp(); + OvmVm.migrate(_conn, vmName, destIp); + state = State.Stopping; + cleanup(vm); + return new MigrateAnswer(cmd, true, "migration succeeded", null); + } catch (Exception e) { + String msg = "Catch Exception " + e.getClass().getName() + ": Migration failed due to " + e.toString(); + s_logger.debug(msg, e); + return new MigrateAnswer(cmd, false, msg, null); + } finally { + synchronized (_vms) { + _vms.put(vmName, state); + } + } + } + + protected CheckVirtualMachineAnswer execute(final CheckVirtualMachineCommand cmd) { + final String vmName = cmd.getVmName(); + try { + Map res = OvmVm.register(_conn, vmName); + Integer vncPort = Integer.parseInt(res.get("vncPort")); + HashMap states = getAllVms(); + State vmState = states.get(vmName); + if (vmState == null) { + s_logger.warn("Check state of " + vmName + " return null in CheckVirtualMachineCommand"); + vmState = State.Stopped; + } + + if (vmState == State.Running) { + synchronized (_vms) { + _vms.put(vmName, State.Running); + } + } + + return new CheckVirtualMachineAnswer(cmd, vmState, vncPort); + } catch (Exception e) { + s_logger.debug("Check migration for " + vmName + " failed", e); + return new CheckVirtualMachineAnswer(cmd, State.Stopped, null); + } + } + + protected MaintainAnswer execute(MaintainCommand cmd) { + return new MaintainAnswer(cmd); + } + + protected GetVncPortAnswer execute(GetVncPortCommand cmd) { + try { + Integer vncPort = OvmVm.getVncPort(_conn, cmd.getName()); + return new GetVncPortAnswer(cmd, vncPort); + } catch (Exception e) { + s_logger.debug("get vnc port for " + cmd.getName() + " failed", e); + return new GetVncPortAnswer(cmd, e.getMessage()); + } + } + + protected Answer execute(PingTestCommand cmd) { + try { + if (cmd.getComputingHostIp() != null) { + OvmHost.pingAnotherHost(_conn, cmd.getComputingHostIp()); + } else { + return new Answer(cmd, false, "why asks me to ping router???"); + } + + return new Answer(cmd, true, "success"); + } catch (Exception e) { + s_logger.debug("Ping " + cmd.getComputingHostIp() + " failed", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + protected FenceAnswer execute(FenceCommand cmd) { + try { + Boolean res = OvmHost.fence(_conn, cmd.getHostIp()); + return new FenceAnswer(cmd, res, res.toString()); + } catch (Exception e) { + s_logger.debug("fence " + cmd.getHostIp() + " failed", e); + return new FenceAnswer(cmd, false, e.getMessage()); + } + } + + protected Answer execute(AttachIsoCommand cmd) { + try { + URI iso = new URI(cmd.getIsoPath()); + String isoPath = iso.getHost() + ":" + iso.getPath(); + OvmVm.detachOrAttachIso(_conn, cmd.getVmName(), isoPath, cmd.isAttach()); + return new Answer(cmd); + } catch (Exception e) { + s_logger.debug("Attach or detach ISO " + cmd.getIsoPath() + " for " + cmd.getVmName() + " attach:" + cmd.isAttach() + " failed", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + private Answer execute(SecurityIngressRulesCmd cmd) { + boolean result = false; + try { + OvmVif.Details vif = getVifFromVm(cmd.getVmName(), null); + String vifDeviceName = vif.name; + String bridgeName = vif.bridge; + result = addNetworkRules(cmd.getVmName(), Long.toString(cmd.getVmId()), cmd.getGuestIp(), cmd.getSignature(), String.valueOf(cmd.getSeqNum()), cmd.getGuestMac(), cmd.stringifyRules(), vifDeviceName, bridgeName); + } catch (XmlRpcException e) { + s_logger.error(e); + result = false; + } + + if (!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); + } + } + + private Answer execute(CleanupNetworkRulesCmd cmd) { + boolean result = false; + try { + result = cleanupNetworkRules(); + } catch (XmlRpcException e) { + s_logger.error(e); + result = false; + } + + if (result) { + return new Answer(cmd); + } else { + return new Answer(cmd, result, "Failed to cleanup network rules."); + } + } + + protected boolean defaultNetworkRulesForUserVm(String vmName, Long vmId, NicTO nic) throws XmlRpcException { + if (!_canBridgeFirewall) { + return false; + } + + OvmVif.Details vif = getVifFromVm(vmName, nic.getDeviceId()); + String ipAddress = nic.getIp(); + String macAddress = vif.mac; + String vifName = vif.name; + String bridgeName = vif.bridge; + + return OvmSecurityGroup.defaultNetworkRulesForUserVm(_conn, vmName, String.valueOf(vmId), ipAddress, macAddress, vifName, bridgeName); + } + + protected boolean deleteAllNetworkRulesForVm(String vmName) throws XmlRpcException { + if (!_canBridgeFirewall) { + return false; + } + + String vif = getVifFromVm(vmName, null).name; + return OvmSecurityGroup.deleteAllNetworkRulesForVm(_conn, vmName, vif); + } + + protected boolean addNetworkRules(String vmName, String vmId, String guestIp, String signature, String seqno, String vifMacAddress, String rules, String vifDeviceName, String bridgeName) throws XmlRpcException { + if (!_canBridgeFirewall) { + return false; + } + + String newRules = rules.replace(" ", ";"); + return OvmSecurityGroup.addNetworkRules(_conn, vmName, vmId, guestIp, signature, seqno, vifMacAddress, newRules, vifDeviceName, bridgeName); + } + + protected boolean cleanupNetworkRules() throws XmlRpcException { + if (!_canBridgeFirewall) { + return false; + } + + return OvmSecurityGroup.cleanupNetworkRules(_conn); + } + + protected boolean canBridgeFirewall() throws XmlRpcException { + return OvmSecurityGroup.canBridgeFirewall(_conn); + } + + protected OvmVif.Details getVifFromVm(String vmName, Integer deviceId) throws XmlRpcException { + String vif = null; + List vifs = null; + + try { + vifs = getInterfaces(vmName); + } catch (XmlRpcException e) { + s_logger.error("Failed to get VIFs for VM " + vmName, e); + throw e; + } + + if (deviceId != null && vifs.size() > deviceId) { + return vifs.get(deviceId); + } else if (deviceId == null && vifs.size() > 0) { + return vifs.get(0); + } else { + return null; + } + } + + protected List getInterfaces(String vmName) throws XmlRpcException { + OvmVm.Details vmDetails = OvmVm.getDetails(_conn, vmName); + return vmDetails.vifs; + } + + protected Answer execute(PrepareOCFS2NodesCommand cmd) { + List> nodes = cmd.getNodes(); + StringBuffer params = new StringBuffer(); + for (Ternary node : nodes) { + String param = String.format("%1$s:%2$s:%3$s", node.first(), node.second(), node.third()); + params.append(param); + params.append(";"); + } + + try { + OvmStoragePool.prepareOCFS2Nodes(_conn, cmd.getClusterName(), params.toString()); + return new Answer(cmd, true, "Success"); + } catch (XmlRpcException e) { + s_logger.debug("OCFS2 prepare nodes failed", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof ReadyCommand) { + return execute((ReadyCommand)cmd); + } else if (cmd instanceof CreateStoragePoolCommand) { + return execute((CreateStoragePoolCommand)cmd); + } else if (cmd instanceof ModifyStoragePoolCommand) { + return execute((ModifyStoragePoolCommand)cmd); + } else if (cmd instanceof PrimaryStorageDownloadCommand) { + return execute((PrimaryStorageDownloadCommand)cmd); + } else if (cmd instanceof CreateCommand) { + return execute((CreateCommand)cmd); + } else if (cmd instanceof GetHostStatsCommand) { + return execute((GetHostStatsCommand)cmd); + } else if (cmd instanceof StopCommand) { + return execute((StopCommand)cmd); + } else if (cmd instanceof RebootCommand) { + return execute((RebootCommand)cmd); + } else if (cmd instanceof GetStorageStatsCommand) { + return execute((GetStorageStatsCommand)cmd); + } else if (cmd instanceof GetVmStatsCommand) { + return execute((GetVmStatsCommand)cmd); + } else if (cmd instanceof AttachVolumeCommand) { + return execute((AttachVolumeCommand)cmd); + } else if (cmd instanceof DestroyCommand) { + return execute((DestroyCommand)cmd); + } else if (cmd instanceof PrepareForMigrationCommand) { + return execute((PrepareForMigrationCommand)cmd); + } else if (cmd instanceof MigrateCommand) { + return execute((MigrateCommand)cmd); + } else if (cmd instanceof CheckVirtualMachineCommand) { + return execute((CheckVirtualMachineCommand)cmd); + } else if (cmd instanceof MaintainCommand) { + return execute((MaintainCommand)cmd); + } else if (cmd instanceof StartCommand) { + return execute((StartCommand)cmd); + } else if (cmd instanceof GetVncPortCommand) { + return execute((GetVncPortCommand)cmd); + } else if (cmd instanceof PingTestCommand) { + return execute((PingTestCommand)cmd); + } else if (cmd instanceof FenceCommand) { + return execute((FenceCommand)cmd); + } else if (cmd instanceof AttachIsoCommand) { + return execute((AttachIsoCommand)cmd); + } else if (cmd instanceof SecurityIngressRulesCmd) { + return execute((SecurityIngressRulesCmd) cmd); + } else if (cmd instanceof CleanupNetworkRulesCmd) { + return execute((CleanupNetworkRulesCmd) cmd); + } else if (cmd instanceof PrepareOCFS2NodesCommand) { + return execute((PrepareOCFS2NodesCommand)cmd); + }else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } + + @Override + public void disconnected() { + // TODO Auto-generated method stub + + } + + @Override + public IAgentControl getAgentControl() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setAgentControl(IAgentControl agentControl) { + // TODO Auto-generated method stub + + } + +} diff --git a/ovm/src/com/cloud/ovm/object/Coder.java b/ovm/src/com/cloud/ovm/object/Coder.java new file mode 100644 index 00000000000..e66c4f7e3d7 --- /dev/null +++ b/ovm/src/com/cloud/ovm/object/Coder.java @@ -0,0 +1,69 @@ +package com.cloud.ovm.object; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import com.cloud.ovm.object.OvmHost.Details; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; + +public class Coder { + private static Gson _gson; + private static Gson _mapGson; + + public static Object[] EMPTY_PARAMS = new Object[0]; + + private static class MapDecoder implements JsonDeserializer> { + @Override + public Map deserialize(JsonElement arg0, Type arg1, + JsonDeserializationContext arg2) throws JsonParseException { + if (!arg0.isJsonObject()) { + throw new JsonParseException(arg0.toString() + " is not Json Object, cannot convert to map"); + } + JsonObject objs = arg0.getAsJsonObject(); + Map map = new HashMap(); + for (Entry e : objs.entrySet()) { + if (!e.getValue().isJsonPrimitive()) { + throw new JsonParseException(e.getValue().toString() + " is not a Json primitive," + arg0 + " can not convert to map"); + } + map.put(e.getKey(), e.getValue().getAsString()); + } + return map; + } + + } + + static { + GsonBuilder _builder = new GsonBuilder(); + _builder.registerTypeAdapter(Map.class, new MapDecoder()); + _mapGson = _builder.create(); + _gson = new Gson(); + } + + public static String toJson(Object obj) { + return _gson.toJson(obj); + } + + public static T fromJson(String str, Class clz) { + return (T)_gson.fromJson(str, clz); + } + + @SuppressWarnings("unchecked") + public static Map mapFromJson(String str) { + return _mapGson.fromJson(str, Map.class); + } + + public static List listFromJson(String str) { + Type listType = new TypeToken>() {}.getType(); + return _gson.fromJson(str, listType); + } +} diff --git a/ovm/src/com/cloud/ovm/object/Connection.java b/ovm/src/com/cloud/ovm/object/Connection.java new file mode 100644 index 00000000000..8055a3e9c48 --- /dev/null +++ b/ovm/src/com/cloud/ovm/object/Connection.java @@ -0,0 +1,108 @@ +package com.cloud.ovm.object; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.TimeZone; + +import org.apache.log4j.Logger; +import org.apache.xmlrpc.XmlRpcException; +import org.apache.xmlrpc.client.TimingOutCallback; +import org.apache.xmlrpc.client.XmlRpcClient; +import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; + +import com.cloud.utils.exception.CloudRuntimeException; + +public class Connection { + private static final Logger s_logger = Logger.getLogger(Connection.class); + private XmlRpcClientConfigImpl _config = new XmlRpcClientConfigImpl(); + XmlRpcClient _client; + String _username; + String _password; + String _ip; + Integer _port = 8899; + Boolean _isSsl = false; + + private XmlRpcClient getXmlClient() { + XmlRpcClient client = new XmlRpcClient(); + + URL url; + try { + url = new URL("http://" + _ip + ":" + _port.toString()); + _config.setTimeZone(TimeZone.getTimeZone("UTC")); + _config.setServerURL(url); + _config.setReplyTimeout(0); // disable, we use asyncexecute to control timeout + _config.setConnectionTimeout(6000); + _config.setBasicUserName(_username); + _config.setBasicPassword(_password); + client.setConfig(_config); + } catch (MalformedURLException e) { + throw new CloudRuntimeException(e.getMessage()); + } + + return client; + } + + public Connection(String ip, Integer port, String username, String password) { + _ip = ip; + _port = port; + _username = username; + _password = password; + _client = getXmlClient(); + } + + public Connection(String ip, String username, String password) { + _ip = ip; + _username = username; + _password = password; + _client = getXmlClient(); + } + + public Object call(String method, Object[] params) throws XmlRpcException { + /* default timeout is 10 mins */ + return callTimeoutInSec(method, params, 600); + } + + public Object callTimeoutInSec(String method, Object[] params, int timeout) throws XmlRpcException { + TimingOutCallback callback = new TimingOutCallback(timeout * 1000); + Object[] mParams = new Object[params.length + 1]; + mParams[0] = method; + for (int i=0; i interfaces; + + public String toJson() { + return Coder.toJson(this); + } + } + + public static void create(Connection c, Details d) throws XmlRpcException { + Object[] params = {d.toJson()}; + c.call("OvmNetwork.createBridge", params); + } + + public static void delete(Connection c, String name) throws XmlRpcException { + Object[] params = {name}; + c.call("OvmNetwork.deleteBridge", params); + } + + public static List getAllBridges(Connection c) throws XmlRpcException { + String res = (String) c.call("OvmNetwork.getAllBridges", Coder.EMPTY_PARAMS); + return Coder.listFromJson(res); + } + + public static String getBridgeByIp(Connection c, String ip) throws XmlRpcException { + Object[] params = {ip}; + String res = (String) c.call("OvmNetwork.getBridgeByIp", params); + return Coder.mapFromJson(res).get("bridge"); + } + + public static void createVlanBridge(Connection c, OvmBridge.Details bDetails, OvmVlan.Details vDetails) throws XmlRpcException { + Object[] params = {bDetails.toJson(), vDetails.toJson()}; + c.call("OvmNetwork.createVlanBridge", params); + } + + public static void deleteVlanBridge(Connection c, String name) throws XmlRpcException { + Object[] params = {name}; + c.call("OvmNetwork.deleteVlanBridge", params); + } + + public static Details getDetails(Connection c, String name) throws XmlRpcException { + Object[] params = {name}; + String res = (String) c.call("OvmNetwork.getBridgeDetails", params); + return Coder.fromJson(res, Details.class); + } +} diff --git a/ovm/src/com/cloud/ovm/object/OvmDisk.java b/ovm/src/com/cloud/ovm/object/OvmDisk.java new file mode 100644 index 00000000000..a99a0a90b7f --- /dev/null +++ b/ovm/src/com/cloud/ovm/object/OvmDisk.java @@ -0,0 +1,15 @@ +package com.cloud.ovm.object; + + +public class OvmDisk extends OvmObject { + public static final String WRITE = "w"; + public static final String READ = "r"; + public static final String SHAREDWRITE = "w!"; + public static final String SHAREDREAD = "r!"; + + public static class Details { + public String type; + public String path; + public Boolean isIso; + } +} diff --git a/ovm/src/com/cloud/ovm/object/OvmHost.java b/ovm/src/com/cloud/ovm/object/OvmHost.java new file mode 100644 index 00000000000..5a3492477f8 --- /dev/null +++ b/ovm/src/com/cloud/ovm/object/OvmHost.java @@ -0,0 +1,77 @@ +package com.cloud.ovm.object; + +import java.util.List; +import java.util.Map; + +import org.apache.xmlrpc.XmlRpcException; + +public class OvmHost extends OvmObject { + public static final String SITE = "site"; + public static final String UTILITY = "utility"; + public static final String XEN = "xen"; + + public static class Details { + public String masterIp; + public Integer cpuNum; + public Integer cpuSpeed; + public Long totalMemory; + public Long freeMemory; + public Long dom0Memory; + public String agentVersion; + public String name; + public String dom0KernelVersion; + public String hypervisorVersion; + + public String toJson() { + return Coder.toJson(this); + } + } + + public static void registerAsMaster(Connection c) throws XmlRpcException { + Object[] params = {c.getIp(), c.getUserName(), c.getPassword(), c.getPort(), c.getIsSsl()}; + c.call("OvmHost.registerAsMaster", params); + } + + public static void registerAsVmServer(Connection c) throws XmlRpcException { + Object[] params = {c.getIp(), c.getUserName(), c.getPassword(), c.getPort(), c.getIsSsl()}; + c.call("OvmHost.registerAsVmServer", params); + } + + public static Details getDetails(Connection c) throws XmlRpcException { + String res = (String)c.call("OvmHost.getDetails", Coder.EMPTY_PARAMS); + return Coder.fromJson(res, OvmHost.Details.class); + } + + public static void ping(Connection c) throws XmlRpcException { + Object[] params = {c.getIp()}; + c.call("OvmHost.ping", params); + } + + public static Map getPerformanceStats(Connection c, String bridge) throws XmlRpcException { + Object[] params = {bridge}; + String res = (String) c.call("OvmHost.getPerformanceStats", params); + return Coder.mapFromJson(res); + } + + public static Map getAllVms(Connection c) throws XmlRpcException { + String res = (String) c.call("OvmHost.getAllVms", Coder.EMPTY_PARAMS); + return Coder.mapFromJson(res); + } + + public static void setupHeartBeat(Connection c, String poolUuid, String ip) throws XmlRpcException { + Object[] params = {poolUuid, ip}; + c.call("OvmHost.setupHeartBeat", params); + } + + public static Boolean fence(Connection c, String ip) throws XmlRpcException { + Object[] params = {ip}; + String res = (String)c.call("OvmHost.fence", params); + Map result = Coder.mapFromJson(res); + return Boolean.parseBoolean(result.get("isLive")); + } + + public static void pingAnotherHost(Connection c, String ip) throws XmlRpcException { + Object[] params = {ip}; + c.call("OvmHost.pingAnotherHost", params); + } +} diff --git a/ovm/src/com/cloud/ovm/object/OvmObject.java b/ovm/src/com/cloud/ovm/object/OvmObject.java new file mode 100644 index 00000000000..2f3d4e500b2 --- /dev/null +++ b/ovm/src/com/cloud/ovm/object/OvmObject.java @@ -0,0 +1,4 @@ +package com.cloud.ovm.object; + +public class OvmObject { +} diff --git a/ovm/src/com/cloud/ovm/object/OvmSecurityGroup.java b/ovm/src/com/cloud/ovm/object/OvmSecurityGroup.java new file mode 100644 index 00000000000..718cd0438f4 --- /dev/null +++ b/ovm/src/com/cloud/ovm/object/OvmSecurityGroup.java @@ -0,0 +1,38 @@ +package com.cloud.ovm.object; + +import org.apache.xmlrpc.XmlRpcException; + +public class OvmSecurityGroup extends OvmObject { + + // canBridgeFirewall + // cleanupRules + // defaultNetworkRulesUserVm + // addNetworkRules + // deleteAllNetworkRulesForVm + + public static boolean canBridgeFirewall(Connection c) throws XmlRpcException { + Object[] params = {}; + return (Boolean) c.call("OvmSecurityGroup.can_bridge_firewall", params); + } + + public static boolean cleanupNetworkRules(Connection c) throws XmlRpcException { + Object[] params = {}; + return (Boolean) c.call("OvmSecurityGroup.cleanup_rules", params); + } + + public static boolean defaultNetworkRulesForUserVm(Connection c, String vmName, String vmId, String ipAddress, String macAddress, String vifName, String bridgeName) throws XmlRpcException { + Object[] params = {vmName, vmId, ipAddress, macAddress, vifName, bridgeName}; + return (Boolean) c.call("OvmSecurityGroup.default_network_rules_user_vm", params); + } + + public static boolean addNetworkRules(Connection c, String vmName, String vmId, String guestIp, String signature, String seqno, String vifMacAddress, String newRules, String vifDeviceName, String bridgeName) throws XmlRpcException { + Object[] params = {vmName, vmId, guestIp, signature, seqno, vifMacAddress, newRules, vifDeviceName, bridgeName}; + return (Boolean) c.call("OvmSecurityGroup.add_network_rules", params); + } + + public static boolean deleteAllNetworkRulesForVm(Connection c, String vmName, String vif) throws XmlRpcException { + Object[] params = {vmName, vif}; + return (Boolean) c.call("OvmSecurityGroup.delete_all_network_rules_for_vm", params); + } + +} diff --git a/ovm/src/com/cloud/ovm/object/OvmStoragePool.java b/ovm/src/com/cloud/ovm/object/OvmStoragePool.java new file mode 100755 index 00000000000..3f5ea4d03e1 --- /dev/null +++ b/ovm/src/com/cloud/ovm/object/OvmStoragePool.java @@ -0,0 +1,70 @@ +package com.cloud.ovm.object; + +import java.util.Map; + +import org.apache.xmlrpc.XmlRpcException; + +import com.cloud.utils.Pair; + +public class OvmStoragePool extends OvmObject { + public static final String NFS = "OVSSPNFS"; + public static final String OCFS2 = "OVSSPOCFS2"; + public static final String PARTITION = "OVSSPPartition"; + + public static class Details { + public String uuid; + public String type; + public String path; + public String mountPoint; + public Long totalSpace; + public Long freeSpace; + public Long usedSpace; + + public String toJson() { + return Coder.toJson(this); + } + } + + /** + * @param c: connection + * @param d: includes three fields {uuid, type, path} + * @throws XmlRpcException + */ + public static void create(Connection c, Details d) throws XmlRpcException { + Object[] params = {d.toJson()}; + c.callTimeoutInSec("OvmStoragePool.create", params, 3600); + } + + /** + * + * @param c: connection + * @param uuid: uuid of primary storage + * @return: Details with full fields + * @throws XmlRpcException + */ + public static Details getDetailsByUuid(Connection c, String uuid) throws XmlRpcException { + Object[] params = {uuid}; + String res = (String)c.call("OvmStoragePool.getDetailsByUuid", params); + return Coder.fromJson(res, Details.class); + } + + /** + * + * @param c: Connection + * @param uuid: Pool uuid + * @param from: secondary storage download path + * @return: + * @throws XmlRpcException + */ + public static Pair downloadTemplate(Connection c, String uuid, String from) throws XmlRpcException { + Object[] params = {uuid, from}; + String res = (String) c.callTimeoutInSec("OvmStoragePool.downloadTemplate", params, 3600); + Map pair = Coder.mapFromJson(res); + return new Pair((String)pair.get("installPath"), Long.parseLong((String)pair.get("templateSize"))); + } + + public static void prepareOCFS2Nodes(Connection c, String clusterName, String nodes) throws XmlRpcException { + Object[] params = {clusterName, nodes}; + c.call("OvmStoragePool.prepareOCFS2Nodes", params); + } +} diff --git a/ovm/src/com/cloud/ovm/object/OvmVif.java b/ovm/src/com/cloud/ovm/object/OvmVif.java new file mode 100644 index 00000000000..ac6d1e7ee10 --- /dev/null +++ b/ovm/src/com/cloud/ovm/object/OvmVif.java @@ -0,0 +1,14 @@ +package com.cloud.ovm.object; + +public class OvmVif extends OvmObject { + public static final String NETFRONT = "netfront"; + public static final String IOEMU = "ioemu"; + + public static class Details { + public String name; + public String mac; + public String bridge; + public String type; + } + +} diff --git a/ovm/src/com/cloud/ovm/object/OvmVlan.java b/ovm/src/com/cloud/ovm/object/OvmVlan.java new file mode 100644 index 00000000000..9d1a9e949ed --- /dev/null +++ b/ovm/src/com/cloud/ovm/object/OvmVlan.java @@ -0,0 +1,27 @@ +package com.cloud.ovm.object; + +import org.apache.xmlrpc.XmlRpcException; + +public class OvmVlan extends OvmObject { + public static class Details { + public String name; + public int vid; + public String pif; + + public String toJson() { + return Coder.toJson(this); + } + } + + public static String create(Connection c, Details d) throws XmlRpcException { + Object[] params = {d.toJson()}; + String res = (String)c.call("OvmNetwork.createVlan", params); + Details ret = Coder.fromJson(res, Details.class); + return ret.name; + } + + public static void delete(Connection c, String name) throws XmlRpcException { + Object[] params = {name}; + c.call("OvmNetwork.deleteVlan", params); + } +} diff --git a/ovm/src/com/cloud/ovm/object/OvmVm.java b/ovm/src/com/cloud/ovm/object/OvmVm.java new file mode 100644 index 00000000000..a74534d45d9 --- /dev/null +++ b/ovm/src/com/cloud/ovm/object/OvmVm.java @@ -0,0 +1,96 @@ +package com.cloud.ovm.object; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.xmlrpc.XmlRpcException; + +import com.cloud.ovm.object.OvmHost.Details; + +public class OvmVm extends OvmObject { + public static final String CD = "CD"; + public static final String HDD = "HDD"; + public static final String HVM = "HVM"; + public static final String PV = "PV"; + public static final String FROMCONFIGFILE = "FROMCONFIGFILE"; + + public static class Details { + public int cpuNum; + public long memory; + public OvmDisk.Details rootDisk; + public List disks; + public List vifs; + public String name; + public String uuid; + public String powerState; + public String bootDev; + public String type; + + public Details() { + disks = new ArrayList(); + vifs = new ArrayList(); + } + + public String toJson() { + return Coder.toJson(this); + } + } + + public OvmVm() { + } + + /*********** XML RPC Call **************/ + public static void create(Connection c, Details d) throws XmlRpcException { + Object[] params = {d.toJson()}; + c.call("OvmVm.create", params); + } + + public static Map reboot(Connection c, String vmName) throws XmlRpcException { + Object[] params = {vmName}; + String res = (String) c.call("OvmVm.reboot", params); + return Coder.mapFromJson(res); + } + + public static void stop(Connection c, String vmName) throws XmlRpcException { + Object[] params = {vmName}; + /* Agent will destroy vm if vm shutdowns failed due to timout after 10 mins, so we set timeout to 20 mins here*/ + c.callTimeoutInSec("OvmVm.stop", params, 1200); + } + + public static Details getDetails(Connection c, String vmName) throws XmlRpcException { + Object[] params = {vmName}; + String res = (String)c.call("OvmVm.getDetails", params); + return Coder.fromJson(res, OvmVm.Details.class); + } + + public static Map getVmStats(Connection c, String vmName) throws XmlRpcException { + Object[] params = {vmName}; + String res = (String)c.call("OvmVm.getVmStats", params); + return Coder.mapFromJson(res); + } + + public static void migrate(Connection c, String vmName, String dest) throws XmlRpcException { + Object[] params = {vmName, dest}; + c.call("OvmVm.migrate", params); + } + + public static Map register(Connection c, String vmName) throws XmlRpcException { + Object[] params = {vmName}; + String res = (String) c.call("OvmVm.register", params); + return Coder.mapFromJson(res); + } + + public static Integer getVncPort(Connection c, String vmName) throws XmlRpcException { + Object[] params = {vmName}; + String res = (String) c.call("OvmVm.getVncPort", params); + Map result = Coder.mapFromJson(res); + return Integer.parseInt(result.get("vncPort")); + } + + public static void detachOrAttachIso(Connection c, String vmName, String iso, Boolean isAttach) throws XmlRpcException { + Object[] params = {vmName, iso, isAttach}; + c.call("OvmVm.detachOrAttachIso", params); + } + +} diff --git a/ovm/src/com/cloud/ovm/object/OvmVolume.java b/ovm/src/com/cloud/ovm/object/OvmVolume.java new file mode 100644 index 00000000000..7450e45b11e --- /dev/null +++ b/ovm/src/com/cloud/ovm/object/OvmVolume.java @@ -0,0 +1,37 @@ +package com.cloud.ovm.object; + +import org.apache.xmlrpc.XmlRpcException; + +public class OvmVolume extends OvmObject { + public static class Details { + public String name; + public String uuid; + public String poolUuid; + public Long size; + public String path; + + public String toJson() { + return Coder.toJson(this); + } + } + + public static Details createDataDsik(Connection c, String poolUuid, String size, Boolean isRoot) throws XmlRpcException { + Object[] params = {poolUuid, size, isRoot}; + String res = (String) c.call("OvmVolume.createDataDisk", params); + Details result = Coder.fromJson(res, Details.class); + return result; + } + + public static Details createFromTemplate(Connection c, String poolUuid, String templateUrl) throws XmlRpcException { + Object[] params = {poolUuid, templateUrl}; + String res = (String) c.callTimeoutInSec("OvmVolume.createFromTemplate", params, 3600*3); + Details result = Coder.fromJson(res, Details.class); + return result; + } + + public static void destroy(Connection c, String poolUuid, String path) throws XmlRpcException { + Object[] params = {poolUuid, path}; + c.call("OvmVolume.destroy", params); + } + +} diff --git a/ovm/src/com/cloud/ovm/object/Test.java b/ovm/src/com/cloud/ovm/object/Test.java new file mode 100644 index 00000000000..4ae27a3ec36 --- /dev/null +++ b/ovm/src/com/cloud/ovm/object/Test.java @@ -0,0 +1,167 @@ +package com.cloud.ovm.object; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.cloud.utils.Pair; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class Test { + public static void main(String[] args) { + try { + /*Connection c = new Connection("192.168.105.155", "oracle", "password"); + Utils util = new UtilsImpl(c); + Storage storage = new StorageImpl(c); + String[] res = util.listDir("/etc", 1); + for (String s : res) { + System.out.println(s); + } + + + Pool pool = new PoolImpl(c); + + //pool.registerServer("192.168.105.155", Pool.ServerType.SITE); + //pool.registerServer("192.168.105.155", Pool.ServerType.UTILITY); + //pool.registerServer("192.168.105.155", Pool.ServerType.XEN); + System.out.println("Is:" + pool.isServerRegistered()); + //String ip = pool.getMasterIp(); + //System.out.println("IP:" + ip); + System.out.println(pool.getServerConfig()); + System.out.println(pool.getServerXmInfo()); + System.out.println(pool.getHostInfo()); + System.out.println(pool.getAgentVersion()); + String[] srs = storage.listSr(); + for (int i=0; i spaceInfo = storage.getSrSpaceInfo("192.168.110.232:/export/frank/nfs"); + System.out.println("Total:" + spaceInfo.first()); + System.out.println("Free:" + spaceInfo.second());*/ + OvmVm.Details vm = new OvmVm.Details(); + vm.cpuNum = 1; + vm.memory = 512; + vm.name = "Test"; + vm.uuid = "This-is-a-test"; + OvmDisk.Details rootDisk = new OvmDisk.Details(); + rootDisk.path = "/root/root.raw"; + rootDisk.type = OvmDisk.WRITE; + vm.rootDisk = rootDisk; + OvmDisk.Details dataDisk = new OvmDisk.Details(); + dataDisk.path = "/tmp/data.raw"; + dataDisk.type = OvmDisk.SHAREDWRITE; + vm.disks.add(dataDisk); + vm.disks.add(dataDisk); + vm.disks.add(dataDisk); + vm.disks.add(dataDisk); + vm.disks.add(dataDisk); + OvmVif.Details vif = new OvmVif.Details(); + vif.mac = "00:ff:ff:ff:ff:ee"; + vif.bridge = "xenbr0"; + vif.type = OvmVif.NETFRONT; + vm.vifs.add(vif); + vm.vifs.add(vif); + vm.vifs.add(vif); + vm.vifs.add(vif); + vm.vifs.add(vif); + //System.out.println(vm.toJson()); + Connection c = new Connection("192.168.189.12", "oracle", "password"); + //System.out.println(Coder.toJson(OvmHost.getDetails(c))); + String txt = "{\"MasterIp\": \"192.168.189.12\", \"dom0Memory\": 790626304, \"freeMemory\": 16378757120, \"totalMemory\": 17169383424, \"cpuNum\": 4, \"agentVersion\": \"2.3-38\", \"cpuSpeed\": 2261}"; + //OvmHost.Details d = new GsonBuilder().create().fromJson(txt, OvmHost.Details.class); + //OvmHost.Details d = Coder.fromJson(txt, OvmHost.Details.class); + //OvmHost.Details d = OvmHost.getDetails(c); + //System.out.println(Coder.toJson(d)); +// OvmStoragePool.Details pool = new OvmStoragePool.Details(); +// pool.path = "192.168.110.232:/export/frank/ovs"; +// pool.type = OvmStoragePool.NFS; +// pool.uuid = "123"; +// System.out.println(pool.toJson()); + + String cmd = null; + System.out.println(args.length); + if (args.length >= 1) { + cmd = args[0]; + OvmVm.Details d = new OvmVm.Details(); + d.cpuNum = 1; + d.memory = 512 * 1024 * 1024; + d.name = "MyTest"; + d.uuid = "1-2-3-4-5"; + OvmDisk.Details r = new OvmDisk.Details(); + r.path = "/var/ovs/mount/60D0985974CA425AAF5D01A1F161CC8B/running_pool/36_systemvm/System.img"; + r.type = OvmDisk.WRITE; + d.rootDisk = r; + OvmVif.Details v = new OvmVif.Details(); + v.mac = "00:16:3E:5C:B1:D1"; + v.bridge = "xenbr0"; + v.type = OvmVif.NETFRONT; + d.vifs.add(v); + System.out.println(d.toJson()); + + if (cmd.equalsIgnoreCase("create")) { + // String s = + // "{\"cpuNum\":1,\"memory\":512,\"rootDisk\":{\"type\":\"w\",\"path\":\"/var/ovs/mount/60D0985974CA425AAF5D01A1F161CC8B/running_pool/36_systemvm/System.img\"},\"disks\":[],\"vifs\":[{\"mac\":\"00:16:3E:5C:B1:D1\",\"bridge\":\"xenbr0\",\"type\":\"netfront\"}],\"name\":\"MyTest\",\"uuid\":\"1-2-3-4-5\"}"; + OvmVm.create(c, d); + // c.call("OvmVm.echo", new Object[]{s}); + } else if (cmd.equalsIgnoreCase("reboot")) { + Map res = OvmVm.reboot(c, "MyTest"); + System.out.println(res.get("vncPort")); + //OvmVm.stop(c, "MyTest"); + //OvmVm.create(c, d); + } else if (cmd.equalsIgnoreCase("stop")) { + OvmVm.stop(c, "MyTest"); + } else if (cmd.equalsIgnoreCase("details")) { + OvmVm.Details ddd = OvmVm.getDetails(c, "MyTest"); + System.out.println(ddd.vifs.size()); + System.out.println(ddd.rootDisk.path); + System.out.println(ddd.powerState); + } else if (cmd.equalsIgnoreCase("all")) { + System.out.println(OvmHost.getAllVms(c)); + } else if (cmd.equalsIgnoreCase("createBridge")) { + OvmBridge.Details bd = new OvmBridge.Details(); + bd.name = "xenbr10"; + bd.attach = args[1]; + OvmBridge.create(c, bd); + } else if (cmd.equalsIgnoreCase("createVlan")) { + OvmVlan.Details vd = new OvmVlan.Details(); + vd.pif = "eth0"; + vd.vid = 1000; + String vname = OvmVlan.create(c, vd); + System.out.println(vname); + } else if (cmd.equalsIgnoreCase("delVlan")) { + OvmVlan.delete(c, args[1]); + } else if (cmd.equalsIgnoreCase("delBr")) { + OvmBridge.delete(c, args[1]); + } else if (cmd.equalsIgnoreCase("getBrs")) { + List brs = OvmBridge.getAllBridges(c); + System.out.println(brs); + } else if (cmd.equalsIgnoreCase("getBrDetails")) { + OvmBridge.Details brd = OvmBridge.getDetails(c, args[1]); + System.out.println(brd.interfaces); + } + + } + + List l = new ArrayList(); + l.add("4b4d8951-f0b6-36c5-b4f3-a82ff2611c65"); + System.out.println(Coder.toJson(l)); + +// Map res = OvmHost.getPerformanceStats(c, "xenbr0"); +// System.out.println(res.toString()); +// String stxt = "{\"vifs\": [{\"bridge\": \"xenbr0\", \"mac\": \"00:16:3E:5C:B1:D1\", \"type\": \"netfront\"}], \"powerState\": \"RUNNING\", \"disks\": [], \"cpuNum\": 1, \"memory\": 536870912, \"rootDisk\": {\"path\": \"/var/ovs/mount/60D0985974CA425AAF5D01A1F161CC8B/running_pool/MyTest/System.img\", \"type\": \"w\"}}"; +// OvmVm.Details ddd = Coder.fromJson(stxt, OvmVm.Details.class); +// System.out.println(ddd.vifs.size()); +// System.out.println(ddd.rootDisk.path); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } +} diff --git a/scripts/network/juniper/access-profile-add.xml b/scripts/network/juniper/access-profile-add.xml new file mode 100644 index 00000000000..7892d669fb5 --- /dev/null +++ b/scripts/network/juniper/access-profile-add.xml @@ -0,0 +1,20 @@ + + + + + +%access-profile-name% + +%username% + +%password% + + + +%address-pool-name% + + + + + + diff --git a/scripts/network/juniper/access-profile-getall.xml b/scripts/network/juniper/access-profile-getall.xml new file mode 100644 index 00000000000..d4376590a34 --- /dev/null +++ b/scripts/network/juniper/access-profile-getall.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/scripts/network/juniper/access-profile-getone.xml b/scripts/network/juniper/access-profile-getone.xml new file mode 100644 index 00000000000..2e6960b6ef1 --- /dev/null +++ b/scripts/network/juniper/access-profile-getone.xml @@ -0,0 +1,11 @@ + + + + + +%access-profile-name% + + + + + diff --git a/scripts/network/juniper/address-book-entry-add.xml b/scripts/network/juniper/address-book-entry-add.xml new file mode 100644 index 00000000000..e97ac0eb2e7 --- /dev/null +++ b/scripts/network/juniper/address-book-entry-add.xml @@ -0,0 +1,19 @@ + + + + + + +%zone% + +
+%entry-name% +%ip% +
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/scripts/network/juniper/address-book-entry-getall.xml b/scripts/network/juniper/address-book-entry-getall.xml new file mode 100644 index 00000000000..85402490458 --- /dev/null +++ b/scripts/network/juniper/address-book-entry-getall.xml @@ -0,0 +1,15 @@ + + + + + + +%zone% + + + + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/address-book-entry-getone.xml b/scripts/network/juniper/address-book-entry-getone.xml new file mode 100644 index 00000000000..4b799295c61 --- /dev/null +++ b/scripts/network/juniper/address-book-entry-getone.xml @@ -0,0 +1,18 @@ + + + + + + +%zone% + +
+%entry-name% +
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/scripts/network/juniper/address-pool-add.xml b/scripts/network/juniper/address-pool-add.xml new file mode 100644 index 00000000000..4cbaa91d7cc --- /dev/null +++ b/scripts/network/juniper/address-pool-add.xml @@ -0,0 +1,26 @@ + + + + + + +%address-pool-name% + + +%guest-network-cidr% + +%address-range-name% +%low-address% +%high-address% + + +%primary-dns-address% + + + + + + + + + diff --git a/scripts/network/juniper/address-pool-getall.xml b/scripts/network/juniper/address-pool-getall.xml new file mode 100644 index 00000000000..9a4486c51c7 --- /dev/null +++ b/scripts/network/juniper/address-pool-getall.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/scripts/network/juniper/address-pool-getone.xml b/scripts/network/juniper/address-pool-getone.xml new file mode 100644 index 00000000000..060a5678fc6 --- /dev/null +++ b/scripts/network/juniper/address-pool-getone.xml @@ -0,0 +1,13 @@ + + + + + + +%address-pool-name% + + + + + + diff --git a/scripts/network/juniper/application-add.xml b/scripts/network/juniper/application-add.xml new file mode 100644 index 00000000000..30424949d36 --- /dev/null +++ b/scripts/network/juniper/application-add.xml @@ -0,0 +1,13 @@ + + + + + +%name% +%protocol% +%dest-port% + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/application-getone.xml b/scripts/network/juniper/application-getone.xml new file mode 100644 index 00000000000..b70097b31dc --- /dev/null +++ b/scripts/network/juniper/application-getone.xml @@ -0,0 +1,11 @@ + + + + + +%name% + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/close-configuration.xml b/scripts/network/juniper/close-configuration.xml new file mode 100644 index 00000000000..ed420d893d5 --- /dev/null +++ b/scripts/network/juniper/close-configuration.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/scripts/network/juniper/commit.xml b/scripts/network/juniper/commit.xml new file mode 100644 index 00000000000..673c812a336 --- /dev/null +++ b/scripts/network/juniper/commit.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/scripts/network/juniper/dest-nat-pool-add.xml b/scripts/network/juniper/dest-nat-pool-add.xml new file mode 100644 index 00000000000..9cccfae9af9 --- /dev/null +++ b/scripts/network/juniper/dest-nat-pool-add.xml @@ -0,0 +1,19 @@ + + + + + + + +%pool-name% +
+%private-address% +%dest-port% +
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/scripts/network/juniper/dest-nat-pool-getall.xml b/scripts/network/juniper/dest-nat-pool-getall.xml new file mode 100644 index 00000000000..21df17e3384 --- /dev/null +++ b/scripts/network/juniper/dest-nat-pool-getall.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/dest-nat-pool-getone.xml b/scripts/network/juniper/dest-nat-pool-getone.xml new file mode 100644 index 00000000000..4d10737f29a --- /dev/null +++ b/scripts/network/juniper/dest-nat-pool-getone.xml @@ -0,0 +1,15 @@ + + + + + + + +%pool-name% + + + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/dest-nat-rule-add.xml b/scripts/network/juniper/dest-nat-rule-add.xml new file mode 100644 index 00000000000..f8104ff0968 --- /dev/null +++ b/scripts/network/juniper/dest-nat-rule-add.xml @@ -0,0 +1,38 @@ + + + + + + + +%rule-set% +%from-zone% + +%rule-name% + + +%public-address% + + +%src-port% + + + + + +%pool-name% + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/dest-nat-rule-getall.xml b/scripts/network/juniper/dest-nat-rule-getall.xml new file mode 100644 index 00000000000..8c8eeb2a697 --- /dev/null +++ b/scripts/network/juniper/dest-nat-rule-getall.xml @@ -0,0 +1,15 @@ + + + + + + + +%rule-set% + + + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/dest-nat-rule-getone.xml b/scripts/network/juniper/dest-nat-rule-getone.xml new file mode 100644 index 00000000000..3c2ebf1c9c3 --- /dev/null +++ b/scripts/network/juniper/dest-nat-rule-getone.xml @@ -0,0 +1,20 @@ + + + + + + + +%from-zone% + +%rule-name% + + + + + + + + + + diff --git a/scripts/network/juniper/dynamic-vpn-client-add.xml b/scripts/network/juniper/dynamic-vpn-client-add.xml new file mode 100644 index 00000000000..194e9ed0f32 --- /dev/null +++ b/scripts/network/juniper/dynamic-vpn-client-add.xml @@ -0,0 +1,29 @@ + + + + + + +%client-name% + +%guest-network-cidr% + + +0.0.0.0/0 + + +0.0.0.0/32 + + +1.1.1.1/24 + +%ipsec-vpn-name% + +%username% + + + + + + + diff --git a/scripts/network/juniper/dynamic-vpn-client-getall.xml b/scripts/network/juniper/dynamic-vpn-client-getall.xml new file mode 100644 index 00000000000..e8c7e280fea --- /dev/null +++ b/scripts/network/juniper/dynamic-vpn-client-getall.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/scripts/network/juniper/dynamic-vpn-client-getone.xml b/scripts/network/juniper/dynamic-vpn-client-getone.xml new file mode 100644 index 00000000000..7ddd2ff705b --- /dev/null +++ b/scripts/network/juniper/dynamic-vpn-client-getone.xml @@ -0,0 +1,13 @@ + + + + + + +%client-name% + + + + + + diff --git a/scripts/network/juniper/filter-getone.xml b/scripts/network/juniper/filter-getone.xml new file mode 100644 index 00000000000..b66c2dd8eb4 --- /dev/null +++ b/scripts/network/juniper/filter-getone.xml @@ -0,0 +1,11 @@ + + + + + +%filter-name% + + + + + diff --git a/scripts/network/juniper/filter-term-getone.xml b/scripts/network/juniper/filter-term-getone.xml new file mode 100644 index 00000000000..eb1692e6d41 --- /dev/null +++ b/scripts/network/juniper/filter-term-getone.xml @@ -0,0 +1,14 @@ + + + + + +%filter-name% + +%term-name% + + + + + + diff --git a/scripts/network/juniper/firewall-filter-bytes-getall.xml b/scripts/network/juniper/firewall-filter-bytes-getall.xml new file mode 100644 index 00000000000..af8dce000ab --- /dev/null +++ b/scripts/network/juniper/firewall-filter-bytes-getall.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/scripts/network/juniper/guest-vlan-filter-term-add.xml b/scripts/network/juniper/guest-vlan-filter-term-add.xml new file mode 100644 index 00000000000..26fd2ffb98e --- /dev/null +++ b/scripts/network/juniper/guest-vlan-filter-term-add.xml @@ -0,0 +1,18 @@ + + + + + +%filter-name% + +%term-name% + +%term-name% + + + + + + + + diff --git a/scripts/network/juniper/ike-gateway-add.xml b/scripts/network/juniper/ike-gateway-add.xml new file mode 100644 index 00000000000..6cd78fdeb59 --- /dev/null +++ b/scripts/network/juniper/ike-gateway-add.xml @@ -0,0 +1,21 @@ + + + + + + +%gateway-name% +%ike-policy-name% + +%ike-gateway-hostname% + +%public-interface-name% + +%access-profile-name% + + + + + + + diff --git a/scripts/network/juniper/ike-gateway-getall.xml b/scripts/network/juniper/ike-gateway-getall.xml new file mode 100644 index 00000000000..45e977e3634 --- /dev/null +++ b/scripts/network/juniper/ike-gateway-getall.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/ike-gateway-getone.xml b/scripts/network/juniper/ike-gateway-getone.xml new file mode 100644 index 00000000000..ae7359d3a60 --- /dev/null +++ b/scripts/network/juniper/ike-gateway-getone.xml @@ -0,0 +1,13 @@ + + + + + + +%gateway-name% + + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/ike-policy-add.xml b/scripts/network/juniper/ike-policy-add.xml new file mode 100644 index 00000000000..29b96e7ae64 --- /dev/null +++ b/scripts/network/juniper/ike-policy-add.xml @@ -0,0 +1,18 @@ + + + + + + +%policy-name% +aggressive +%proposal-name% + +%pre-shared-key% + + + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/ike-policy-getall.xml b/scripts/network/juniper/ike-policy-getall.xml new file mode 100644 index 00000000000..237cf6e47b6 --- /dev/null +++ b/scripts/network/juniper/ike-policy-getall.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/ike-policy-getone.xml b/scripts/network/juniper/ike-policy-getone.xml new file mode 100644 index 00000000000..58d146041bf --- /dev/null +++ b/scripts/network/juniper/ike-policy-getone.xml @@ -0,0 +1,13 @@ + + + + + + +%policy-name% + + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/ipsec-vpn-add.xml b/scripts/network/juniper/ipsec-vpn-add.xml new file mode 100644 index 00000000000..08b5bb45f24 --- /dev/null +++ b/scripts/network/juniper/ipsec-vpn-add.xml @@ -0,0 +1,18 @@ + + + + + + +%ipsec-vpn-name% + +%ike-gateway% +%ipsec-policy-name% + +on-traffic + + + + + + diff --git a/scripts/network/juniper/ipsec-vpn-getall.xml b/scripts/network/juniper/ipsec-vpn-getall.xml new file mode 100644 index 00000000000..64dc6b43752 --- /dev/null +++ b/scripts/network/juniper/ipsec-vpn-getall.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/scripts/network/juniper/ipsec-vpn-getone.xml b/scripts/network/juniper/ipsec-vpn-getone.xml new file mode 100644 index 00000000000..bccf8b73f59 --- /dev/null +++ b/scripts/network/juniper/ipsec-vpn-getone.xml @@ -0,0 +1,13 @@ + + + + + + +%ipsec-vpn-name% + + + + + + diff --git a/scripts/network/juniper/login.xml b/scripts/network/juniper/login.xml new file mode 100644 index 00000000000..188a00b47e1 --- /dev/null +++ b/scripts/network/juniper/login.xml @@ -0,0 +1,13 @@ + + + + + +%username% + + +%password% + + + + \ No newline at end of file diff --git a/scripts/network/juniper/open-configuration.xml b/scripts/network/juniper/open-configuration.xml new file mode 100644 index 00000000000..01c0108ac0f --- /dev/null +++ b/scripts/network/juniper/open-configuration.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/private-interface-add.xml b/scripts/network/juniper/private-interface-add.xml new file mode 100644 index 00000000000..616f0428d4b --- /dev/null +++ b/scripts/network/juniper/private-interface-add.xml @@ -0,0 +1,23 @@ + + + + + +%private-interface-name% + + +%vlan-id% +%vlan-id% + + +
+%private-interface-ip% +
+
+
+
+
+
+
+
+
diff --git a/scripts/network/juniper/private-interface-getall.xml b/scripts/network/juniper/private-interface-getall.xml new file mode 100644 index 00000000000..656ce4396cf --- /dev/null +++ b/scripts/network/juniper/private-interface-getall.xml @@ -0,0 +1,11 @@ + + + + + +%private-interface-name% + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/private-interface-getone.xml b/scripts/network/juniper/private-interface-getone.xml new file mode 100644 index 00000000000..957eef50142 --- /dev/null +++ b/scripts/network/juniper/private-interface-getone.xml @@ -0,0 +1,15 @@ + + + + + +%private-interface-name% + + +%vlan-id% + + + + + + diff --git a/scripts/network/juniper/private-interface-with-filters-add.xml b/scripts/network/juniper/private-interface-with-filters-add.xml new file mode 100644 index 00000000000..46a73c7b2df --- /dev/null +++ b/scripts/network/juniper/private-interface-with-filters-add.xml @@ -0,0 +1,31 @@ + + + + + +%private-interface-name% + + +%vlan-id% +%vlan-id% + + + + +%input-filter-name% + + +%output-filter-name% + + +
+%private-interface-ip% +
+
+
+
+
+
+
+
+
diff --git a/scripts/network/juniper/proxy-arp-add.xml b/scripts/network/juniper/proxy-arp-add.xml new file mode 100644 index 00000000000..f308d11a6d0 --- /dev/null +++ b/scripts/network/juniper/proxy-arp-add.xml @@ -0,0 +1,18 @@ + + + + + + + +%public-interface-name% +
+%public-ip-address% +
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/scripts/network/juniper/proxy-arp-getall.xml b/scripts/network/juniper/proxy-arp-getall.xml new file mode 100644 index 00000000000..8ec41723b2b --- /dev/null +++ b/scripts/network/juniper/proxy-arp-getall.xml @@ -0,0 +1,13 @@ + + + + + + +%interface-name% + + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/proxy-arp-getone.xml b/scripts/network/juniper/proxy-arp-getone.xml new file mode 100644 index 00000000000..2d42a8b77c8 --- /dev/null +++ b/scripts/network/juniper/proxy-arp-getone.xml @@ -0,0 +1,18 @@ + + + + + + + +%public-interface-name% +
+%public-ip-address% +
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/scripts/network/juniper/public-ip-filter-term-add.xml b/scripts/network/juniper/public-ip-filter-term-add.xml new file mode 100644 index 00000000000..4687759e3ac --- /dev/null +++ b/scripts/network/juniper/public-ip-filter-term-add.xml @@ -0,0 +1,23 @@ + + + + + +%filter-name% + +%term-name% + +<%address-type%> +%ip-address% + + + +%term-name% + + + + + + + + diff --git a/scripts/network/juniper/rollback.xml b/scripts/network/juniper/rollback.xml new file mode 100644 index 00000000000..cb9c99c5290 --- /dev/null +++ b/scripts/network/juniper/rollback.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/scripts/network/juniper/security-policy-add.xml b/scripts/network/juniper/security-policy-add.xml new file mode 100644 index 00000000000..02c88e389c8 --- /dev/null +++ b/scripts/network/juniper/security-policy-add.xml @@ -0,0 +1,29 @@ + + + + + + +%from-zone% +%to-zone% + +%policy-name% + +%src-address% +%dest-address% +%applications% + + + +%tunnel% + + + + + + + + + + + diff --git a/scripts/network/juniper/security-policy-getall.xml b/scripts/network/juniper/security-policy-getall.xml new file mode 100644 index 00000000000..b0de154f8ec --- /dev/null +++ b/scripts/network/juniper/security-policy-getall.xml @@ -0,0 +1,14 @@ + + + + + + +%from-zone% +%to-zone% + + + + + + diff --git a/scripts/network/juniper/security-policy-getone.xml b/scripts/network/juniper/security-policy-getone.xml new file mode 100644 index 00000000000..29782677a77 --- /dev/null +++ b/scripts/network/juniper/security-policy-getone.xml @@ -0,0 +1,17 @@ + + + + + + +%from-zone% +%to-zone% + +%policy-name% + + + + + + + diff --git a/scripts/network/juniper/security-policy-group.xml b/scripts/network/juniper/security-policy-group.xml new file mode 100644 index 00000000000..e13c6374b34 --- /dev/null +++ b/scripts/network/juniper/security-policy-group.xml @@ -0,0 +1,14 @@ + + + + + + +%from-zone% +%to-zone% + + + + + + diff --git a/scripts/network/juniper/security-policy-rename.xml b/scripts/network/juniper/security-policy-rename.xml new file mode 100644 index 00000000000..14218aae190 --- /dev/null +++ b/scripts/network/juniper/security-policy-rename.xml @@ -0,0 +1,17 @@ + + + + + + +%from-zone% +%to-zone% + +%policy-name% + + + + + + + diff --git a/scripts/network/juniper/src-nat-pool-add.xml b/scripts/network/juniper/src-nat-pool-add.xml new file mode 100644 index 00000000000..9d863e7117c --- /dev/null +++ b/scripts/network/juniper/src-nat-pool-add.xml @@ -0,0 +1,16 @@ + + + + + + + +%pool-name% +
%address%
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/scripts/network/juniper/src-nat-pool-getone.xml b/scripts/network/juniper/src-nat-pool-getone.xml new file mode 100644 index 00000000000..e8e6fd05f28 --- /dev/null +++ b/scripts/network/juniper/src-nat-pool-getone.xml @@ -0,0 +1,15 @@ + + + + + + + +%pool-name% + + + + + + + \ No newline at end of file diff --git a/scripts/network/juniper/src-nat-rule-add.xml b/scripts/network/juniper/src-nat-rule-add.xml new file mode 100644 index 00000000000..b034b504d69 --- /dev/null +++ b/scripts/network/juniper/src-nat-rule-add.xml @@ -0,0 +1,30 @@ + + + + + + + +%rule-set% +%from-zone% +%to-zone% + +%rule-name% + +%private-subnet% + + + +%pool-name% + + + + + + + + + + + + diff --git a/scripts/network/juniper/src-nat-rule-getall.xml b/scripts/network/juniper/src-nat-rule-getall.xml new file mode 100644 index 00000000000..9f7281fa401 --- /dev/null +++ b/scripts/network/juniper/src-nat-rule-getall.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/scripts/network/juniper/src-nat-rule-getone.xml b/scripts/network/juniper/src-nat-rule-getone.xml new file mode 100644 index 00000000000..c83762402f8 --- /dev/null +++ b/scripts/network/juniper/src-nat-rule-getone.xml @@ -0,0 +1,20 @@ + + + + + + + +%from-zone% + +%rule-name% + + + + + + + + + + diff --git a/scripts/network/juniper/static-nat-rule-add.xml b/scripts/network/juniper/static-nat-rule-add.xml new file mode 100644 index 00000000000..f89d1a5e48b --- /dev/null +++ b/scripts/network/juniper/static-nat-rule-add.xml @@ -0,0 +1,31 @@ + + + + + + + +%rule-set% +%from-zone% + +%rule-name% + + +%original-ip% + + + + +%translated-ip% + + + + + + + + + + + + diff --git a/scripts/network/juniper/static-nat-rule-getall.xml b/scripts/network/juniper/static-nat-rule-getall.xml new file mode 100644 index 00000000000..f6a4414e769 --- /dev/null +++ b/scripts/network/juniper/static-nat-rule-getall.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/scripts/network/juniper/static-nat-rule-getone.xml b/scripts/network/juniper/static-nat-rule-getone.xml new file mode 100644 index 00000000000..8fdfc78fb60 --- /dev/null +++ b/scripts/network/juniper/static-nat-rule-getone.xml @@ -0,0 +1,20 @@ + + + + + + + +%rule-set% + +%rule-name% + + + + + + + + + + diff --git a/scripts/network/juniper/test.xml b/scripts/network/juniper/test.xml new file mode 100644 index 00000000000..e75e422bb38 --- /dev/null +++ b/scripts/network/juniper/test.xml @@ -0,0 +1,16 @@ + + + + + + + +trust + + + + + + + + diff --git a/scripts/network/juniper/zone-interface-add.xml b/scripts/network/juniper/zone-interface-add.xml new file mode 100644 index 00000000000..588b7449978 --- /dev/null +++ b/scripts/network/juniper/zone-interface-add.xml @@ -0,0 +1,17 @@ + + + + + + +%private-zone-name% + +%zone-interface-name% + + + + + + + + diff --git a/scripts/network/juniper/zone-interface-getone.xml b/scripts/network/juniper/zone-interface-getone.xml new file mode 100644 index 00000000000..a7ac53084a3 --- /dev/null +++ b/scripts/network/juniper/zone-interface-getone.xml @@ -0,0 +1,17 @@ + + + + + + +%private-zone-name% + +%zone-interface-name% + + + + + + + + diff --git a/server/src/com/cloud/agent/VmmAgentShell.java b/server/src/com/cloud/agent/VmmAgentShell.java new file mode 100644 index 00000000000..3aeb7acc2f4 --- /dev/null +++ b/server/src/com/cloud/agent/VmmAgentShell.java @@ -0,0 +1,530 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +package com.cloud.agent; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.Agent.ExitStatus; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.StartupVMMAgentCommand; +import com.cloud.agent.dao.StorageComponent; +import com.cloud.agent.dao.impl.PropertiesStorage; +import com.cloud.agent.transport.Request; +import com.cloud.resource.ServerResource; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.ProcessUtil; +import com.cloud.utils.PropertiesUtil; +import com.cloud.utils.backoff.BackoffAlgorithm; +import com.cloud.utils.backoff.impl.ConstantTimeBackoff; +import com.cloud.utils.component.Adapters; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.MacAddress; +import com.cloud.utils.nio.HandlerFactory; +import com.cloud.utils.nio.Link; +import com.cloud.utils.nio.NioServer; +import com.cloud.utils.nio.Task; +import com.cloud.utils.nio.Task.Type; + +/** + * Implementation of agent shell to run the agents on System Center Virtual Machine manager + **/ + +public class VmmAgentShell implements IAgentShell, HandlerFactory { + + private static final Logger s_logger = Logger.getLogger(VmmAgentShell.class.getName()); + private final Properties _properties = new Properties(); + private final Map _cmdLineProperties = new HashMap(); + private StorageComponent _storage; + private BackoffAlgorithm _backoff; + private String _version; + private String _zone; + private String _pod; + private String _cluster; + private String _host; + private String _privateIp; + private int _port; + private int _proxyPort; + private int _workers; + private String _guid; + static private NioServer _connection; + static private int _listenerPort=9000; + private int _nextAgentId = 1; + private volatile boolean _exit = false; + private int _pingRetries; + private Thread _consoleProxyMain = null; + private final List _agents = new ArrayList(); + + public VmmAgentShell() { + } + + @Override + public Properties getProperties() { + return _properties; + } + + @Override + public BackoffAlgorithm getBackoffAlgorithm() { + return _backoff; + } + + @Override + public int getPingRetries() { + return _pingRetries; + } + + @Override + public String getZone() { + return _zone; + } + + @Override + public String getPod() { + return _pod; + } + + @Override + public String getHost() { + return _host; + } + + @Override + public String getPrivateIp() { + return _privateIp; + } + + @Override + public int getPort() { + return _port; + } + + @Override + public int getProxyPort() { + return _proxyPort; + } + + @Override + public int getWorkers() { + return _workers; + } + + @Override + public String getGuid() { + return _guid; + } + + @Override + public void upgradeAgent(String url) { + // TODO Auto-generated method stub + + } + + @Override + public String getVersion() { + return _version; + } + + @Override + public Map getCmdLineProperties() { + // TODO Auto-generated method stub + return _cmdLineProperties; + } + + public String getProperty(String prefix, String name) { + if(prefix != null) + return _properties.getProperty(prefix + "." + name); + + return _properties.getProperty(name); + } + + @Override + public String getPersistentProperty(String prefix, String name) { + if(prefix != null) + return _storage.get(prefix + "." + name); + return _storage.get(name); + } + + @Override + public void setPersistentProperty(String prefix, String name, String value) { + if(prefix != null) + _storage.persist(prefix + "." + name, value); + else + _storage.persist(name, value); + } + + private void loadProperties() throws ConfigurationException { + final File file = PropertiesUtil.findConfigFile("agent.properties"); + if (file == null) { + throw new ConfigurationException("Unable to find agent.properties."); + } + + s_logger.info("agent.properties found at " + file.getAbsolutePath()); + + try { + _properties.load(new FileInputStream(file)); + } catch (final FileNotFoundException ex) { + throw new CloudRuntimeException("Cannot find the file: " + file.getAbsolutePath(), ex); + } catch (final IOException ex) { + throw new CloudRuntimeException("IOException in reading " + file.getAbsolutePath(), ex); + } + } + + protected boolean parseCommand(final String[] args) throws ConfigurationException { + String host = null; + String workers = null; + String port = null; + String zone = null; + String pod = null; + String guid = null; + for (int i = 0; i < args.length; i++) { + final String[] tokens = args[i].split("="); + if (tokens.length != 2) { + System.out.println("Invalid Parameter: " + args[i]); + continue; + } + + // save command line properties + _cmdLineProperties.put(tokens[0], tokens[1]); + + if (tokens[0].equalsIgnoreCase("port")) { + port = tokens[1]; + } else if (tokens[0].equalsIgnoreCase("threads")) { + workers = tokens[1]; + } else if (tokens[0].equalsIgnoreCase("host")) { + host = tokens[1]; + } else if(tokens[0].equalsIgnoreCase("zone")) { + zone = tokens[1]; + } else if(tokens[0].equalsIgnoreCase("pod")) { + pod = tokens[1]; + } else if(tokens[0].equalsIgnoreCase("guid")) { + guid = tokens[1]; + } else if(tokens[0].equalsIgnoreCase("eth1ip")) { + _privateIp = tokens[1]; + } + } + + if (port == null) { + port = getProperty(null, "port"); + } + + _port = NumbersUtil.parseInt(port, 8250); + + _proxyPort = NumbersUtil.parseInt(getProperty(null, "consoleproxy.httpListenPort"), 443); + + if (workers == null) { + workers = getProperty(null, "workers"); + } + + _workers = NumbersUtil.parseInt(workers, 5); + + if (host == null) { + host = getProperty(null, "host"); + } + + if (host == null) { + host = "localhost"; + } + _host = host; + + if(zone != null) + _zone = zone; + else + _zone = getProperty(null, "zone"); + if (_zone == null || (_zone.startsWith("@") && _zone.endsWith("@"))) { + _zone = "default"; + } + + if(pod != null) + _pod = pod; + else + _pod = getProperty(null, "pod"); + if (_pod == null || (_pod.startsWith("@") && _pod.endsWith("@"))) { + _pod = "default"; + } + + if (_host == null || (_host.startsWith("@") && _host.endsWith("@"))) { + throw new ConfigurationException("Host is not configured correctly: " + _host); + } + + final String retries = getProperty(null, "ping.retries"); + _pingRetries = NumbersUtil.parseInt(retries, 5); + + String value = getProperty(null, "developer"); + boolean developer = Boolean.parseBoolean(value); + + if(guid != null) + _guid = guid; + else + _guid = getProperty(null, "guid"); + if (_guid == null) { + if (!developer) { + throw new ConfigurationException("Unable to find the guid"); + } + _guid = MacAddress.getMacAddress().toString(":"); + } + + return true; + } + + private void launchAgentFromTypeInfo() throws ConfigurationException { + String typeInfo = getProperty(null, "type"); + if (typeInfo == null) { + s_logger.error("Unable to retrieve the type"); + throw new ConfigurationException("Unable to retrieve the type of this agent."); + } + s_logger.trace("Launching agent based on type=" + typeInfo); + } + + private void launchAgent() throws ConfigurationException { + String resourceClassNames = getProperty(null, "resource"); + s_logger.trace("resource=" + resourceClassNames); + if(resourceClassNames != null) { + launchAgentFromClassInfo(resourceClassNames); + return; + } + + launchAgentFromTypeInfo(); + } + + private void init(String[] args) throws ConfigurationException{ + + final ComponentLocator locator = ComponentLocator.getLocator("agent"); + + final Class c = this.getClass(); + _version = c.getPackage().getImplementationVersion(); + if (_version == null) { + throw new CloudRuntimeException("Unable to find the implementation version of this agent"); + } + s_logger.info("Implementation Version is " + _version); + + parseCommand(args); + + _storage = locator.getManager(StorageComponent.class); + if (_storage == null) { + s_logger.info("Defaulting to using properties file for storage"); + _storage = new PropertiesStorage(); + _storage.configure("Storage", new HashMap()); + } + + // merge with properties from command line to let resource access command line parameters + for(Map.Entry cmdLineProp : getCmdLineProperties().entrySet()) { + _properties.put(cmdLineProp.getKey(), cmdLineProp.getValue()); + } + + final Adapters adapters = locator.getAdapters(BackoffAlgorithm.class); + final Enumeration en = adapters.enumeration(); + while (en.hasMoreElements()) { + _backoff = (BackoffAlgorithm)en.nextElement(); + break; + } + if (en.hasMoreElements()) { + s_logger.info("More than one backoff algorithm specified. Using the first one "); + } + + if (_backoff == null) { + s_logger.info("Defaulting to the constant time backoff algorithm"); + _backoff = new ConstantTimeBackoff(); + _backoff.configure("ConstantTimeBackoff", new HashMap()); + } + } + + private void launchAgentFromClassInfo(String resourceClassNames) throws ConfigurationException { + String[] names = resourceClassNames.split("\\|"); + for(String name: names) { + Class impl; + try { + impl = Class.forName(name); + final Constructor constructor = impl.getDeclaredConstructor(); + constructor.setAccessible(true); + ServerResource resource = (ServerResource)constructor.newInstance(); + launchAgent(getNextAgentId(), resource); + } catch (final ClassNotFoundException e) { + throw new ConfigurationException("Resource class not found: " + name); + } catch (final SecurityException e) { + throw new ConfigurationException("Security excetion when loading resource: " + name); + } catch (final NoSuchMethodException e) { + throw new ConfigurationException("Method not found excetion when loading resource: " + name); + } catch (final IllegalArgumentException e) { + throw new ConfigurationException("Illegal argument excetion when loading resource: " + name); + } catch (final InstantiationException e) { + throw new ConfigurationException("Instantiation excetion when loading resource: " + name); + } catch (final IllegalAccessException e) { + throw new ConfigurationException("Illegal access exception when loading resource: " + name); + } catch (final InvocationTargetException e) { + throw new ConfigurationException("Invocation target exception when loading resource: " + name); + } + } + } + + private void launchAgent(int localAgentId, ServerResource resource) throws ConfigurationException { + // we don't track agent after it is launched for now + Agent agent = new Agent(this, localAgentId, resource); + _agents.add(agent); + agent.start(); + } + + public synchronized int getNextAgentId() { + return _nextAgentId++; + } + + private void run(String[] args) { + + try { + System.setProperty("java.net.preferIPv4Stack","true"); + loadProperties(); + init(args); + + String instance = getProperty(null, "instance"); + if (instance == null) { + instance = ""; + } else { + instance += "."; + } + + // TODO need to do this check. For Agentshell running on windows needs different approach + //final String run = "agent." + instance + "pid"; + //s_logger.debug("Checking to see if " + run + "exists."); + //ProcessUtil.pidCheck(run); + + + // TODO: For Hyper-V agent.properties need to be revamped to support multiple agents + // corresponding to multiple clusters but running on a SCVMM host + + // read the persistent storage and launch the agents + //launchAgent(); + + // FIXME get rid of this approach of agent listening for boot strap commands from the management server + + // now listen for bootstrap request from the management server and launch agents + _connection = new NioServer("VmmAgentShell", _listenerPort, 1, this); + _connection.start(); + s_logger.info("SCVMM agent is listening on port " +_listenerPort + " for bootstrap command from management server"); + while(_connection.isRunning()); + } catch(final ConfigurationException e) { + s_logger.error("Unable to start agent: " + e.getMessage()); + System.out.println("Unable to start agent: " + e.getMessage()); + System.exit(ExitStatus.Configuration.value()); + } catch (final Exception e) { + s_logger.error("Unable to start agent: ", e); + System.out.println("Unable to start agent: " + e.getMessage()); + System.exit(ExitStatus.Error.value()); + } + } + + @Override + public Task create(com.cloud.utils.nio.Task.Type type, Link link, + byte[] data) { + return new AgentBootStrapHandler(type, link, data); + } + + public void stop() { + _exit = true; + if(_consoleProxyMain != null) { + _consoleProxyMain.interrupt(); + } + } + + public static void main(String[] args) { + + VmmAgentShell shell = new VmmAgentShell(); + Runtime.getRuntime().addShutdownHook(new ShutdownThread(shell)); + shell.run(args); + } + + // class to handle the bootstrap command from the management server + private class AgentBootStrapHandler extends Task { + + public AgentBootStrapHandler(Task.Type type, Link link, byte[] data) { + super(type, link, data); + } + + @Override + protected void doTask(Task task) throws Exception { + final Type type = task.getType(); + s_logger.info("recieved task of type "+ type.toString() +" to handle in BootStrapTakHandler"); + if (type == Task.Type.DATA) + { + final byte[] data = task.getData(); + final Request request = Request.parse(data); + final Command cmd = request.getCommand(); + + if (cmd instanceof StartupVMMAgentCommand) { + + StartupVMMAgentCommand vmmCmd = (StartupVMMAgentCommand) cmd; + + _zone = Long.toString(vmmCmd.getDataCenter()); + _cmdLineProperties.put("zone", _zone); + + _pod = Long.toString(vmmCmd.getPod()); + _cmdLineProperties.put("pod", _pod); + + _cluster = vmmCmd.getClusterName(); + _cmdLineProperties.put("cluster", _cluster); + + _guid = vmmCmd.getGuid(); + _cmdLineProperties.put("guid", _guid); + + _host = vmmCmd.getManagementServerIP(); + _port = NumbersUtil.parseInt(vmmCmd.getport(), 8250); + + s_logger.info("Recieved boot strap command from management server with parameters " + + " Zone:"+ _zone + " "+ + " Cluster:"+ _cluster + " "+ + " pod:"+_pod + " "+ + " host:"+ _host +" "+ + " port:"+_port); + + launchAgentFromClassInfo("com.cloud.hypervisor.hyperv.resource.HypervResource"); + + // TODO: persist the info in agent.properties for agent restarts + } + } + } + } + + private static class ShutdownThread extends Thread { + VmmAgentShell _shell; + public ShutdownThread(VmmAgentShell shell) { + this._shell = shell; + } + + @Override + public void run() { + _shell.stop(); + } + } + +} \ No newline at end of file diff --git a/server/src/com/cloud/api/commands/AddExternalFirewallCmd.java b/server/src/com/cloud/api/commands/AddExternalFirewallCmd.java new file mode 100644 index 00000000000..c42a2f988b1 --- /dev/null +++ b/server/src/com/cloud/api/commands/AddExternalFirewallCmd.java @@ -0,0 +1,117 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.BaseCmd.CommandType; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.Host; +import com.cloud.network.ExternalNetworkManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.ExternalFirewallResponse; +import com.cloud.user.Account; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.exception.CloudRuntimeException; + +@Implementation(description="Adds an external firewall appliance", responseObject = ExternalFirewallResponse.class) +public class AddExternalFirewallCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(AddExternalFirewallCmd.class.getName()); + private static final String s_name = "addexternalfirewallresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required = true, description="Zone in which to add the external firewall appliance.") + private Long zoneId; + + @Parameter(name=ApiConstants.URL, type=CommandType.STRING, required = true, description="URL of the external firewall appliance.") + private String url; + + @Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, required = true, description="Username of the external firewall appliance.") + private String username; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required = true, description="Password of the external firewall appliance.") + private String password; + + @Parameter(name=ApiConstants.EXTERNAL_FIREWALL_TYPE, type=CommandType.STRING, description="External firewall type. Now supports JuniperSRX.") + private String type; + /////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getZoneId() { + return zoneId; + } + + public String getUrl() { + return url; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getType() { + return type; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + try { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + ExternalNetworkManager externalNetworkMgr = locator.getManager(ExternalNetworkManager.class); + Host externalFirewall = externalNetworkMgr.addExternalFirewall(this); + ExternalFirewallResponse response = externalNetworkMgr.createExternalFirewallResponse(externalFirewall); + response.setObjectName("externalfirewall"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException ipve) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, ipve.getMessage()); + } catch (CloudRuntimeException cre) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, cre.getMessage()); + } + } +} + diff --git a/server/src/com/cloud/api/commands/AddExternalLoadBalancerCmd.java b/server/src/com/cloud/api/commands/AddExternalLoadBalancerCmd.java new file mode 100644 index 00000000000..5ec3ace41e7 --- /dev/null +++ b/server/src/com/cloud/api/commands/AddExternalLoadBalancerCmd.java @@ -0,0 +1,118 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.Host; +import com.cloud.network.ExternalNetworkManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.ExternalLoadBalancerResponse; +import com.cloud.user.Account; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.exception.CloudRuntimeException; + +@Implementation(description="Adds an external load balancer appliance.", responseObject = ExternalLoadBalancerResponse.class) +public class AddExternalLoadBalancerCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(AddExternalLoadBalancerCmd.class.getName()); + private static final String s_name = "addexternalloadbalancerresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required = true, description="Zone in which to add the external load balancer appliance.") + private Long zoneId; + + @Parameter(name=ApiConstants.URL, type=CommandType.STRING, required = true, description="URL of the external load balancer appliance.") + private String url; + + @Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, required = true, description="Username of the external load balancer appliance.") + private String username; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required = true, description="Password of the external load balancer appliance.") + private String password; + + @Parameter(name=ApiConstants.EXTERNAL_LB_TYPE, type=CommandType.STRING, description="External load balancer type. Now supports F5BigIP.") + private String type; + + /////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getZoneId() { + return zoneId; + } + + public String getUrl() { + return url; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getType() { + return type; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + try { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + ExternalNetworkManager externalNetworkMgr = locator.getManager(ExternalNetworkManager.class); + Host externalLoadBalancer = externalNetworkMgr.addExternalLoadBalancer(this); + ExternalLoadBalancerResponse response = externalNetworkMgr.createExternalLoadBalancerResponse(externalLoadBalancer); + response.setObjectName("externalloadbalancer"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException ipve) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, ipve.getMessage()); + } catch (CloudRuntimeException cre) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, cre.getMessage()); + } + } + +} + diff --git a/server/src/com/cloud/api/commands/AddNetworkDeviceCmd.java b/server/src/com/cloud/api/commands/AddNetworkDeviceCmd.java new file mode 100644 index 00000000000..77b0f3ea4e3 --- /dev/null +++ b/server/src/com/cloud/api/commands/AddNetworkDeviceCmd.java @@ -0,0 +1,79 @@ +package com.cloud.api.commands; + +import java.util.Map; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.network.NetworkDeviceManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.NetworkDeviceResponse; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.exception.CloudRuntimeException; + +@Implementation(description="Adds a network device of one of the following types: ExternalDhcp, ExternalFirewall, ExternalLoadBalancer, PxeServer", responseObject = NetworkDeviceResponse.class) +public class AddNetworkDeviceCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(AddNetworkDeviceCmd.class); + private static final String s_name = "addnetworkdeviceresponse"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NETWORK_DEVICE_TYPE, type = CommandType.STRING, description = "Network device type, now supports ExternalDhcp, ExternalFirewall, ExternalLoadBalancer, PxeServer") + private String type; + + @Parameter(name = ApiConstants.NETWORK_DEVICE_PARAMETER_LIST, type = CommandType.MAP, description = "parameters for network device") + private Map paramList; + + + public String getType() { + return type; + } + + public Map getParamList() { + return paramList; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, + ResourceAllocationException { + try { + NetworkDeviceManager nwDeviceMgr; + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + nwDeviceMgr = locator.getManager(NetworkDeviceManager.class); + Host device = nwDeviceMgr.addNetworkDevice(this); + NetworkDeviceResponse response = nwDeviceMgr.getApiResponse(device); + response.setObjectName("networkdevice"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException ipve) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, ipve.getMessage()); + } catch (CloudRuntimeException cre) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, cre.getMessage()); + } + + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/server/src/com/cloud/api/commands/AddTrafficMonitorCmd.java b/server/src/com/cloud/api/commands/AddTrafficMonitorCmd.java new file mode 100644 index 00000000000..c6e0e1ff125 --- /dev/null +++ b/server/src/com/cloud/api/commands/AddTrafficMonitorCmd.java @@ -0,0 +1,98 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.Host; +import com.cloud.network.NetworkUsageManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.ExternalFirewallResponse; +import com.cloud.server.api.response.TrafficMonitorResponse; +import com.cloud.user.Account; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.exception.CloudRuntimeException; + +@Implementation(description="Adds Traffic Monitor Host for Direct Network Usage", responseObject = ExternalFirewallResponse.class) +public class AddTrafficMonitorCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(AddTrafficMonitorCmd.class.getName()); + private static final String s_name = "addtrafficmonitorresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required = true, description="Zone in which to add the external firewall appliance.") + private Long zoneId; + + + @Parameter(name=ApiConstants.URL, type=CommandType.STRING, required = true, description="URL of the traffic monitor Host") + private String url; + + /////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getZoneId() { + return zoneId; + } + + public String getUrl() { + return url; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + try { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetworkUsageManager networkUsageMgr = locator.getManager(NetworkUsageManager.class); + Host trafficMoinitor = networkUsageMgr.addTrafficMonitor(this); + TrafficMonitorResponse response = networkUsageMgr.getApiResponse(trafficMoinitor); + response.setObjectName("trafficmonitor"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException ipve) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, ipve.getMessage()); + } catch (CloudRuntimeException cre) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, cre.getMessage()); + } + } +} + diff --git a/server/src/com/cloud/api/commands/DeleteExternalFirewallCmd.java b/server/src/com/cloud/api/commands/DeleteExternalFirewallCmd.java new file mode 100644 index 00000000000..330870d7841 --- /dev/null +++ b/server/src/com/cloud/api/commands/DeleteExternalFirewallCmd.java @@ -0,0 +1,87 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.SuccessResponse; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.ExternalNetworkManager; +import com.cloud.server.ManagementService; +import com.cloud.user.Account; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="Deletes an external firewall appliance.", responseObject = SuccessResponse.class) +public class DeleteExternalFirewallCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DeleteExternalFirewallCmd.class.getName()); + private static final String s_name = "deleteexternalfirewallresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.LONG, required = true, description="Id of the external firewall appliance.") + private Long id; + + /////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + try { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + ExternalNetworkManager externalNetworkMgr = locator.getManager(ExternalNetworkManager.class); + boolean result = externalNetworkMgr.deleteExternalFirewall(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to delete external firewall."); + } + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, "Failed to delete external firewall."); + } + } +} diff --git a/server/src/com/cloud/api/commands/DeleteExternalLoadBalancerCmd.java b/server/src/com/cloud/api/commands/DeleteExternalLoadBalancerCmd.java new file mode 100644 index 00000000000..6e751f6d3aa --- /dev/null +++ b/server/src/com/cloud/api/commands/DeleteExternalLoadBalancerCmd.java @@ -0,0 +1,87 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.SuccessResponse; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.ExternalNetworkManager; +import com.cloud.server.ManagementService; +import com.cloud.user.Account; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="Deletes an external load balancer appliance.", responseObject = SuccessResponse.class) +public class DeleteExternalLoadBalancerCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DeleteExternalLoadBalancerCmd.class.getName()); + private static final String s_name = "deleteexternalloadbalancerresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.LONG, required = true, description="Id of the external loadbalancer appliance.") + private Long id; + + /////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + try { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + ExternalNetworkManager externalNetworkMgr = locator.getManager(ExternalNetworkManager.class); + boolean result = externalNetworkMgr.deleteExternalLoadBalancer(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to delete external load balancer."); + } + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, "Failed to delete external load balancer."); + } + } +} diff --git a/server/src/com/cloud/api/commands/DeleteNetworkDeviceCmd.java b/server/src/com/cloud/api/commands/DeleteNetworkDeviceCmd.java new file mode 100644 index 00000000000..6aa071fc78d --- /dev/null +++ b/server/src/com/cloud/api/commands/DeleteNetworkDeviceCmd.java @@ -0,0 +1,72 @@ +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.SuccessResponse; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.NetworkDeviceManager; +import com.cloud.server.ManagementService; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.exception.CloudRuntimeException; + +@Implementation(description="Deletes network device.", responseObject=SuccessResponse.class) +public class DeleteNetworkDeviceCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DeleteNetworkDeviceCmd.class); + private static final String s_name = "deletenetworkdeviceresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.LONG, required=true, description = "Id of network device to delete") + private Long id; + + + public Long getId() { + return id; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, + ResourceAllocationException { + try { + NetworkDeviceManager nwDeviceMgr; + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + nwDeviceMgr = locator.getManager(NetworkDeviceManager.class); + boolean result = nwDeviceMgr.deleteNetworkDevice(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to delete network device:" + getId()); + } + } catch (InvalidParameterValueException ipve) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, ipve.getMessage()); + } catch (CloudRuntimeException cre) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, cre.getMessage()); + } + + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/server/src/com/cloud/api/commands/DeleteTrafficMonitorCmd.java b/server/src/com/cloud/api/commands/DeleteTrafficMonitorCmd.java new file mode 100644 index 00000000000..d834206ddb2 --- /dev/null +++ b/server/src/com/cloud/api/commands/DeleteTrafficMonitorCmd.java @@ -0,0 +1,87 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.SuccessResponse; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.NetworkUsageManager; +import com.cloud.server.ManagementService; +import com.cloud.user.Account; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="Deletes an traffic monitor host.", responseObject = SuccessResponse.class) +public class DeleteTrafficMonitorCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DeleteTrafficMonitorCmd.class.getName()); + private static final String s_name = "deletetrafficmonitorresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.LONG, required = true, description="Id of the Traffic Monitor Host.") + private Long id; + + /////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + try { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetworkUsageManager _networkUsageMgr = locator.getManager(NetworkUsageManager.class); + boolean result = _networkUsageMgr.deleteTrafficMonitor(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to delete traffic monitor."); + } + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, "Failed to delete traffic monitor."); + } + } +} diff --git a/server/src/com/cloud/api/commands/GenerateUsageRecordsCmd.java b/server/src/com/cloud/api/commands/GenerateUsageRecordsCmd.java new file mode 100644 index 00000000000..86835979fb7 --- /dev/null +++ b/server/src/com/cloud/api/commands/GenerateUsageRecordsCmd.java @@ -0,0 +1,95 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import java.util.Date; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.SuccessResponse; +import com.cloud.server.ManagementServerExt; +import com.cloud.user.Account; + +@Implementation(description="Generates usage records", responseObject=SuccessResponse.class) +public class GenerateUsageRecordsCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(GenerateUsageRecordsCmd.class.getName()); + + private static final String s_name = "generateusagerecordsresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="List events for the specified domain.") + private Long domainId; + + @Parameter(name=ApiConstants.END_DATE, type=CommandType.DATE, required=true, description="End date range for usage record query. Use yyyy-MM-dd as the date format, e.g. startDate=2009-06-03.") + private Date endDate; + + @Parameter(name=ApiConstants.START_DATE, type=CommandType.DATE, required=true, description="Start date range for usage record query. Use yyyy-MM-dd as the date format, e.g. startDate=2009-06-01.") + private Date startDate; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getDomainId() { + return domainId; + } + + public Date getEndDate() { + return endDate; + } + + public Date getStartDate() { + return startDate; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + ManagementServerExt _mgrExt = (ManagementServerExt)_mgr; + boolean result = _mgrExt.generateUsageRecords(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to generate usage records"); + } + } +} diff --git a/server/src/com/cloud/api/commands/GetUsageRecordsCmd.java b/server/src/com/cloud/api/commands/GetUsageRecordsCmd.java new file mode 100644 index 00000000000..5f7d0707792 --- /dev/null +++ b/server/src/com/cloud/api/commands/GetUsageRecordsCmd.java @@ -0,0 +1,257 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.ApiDBUtils; +import com.cloud.api.BaseCmd; +import com.cloud.api.BaseListCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.response.ListResponse; +import com.cloud.server.ManagementServerExt; +import com.cloud.server.api.response.UsageRecordResponse; +import com.cloud.usage.UsageTypes; +import com.cloud.usage.UsageVO; + +@Implementation(description="Lists usage records for accounts", responseObject=UsageRecordResponse.class) +public class GetUsageRecordsCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(GetUsageRecordsCmd.class.getName()); + + private static final String s_name = "listusagerecordsresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="List usage records for the specified user.") + private String accountName; + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="List usage records for the specified domain.") + private Long domainId; + + @Parameter(name=ApiConstants.END_DATE, type=CommandType.DATE, required=true, description="End date range for usage record query. Use yyyy-MM-dd as the date format, e.g. startDate=2009-06-03.") + private Date endDate; + + @Parameter(name=ApiConstants.START_DATE, type=CommandType.DATE, required=true, description="Start date range for usage record query. Use yyyy-MM-dd as the date format, e.g. startDate=2009-06-01.") + private Date startDate; + + @Parameter(name=ApiConstants.ACCOUNT_ID, type=CommandType.LONG, description="List usage records for the specified account") + private Long accountId; + + @Parameter(name=ApiConstants.TYPE, type=CommandType.LONG, description="List usage records for the specified usage type") + private Long usageType; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public Date getEndDate() { + return endDate; + } + + public Date getStartDate() { + return startDate; + } + + public Long getAccountId() { + return accountId; + } + + public Long getUsageType() { + return usageType; + } + + ///////////////////////////////////////////////////// + /////////////// Misc parameters /////////////////// + ///////////////////////////////////////////////////// + + private TimeZone usageTimezone; + + public TimeZone getUsageTimezone() { + return usageTimezone; + } + + public void setUsageTimezone(TimeZone tz) { + this.usageTimezone = tz; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + public String getDateStringInternal(Date inputDate) { + if (inputDate == null) return null; + + TimeZone tz = getUsageTimezone(); + Calendar cal = Calendar.getInstance(tz); + cal.setTime(inputDate); + + StringBuffer sb = new StringBuffer(); + sb.append(cal.get(Calendar.YEAR)+"-"); + + int month = cal.get(Calendar.MONTH) + 1; + if (month < 10) { + sb.append("0" + month + "-"); + } else { + sb.append(month+"-"); + } + + int day = cal.get(Calendar.DAY_OF_MONTH); + if (day < 10) { + sb.append("0" + day); + } else { + sb.append(""+day); + } + + sb.append("'T'"); + + int hour = cal.get(Calendar.HOUR_OF_DAY); + if (hour < 10) { + sb.append("0" + hour + ":"); + } else { + sb.append(hour+":"); + } + + int minute = cal.get(Calendar.MINUTE); + if (minute < 10) { + sb.append("0" + minute + ":"); + } else { + sb.append(minute+":"); + } + + int seconds = cal.get(Calendar.SECOND); + if (seconds < 10) { + sb.append("0" + seconds); + } else { + sb.append(""+seconds); + } + + double offset = cal.get(Calendar.ZONE_OFFSET); + if (tz.inDaylightTime(inputDate)) { + offset += (1.0*tz.getDSTSavings()); // add the timezone's DST value (typically 1 hour expressed in milliseconds) + } + + offset = offset / (1000d*60d*60d); + int hourOffset = (int)offset; + double decimalVal = Math.abs(offset) - Math.abs(hourOffset); + int minuteOffset = (int)(decimalVal * 60); + + if (hourOffset < 0) { + if (hourOffset > -10) { + sb.append("-0"+Math.abs(hourOffset)); + } else { + sb.append("-"+Math.abs(hourOffset)); + } + } else { + if (hourOffset < 10) { + sb.append("+0" + hourOffset); + } else { + sb.append("+" + hourOffset); + } + } + + sb.append(":"); + + if (minuteOffset == 0) { + sb.append("00"); + } else if (minuteOffset < 10) { + sb.append("0" + minuteOffset); + } else { + sb.append("" + minuteOffset); + } + + return sb.toString(); + } + + @Override + public void execute(){ + ManagementServerExt _mgrExt = (ManagementServerExt)_mgr; + List usageRecords = _mgrExt.getUsageRecords(this); + ListResponse response = new ListResponse(); + List usageResponses = new ArrayList(); + for (Object usageRecordGeneric : usageRecords) { + UsageRecordResponse usageRecResponse = new UsageRecordResponse(); + if (usageRecordGeneric instanceof UsageVO) { + UsageVO usageRecord = (UsageVO)usageRecordGeneric; + + usageRecResponse.setAccountName(ApiDBUtils.findAccountByIdIncludingRemoved(usageRecord.getAccountId()).getAccountName()); + usageRecResponse.setAccountId(usageRecord.getAccountId()); + usageRecResponse.setDomainId(usageRecord.getDomainId()); + usageRecResponse.setZoneId(usageRecord.getZoneId()); + usageRecResponse.setDescription(usageRecord.getDescription()); + usageRecResponse.setUsage(usageRecord.getUsageDisplay()); + usageRecResponse.setUsageType(usageRecord.getUsageType()); + usageRecResponse.setVirtualMachineId(usageRecord.getVmInstanceId()); + usageRecResponse.setVmName(usageRecord.getVmName()); + usageRecResponse.setServiceOfferingId(usageRecord.getOfferingId()); + usageRecResponse.setTemplateId(usageRecord.getTemplateId()); + usageRecResponse.setUsageId(usageRecord.getUsageId()); + if(usageRecord.getUsageType() == UsageTypes.IP_ADDRESS){ + usageRecResponse.setSourceNat((usageRecord.getType().equals("SourceNat"))?true:false); + } else { + usageRecResponse.setType(usageRecord.getType()); + } + usageRecResponse.setSize(usageRecord.getSize()); + + if (usageRecord.getRawUsage() != null) { + DecimalFormat decimalFormat = new DecimalFormat("###########.######"); + usageRecResponse.setRawUsage(decimalFormat.format(usageRecord.getRawUsage())); + } + + if (usageRecord.getStartDate() != null) { + usageRecResponse.setStartDate(getDateStringInternal(usageRecord.getStartDate())); + } + if (usageRecord.getEndDate() != null) { + usageRecResponse.setEndDate(getDateStringInternal(usageRecord.getEndDate())); + } + } + + usageRecResponse.setObjectName("usagerecord"); + usageResponses.add(usageRecResponse); + } + + response.setResponses(usageResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} diff --git a/server/src/com/cloud/api/commands/ListExternalFirewallsCmd.java b/server/src/com/cloud/api/commands/ListExternalFirewallsCmd.java new file mode 100644 index 00000000000..dfa20209f5d --- /dev/null +++ b/server/src/com/cloud/api/commands/ListExternalFirewallsCmd.java @@ -0,0 +1,87 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseListCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.response.ListResponse; +import com.cloud.host.Host; +import com.cloud.network.ExternalNetworkManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.ExternalFirewallResponse; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="List external firewall appliances.", responseObject = ExternalFirewallResponse.class) +public class ListExternalFirewallsCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListServiceOfferingsCmd.class.getName()); + private static final String s_name = "listexternalfirewallsresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required = true, description="zone Id") + private long zoneId; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public long getZoneId() { + return zoneId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + ExternalNetworkManager externalNetworkMgr = locator.getManager(ExternalNetworkManager.class); + List externalFirewalls = externalNetworkMgr.listExternalFirewalls(this); + + ListResponse listResponse = new ListResponse(); + List responses = new ArrayList(); + for (Host externalFirewall : externalFirewalls) { + ExternalFirewallResponse response = externalNetworkMgr.createExternalFirewallResponse(externalFirewall); + response.setObjectName("externalfirewall"); + response.setResponseName(getCommandName()); + responses.add(response); + } + + listResponse.setResponses(responses); + listResponse.setResponseName(getCommandName()); + this.setResponseObject(listResponse); + } +} diff --git a/server/src/com/cloud/api/commands/ListExternalLoadBalancersCmd.java b/server/src/com/cloud/api/commands/ListExternalLoadBalancersCmd.java new file mode 100644 index 00000000000..069f61deddc --- /dev/null +++ b/server/src/com/cloud/api/commands/ListExternalLoadBalancersCmd.java @@ -0,0 +1,88 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseListCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.response.HostResponse; +import com.cloud.api.response.ListResponse; +import com.cloud.host.Host; +import com.cloud.network.ExternalNetworkManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.ExternalLoadBalancerResponse; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="List external load balancer appliances.", responseObject = HostResponse.class) +public class ListExternalLoadBalancersCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListExternalLoadBalancersCmd.class.getName()); + private static final String s_name = "listexternalloadbalancersresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, description="zone Id") + private long zoneId; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public long getZoneId() { + return zoneId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + ExternalNetworkManager externalNetworkMgr = locator.getManager(ExternalNetworkManager.class); + List externalLoadBalancers = externalNetworkMgr.listExternalLoadBalancers(this); + + ListResponse listResponse = new ListResponse(); + List responses = new ArrayList(); + for (Host externalLoadBalancer : externalLoadBalancers) { + ExternalLoadBalancerResponse response = externalNetworkMgr.createExternalLoadBalancerResponse(externalLoadBalancer); + response.setObjectName("externalloadbalancer"); + response.setResponseName(getCommandName()); + responses.add(response); + } + + listResponse.setResponses(responses); + listResponse.setResponseName(getCommandName()); + this.setResponseObject(listResponse); + } +} diff --git a/server/src/com/cloud/api/commands/ListNetworkDeviceCmd.java b/server/src/com/cloud/api/commands/ListNetworkDeviceCmd.java new file mode 100644 index 00000000000..0d8b9b35b1a --- /dev/null +++ b/server/src/com/cloud/api/commands/ListNetworkDeviceCmd.java @@ -0,0 +1,83 @@ +package com.cloud.api.commands; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.BaseListCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.ListResponse; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.network.NetworkDeviceManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.NetworkDeviceResponse; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.exception.CloudRuntimeException; + +@Implementation(description="List network devices", responseObject = NetworkDeviceResponse.class) +public class ListNetworkDeviceCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListNetworkDeviceCmd.class); + private static final String s_name = "listnetworkdevice"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NETWORK_DEVICE_TYPE, type = CommandType.STRING, description = "Network device type, now supports ExternalDhcp, ExternalFirewall, ExternalLoadBalancer, PxeServer") + private String type; + + @Parameter(name = ApiConstants.NETWORK_DEVICE_PARAMETER_LIST, type = CommandType.MAP, description = "parameters for network device") + private Map paramList; + + public String getType() { + return type; + } + + public Map getParamList() { + return paramList; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, + ResourceAllocationException { + try { + NetworkDeviceManager nwDeviceMgr; + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + nwDeviceMgr = locator.getManager(NetworkDeviceManager.class); + List devices = nwDeviceMgr.listNetworkDevice(this); + List nwdeviceResponses = new ArrayList(); + ListResponse listResponse = new ListResponse(); + for (Host d : devices) { + NetworkDeviceResponse response = nwDeviceMgr.getApiResponse(d); + response.setObjectName("networkdevice"); + response.setResponseName(getCommandName()); + nwdeviceResponses.add(response); + } + + listResponse.setResponses(nwdeviceResponses); + listResponse.setResponseName(getCommandName()); + this.setResponseObject(listResponse); + } catch (InvalidParameterValueException ipve) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, ipve.getMessage()); + } catch (CloudRuntimeException cre) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, cre.getMessage()); + } + } + + @Override + public String getCommandName() { + return s_name; + } + +} diff --git a/server/src/com/cloud/api/commands/ListTrafficMonitorsCmd.java b/server/src/com/cloud/api/commands/ListTrafficMonitorsCmd.java new file mode 100644 index 00000000000..b9508c32497 --- /dev/null +++ b/server/src/com/cloud/api/commands/ListTrafficMonitorsCmd.java @@ -0,0 +1,88 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseListCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.response.ListResponse; +import com.cloud.host.Host; +import com.cloud.network.NetworkUsageManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.ExternalFirewallResponse; +import com.cloud.server.api.response.TrafficMonitorResponse; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="List traffic monitor Hosts.", responseObject = ExternalFirewallResponse.class) +public class ListTrafficMonitorsCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListServiceOfferingsCmd.class.getName()); + private static final String s_name = "listtrafficmonitorsresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required = true, description="zone Id") + private long zoneId; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public long getZoneId() { + return zoneId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetworkUsageManager networkUsageMgr = locator.getManager(NetworkUsageManager.class); + List trafficMonitors = networkUsageMgr.listTrafficMonitors(this); + + ListResponse listResponse = new ListResponse(); + List responses = new ArrayList(); + for (Host trafficMonitor : trafficMonitors) { + TrafficMonitorResponse response = networkUsageMgr.getApiResponse(trafficMonitor); + response.setObjectName("trafficmonitor"); + response.setResponseName(getCommandName()); + responses.add(response); + } + + listResponse.setResponses(responses); + listResponse.setResponseName(getCommandName()); + this.setResponseObject(listResponse); + } +} diff --git a/server/src/com/cloud/api/commands/ListUsageTypesCmd.java b/server/src/com/cloud/api/commands/ListUsageTypesCmd.java new file mode 100644 index 00000000000..d93ddf0a7ea --- /dev/null +++ b/server/src/com/cloud/api/commands/ListUsageTypesCmd.java @@ -0,0 +1,69 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.api.commands; + +import java.util.List; + +import org.apache.log4j.Logger; + +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.response.ListResponse; +import com.cloud.server.ManagementServerExt; +import com.cloud.server.api.response.UsageTypeResponse; +import com.cloud.user.Account; + +@Implementation(description = "List Usage Types", responseObject = UsageTypeResponse.class) +public class ListUsageTypesCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(ListUsageTypesCmd.class.getName()); + private static final String s_name = "listusagetypesresponse"; + + @Override + public String getCommandName() { + return s_name; + } + + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + ManagementServerExt _mgrExt = (ManagementServerExt)_mgr; + List result = _mgrExt.listUsageTypes(); + ListResponse response = new ListResponse(); + response.setResponses(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} diff --git a/server/src/com/cloud/api/commands/netapp/AssociateLunCmd.java b/server/src/com/cloud/api/commands/netapp/AssociateLunCmd.java new file mode 100644 index 00000000000..8138825b4c6 --- /dev/null +++ b/server/src/com/cloud/api/commands/netapp/AssociateLunCmd.java @@ -0,0 +1,117 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ +package com.cloud.api.commands.netapp; + +import java.rmi.ServerException; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.netapp.NetappManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.netapp.AssociateLunCmdResponse; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="Associate a LUN with a guest IQN", responseObject = AssociateLunCmdResponse.class) +public class AssociateLunCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(AssociateLunCmd.class.getName()); + private static final String s_name = "associatelunresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, required = true, description="LUN name.") + private String lunName; + + @Parameter(name=ApiConstants.IQN, type=CommandType.STRING, required = true, description="Guest IQN to which the LUN associate.") + private String guestIqn; + + + /////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public String getLunName() { + return lunName; + } + + public String getGuestIQN() { + return guestIqn; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetappManager netappMgr = locator.getManager(NetappManager.class); + + try { + AssociateLunCmdResponse response = new AssociateLunCmdResponse(); + String returnVals[] = null; + returnVals = netappMgr.associateLun(getGuestIQN(), getLunName()); + response.setLun(returnVals[0]); + response.setIpAddress(returnVals[2]); + response.setTargetIQN(returnVals[1]); + response.setObjectName("lun"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (ServerException e) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, e.toString()); + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, e.toString()); + } + } + + @Override + public long getEntityOwnerId() { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/server/src/com/cloud/api/commands/netapp/CreateLunCmd.java b/server/src/com/cloud/api/commands/netapp/CreateLunCmd.java new file mode 100644 index 00000000000..93134939cb9 --- /dev/null +++ b/server/src/com/cloud/api/commands/netapp/CreateLunCmd.java @@ -0,0 +1,122 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *@author-aj + */ +package com.cloud.api.commands.netapp; + +import java.rmi.ServerException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.BaseCmd.CommandType; +import com.cloud.api.ServerApiException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.netapp.NetappManager; +import com.cloud.server.ManagementServerExt; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.netapp.AssociateLunCmdResponse; +import com.cloud.server.api.response.netapp.CreateLunCmdResponse; +import com.cloud.utils.Pair; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="Create a LUN from a pool", responseObject = CreateLunCmdResponse.class) +public class CreateLunCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(CreateLunCmd.class.getName()); + private static final String s_name = "createlunresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, required = true, description="pool name.") + private String poolName; + + @Parameter(name=ApiConstants.SIZE, type=CommandType.LONG, required = true, description="LUN size.") + private long size; + + public String getPoolName() { + return poolName; + } + + public long getLunSize() { + return size; + } + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetappManager netappMgr = locator.getManager(NetappManager.class); + + try { + CreateLunCmdResponse response = new CreateLunCmdResponse(); + String returnVals[] = null; + returnVals = netappMgr.createLunOnFiler(getPoolName(), getLunSize()); + response.setPath(returnVals[0]); + response.setIqn(returnVals[1]); + response.setIpAddress(returnVals[2]); + response.setObjectName("lun"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (ServerException e) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, e.toString()); + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, e.toString()); + } + + } + + @Override + public String getCommandName() { + // TODO Auto-generated method stub + return s_name; + } + + @Override + public long getEntityOwnerId() { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/server/src/com/cloud/api/commands/netapp/CreatePoolCmd.java b/server/src/com/cloud/api/commands/netapp/CreatePoolCmd.java new file mode 100644 index 00000000000..1e75b557bea --- /dev/null +++ b/server/src/com/cloud/api/commands/netapp/CreatePoolCmd.java @@ -0,0 +1,101 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *@author-aj + */ +package com.cloud.api.commands.netapp; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.netapp.NetappManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.netapp.CreatePoolCmdResponse; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="Create a pool", responseObject = CreatePoolCmdResponse.class) +public class CreatePoolCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(CreatePoolCmd.class.getName()); + private static final String s_name = "createpoolresponse"; + + @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, required = true, description="pool name.") + private String poolName; + @Parameter(name=ApiConstants.ALGORITHM, type=CommandType.STRING, required = true, description="algorithm.") + private String algorithm; + + public String getPoolName() { + return poolName; + } + + public String getAlgorithm() { + return algorithm; + } + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetappManager netappMgr = locator.getManager(NetappManager.class); + + try { + CreatePoolCmdResponse response = new CreatePoolCmdResponse(); + netappMgr.createPool(getPoolName(), getAlgorithm()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, e.toString()); + } + + } + + @Override + public String getCommandName() { + // TODO Auto-generated method stub + return s_name; + } + + @Override + public long getEntityOwnerId() { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/server/src/com/cloud/api/commands/netapp/CreateVolumeCmd.java b/server/src/com/cloud/api/commands/netapp/CreateVolumeCmd.java new file mode 100644 index 00000000000..5b7a6ea8432 --- /dev/null +++ b/server/src/com/cloud/api/commands/netapp/CreateVolumeCmd.java @@ -0,0 +1,163 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ +package com.cloud.api.commands.netapp; + +import java.net.UnknownHostException; +import java.rmi.ServerException; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.netapp.NetappManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.netapp.CreateVolumeCmdResponse; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="Create a volume", responseObject = CreateVolumeCmdResponse.class) +public class CreateVolumeCmd extends BaseCmd { + private static final String s_name = "createvolumeresponse"; + + @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, required = true, description="ip address.") + private String ipAddress; + + @Parameter(name=ApiConstants.AGGREGATE_NAME, type=CommandType.STRING, required = true, description="aggregate name.") + private String aggrName; + + @Parameter(name=ApiConstants.POOL_NAME, type=CommandType.STRING, required = true, description="pool name.") + private String poolName; + + @Parameter(name=ApiConstants.VOLUME_NAME, type=CommandType.STRING, required = true, description="volume name.") + private String volName; + + @Parameter(name=ApiConstants.SIZE, type=CommandType.INTEGER, required = true, description="volume size.") + private Integer volSize; + + @Parameter(name=ApiConstants.SNAPSHOT_POLICY, type=CommandType.STRING, required = false, description="snapshot policy.") + private String snapshotPolicy; + + @Parameter(name=ApiConstants.SNAPSHOT_RESERVATION, type=CommandType.INTEGER, required = false, description="snapshot reservation.") + private Integer snapshotReservation; + + @Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, required = true, description="user name.") + private String userName; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required = true, description="password.") + private String password; + + + public String getIpAddress() { + return ipAddress; + } + + public String getAggrName() { + return aggrName; + } + + public String getPoolName() { + return poolName; + } + + public String volName() { + return volName; + } + + public Integer getVolSize() { + return volSize; + } + + public String getSnapshotPolicy() { + return snapshotPolicy; + } + + public Integer getSnapshotReservation() { + return snapshotReservation; + } + + public String getUserName() { + return userName; + } + + public String getPassword() { + return password; + } + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException { + //param checks + if(snapshotReservation != null && (snapshotReservation<0 || snapshotReservation>100)) + throw new InvalidParameterValueException("Invalid snapshot reservation"); + + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetappManager netappMgr = locator.getManager(NetappManager.class); + + StringBuilder s = new StringBuilder(getVolSize().toString()); + s.append("g"); + + try { + netappMgr.createVolumeOnFiler(ipAddress, aggrName, poolName, volName, s.toString(), snapshotPolicy, snapshotReservation, userName, password); + CreateVolumeCmdResponse response = new CreateVolumeCmdResponse(); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (ServerException e) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, e.toString()); + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, e.toString()); + } catch (UnknownHostException e) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, e.toString()); + } + + } + + @Override + public String getCommandName() { + // TODO Auto-generated method stub + return s_name; + } + + @Override + public long getEntityOwnerId() { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/server/src/com/cloud/api/commands/netapp/DeletePoolCmd.java b/server/src/com/cloud/api/commands/netapp/DeletePoolCmd.java new file mode 100644 index 00000000000..bc7484f6c3e --- /dev/null +++ b/server/src/com/cloud/api/commands/netapp/DeletePoolCmd.java @@ -0,0 +1,93 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *@author-aj + */ +package com.cloud.api.commands.netapp; + + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceInUseException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.netapp.NetappManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.netapp.DeletePoolCmdResponse; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="Delete a pool", responseObject = DeletePoolCmdResponse.class) +public class DeletePoolCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DeletePoolCmd.class.getName()); + private static final String s_name = "deletepoolresponse"; + + @Parameter(name=ApiConstants.POOL_NAME, type=CommandType.STRING, required = true, description="pool name.") + private String poolName; + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetappManager netappMgr = locator.getManager(NetappManager.class); + try { + netappMgr.deletePool(poolName); + DeletePoolCmdResponse response = new DeletePoolCmdResponse(); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, e.toString()); + } catch (ResourceInUseException e) { + throw new ServerApiException(BaseCmd.RESOURCE_IN_USE_ERROR, e.toString()); + } + } + + @Override + public String getCommandName() { + // TODO Auto-generated method stub + return s_name; + } + + @Override + public long getEntityOwnerId() { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/server/src/com/cloud/api/commands/netapp/DestroyLunCmd.java b/server/src/com/cloud/api/commands/netapp/DestroyLunCmd.java new file mode 100644 index 00000000000..77b35dc99f1 --- /dev/null +++ b/server/src/com/cloud/api/commands/netapp/DestroyLunCmd.java @@ -0,0 +1,95 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ +package com.cloud.api.commands.netapp; + +import java.rmi.ServerException; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceInUseException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.netapp.NetappManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.netapp.DeleteLUNCmdResponse; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="Destroy a LUN", responseObject = DeleteLUNCmdResponse.class) +public class DestroyLunCmd extends BaseCmd { + + public static final Logger s_logger = Logger.getLogger(DestroyLunCmd.class.getName()); + private static final String s_name = "destroylunresponse"; + + @Parameter(name=ApiConstants.PATH, type=CommandType.STRING, required = true, description="LUN path.") + private String path; + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetappManager netappMgr = locator.getManager(NetappManager.class); + try { + netappMgr.destroyLunOnFiler(path); + DeleteLUNCmdResponse response = new DeleteLUNCmdResponse(); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, e.toString()); + } catch (ServerException e) { + throw new ServerApiException(BaseCmd.RESOURCE_IN_USE_ERROR, e.toString()); + } + } + + @Override + public String getCommandName() { + // TODO Auto-generated method stub + return s_name; + } + + @Override + public long getEntityOwnerId() { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/server/src/com/cloud/api/commands/netapp/DestroyVolumeCmd.java b/server/src/com/cloud/api/commands/netapp/DestroyVolumeCmd.java new file mode 100644 index 00000000000..6d7c8e5d4a5 --- /dev/null +++ b/server/src/com/cloud/api/commands/netapp/DestroyVolumeCmd.java @@ -0,0 +1,104 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ +package com.cloud.api.commands.netapp; + +import java.rmi.ServerException; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceInUseException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.netapp.NetappManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.netapp.DeleteVolumeCmdResponse; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="Destroy a Volume", responseObject = DeleteVolumeCmdResponse.class) +public class DestroyVolumeCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DestroyVolumeCmd.class.getName()); + private static final String s_name = "destroyvolumeresponse"; + + @Parameter(name=ApiConstants.AGGREGATE_NAME, type=CommandType.STRING, required = true, description="aggregate name.") + private String aggrName; + + @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, required = true, description="ip address.") + private String ipAddr; + + @Parameter(name=ApiConstants.VOLUME_NAME, type=CommandType.STRING, required = true, description="volume name.") + private String volumeName; + + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetappManager netappMgr = locator.getManager(NetappManager.class); + try { + netappMgr.destroyVolumeOnFiler(ipAddr, aggrName, volumeName); + DeleteVolumeCmdResponse response = new DeleteVolumeCmdResponse(); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, e.toString()); + } catch (ResourceInUseException e) { + throw new ServerApiException(BaseCmd.RESOURCE_IN_USE_ERROR, e.toString()); + } catch (ServerException e) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, e.toString()); + } + + } + + @Override + public String getCommandName() { + // TODO Auto-generated method stub + return s_name; + } + + @Override + public long getEntityOwnerId() { + // TODO Auto-generated method stub + return 0; + } + +} \ No newline at end of file diff --git a/server/src/com/cloud/api/commands/netapp/DissociateLunCmd.java b/server/src/com/cloud/api/commands/netapp/DissociateLunCmd.java new file mode 100644 index 00000000000..10e89195ece --- /dev/null +++ b/server/src/com/cloud/api/commands/netapp/DissociateLunCmd.java @@ -0,0 +1,96 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ +package com.cloud.api.commands.netapp; + +import java.rmi.ServerException; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.netapp.NetappManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.netapp.DissociateLunCmdResponse; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="Dissociate a LUN", responseObject = DissociateLunCmdResponse.class) +public class DissociateLunCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DissociateLunCmd.class.getName()); + private static final String s_name = "dissociatelunresponse"; + + @Parameter(name=ApiConstants.PATH, type=CommandType.STRING, required = true, description="LUN path.") + private String path; + + @Parameter(name=ApiConstants.IQN, type=CommandType.STRING, required = true, description="Guest IQN.") + private String guestIQN; + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetappManager netappMgr = locator.getManager(NetappManager.class); + try { + netappMgr.disassociateLun(guestIQN, path); + DissociateLunCmdResponse response = new DissociateLunCmdResponse(); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, e.toString()); + } catch (ServerException e) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, e.toString()); + } + } + + @Override + public String getCommandName() { + // TODO Auto-generated method stub + return s_name; + } + + @Override + public long getEntityOwnerId() { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/server/src/com/cloud/api/commands/netapp/ListLunsCmd.java b/server/src/com/cloud/api/commands/netapp/ListLunsCmd.java new file mode 100644 index 00000000000..74648e7e556 --- /dev/null +++ b/server/src/com/cloud/api/commands/netapp/ListLunsCmd.java @@ -0,0 +1,106 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + * + */ +package com.cloud.api.commands.netapp; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.ListResponse; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.netapp.LunVO; +import com.cloud.netapp.NetappManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.netapp.ListLunsCmdResponse; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="List LUN", responseObject = ListLunsCmdResponse.class) +public class ListLunsCmd extends BaseCmd +{ + public static final Logger s_logger = Logger.getLogger(ListLunsCmd.class.getName()); + private static final String s_name = "listlunresponse"; + + @Parameter(name=ApiConstants.POOL_NAME, type=CommandType.STRING, required = true, description="pool name.") + private String poolName; + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetappManager netappMgr = locator.getManager(NetappManager.class); + try { + List lunList = netappMgr.listLunsOnFiler(poolName); + ListResponse listResponse = new ListResponse(); + List responses = new ArrayList(); + for (LunVO lun : lunList) { + ListLunsCmdResponse response = new ListLunsCmdResponse(); + response.setId(lun.getId()); + response.setIqn(lun.getTargetIqn()); + response.setName(lun.getLunName()); + response.setVolumeId(lun.getVolumeId()); + response.setObjectName("lun"); + responses.add(response); + } + listResponse.setResponses(responses); + listResponse.setResponseName(getCommandName()); + this.setResponseObject(listResponse); + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, e.toString()); + } + } + + @Override + public String getCommandName() { + // TODO Auto-generated method stub + return s_name; + } + + @Override + public long getEntityOwnerId() { + // TODO Auto-generated method stub + return 0; + } +} diff --git a/server/src/com/cloud/api/commands/netapp/ListPoolsCmd.java b/server/src/com/cloud/api/commands/netapp/ListPoolsCmd.java new file mode 100644 index 00000000000..533ac9eba47 --- /dev/null +++ b/server/src/com/cloud/api/commands/netapp/ListPoolsCmd.java @@ -0,0 +1,101 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *@author-aj + */ +package com.cloud.api.commands.netapp; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.ListResponse; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.netapp.NetappManager; +import com.cloud.netapp.PoolVO; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.netapp.ListPoolsCmdResponse; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="List Pool", responseObject = ListPoolsCmdResponse.class) +public class ListPoolsCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(ListPoolsCmd.class.getName()); + private static final String s_name = "listpoolresponse"; + + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetappManager netappMgr = locator.getManager(NetappManager.class); + try { + List poolList = netappMgr.listPools(); + ListResponse listResponse = new ListResponse(); + List responses = new ArrayList(); + for (PoolVO pool : poolList) { + ListPoolsCmdResponse response = new ListPoolsCmdResponse(); + response.setId(pool.getId()); + response.setName(pool.getName()); + response.setAlgorithm(pool.getAlgorithm()); + response.setObjectName("pool"); + responses.add(response); + } + listResponse.setResponses(responses); + listResponse.setResponseName(getCommandName()); + this.setResponseObject(listResponse); + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, e.toString()); + } + + } + + @Override + public String getCommandName() { + // TODO Auto-generated method stub + return s_name; + } + + @Override + public long getEntityOwnerId() { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/server/src/com/cloud/api/commands/netapp/ListVolumesCmd.java b/server/src/com/cloud/api/commands/netapp/ListVolumesCmd.java new file mode 100644 index 00000000000..e5f9dac61e1 --- /dev/null +++ b/server/src/com/cloud/api/commands/netapp/ListVolumesCmd.java @@ -0,0 +1,111 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *@author-aj + */ +package com.cloud.api.commands.netapp; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.ListResponse; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.netapp.NetappManager; +import com.cloud.netapp.NetappVolumeVO; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.netapp.ListVolumesCmdResponse; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="List Volumes", responseObject = ListVolumesCmdResponse.class) +public class ListVolumesCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(ListVolumesCmd.class.getName()); + private static final String s_name = "listvolumesresponse"; + + @Parameter(name=ApiConstants.POOL_NAME, type=CommandType.STRING, required = true, description="pool name.") + private String poolName; + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetappManager netappMgr = locator.getManager(NetappManager.class); + + try { + List volumes = netappMgr.listVolumesOnFiler(poolName); + ListResponse listResponse = new ListResponse(); + List responses = new ArrayList(); + for (NetappVolumeVO volume : volumes) { + ListVolumesCmdResponse response = new ListVolumesCmdResponse(); + response.setId(volume.getId()); + response.setIpAddress(volume.getIpAddress()); + response.setPoolName(volume.getPoolName()); + response.setAggrName(volume.getAggregateName()); + response.setVolumeName(volume.getVolumeName()); + response.setSnapshotPolicy(volume.getSnapshotPolicy()); + response.setSnapshotReservation(volume.getSnapshotReservation()); + response.setVolumeSize(volume.getVolumeSize()); + response.setObjectName("volume"); + responses.add(response); + } + listResponse.setResponses(responses); + listResponse.setResponseName(getCommandName()); + this.setResponseObject(listResponse); + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, e.toString()); + } + + } + + @Override + public String getCommandName() { + // TODO Auto-generated method stub + return s_name; + } + + @Override + public long getEntityOwnerId() { + // TODO Auto-generated method stub + return 0; + } + +} \ No newline at end of file diff --git a/server/src/com/cloud/api/commands/netapp/ModifyPoolCmd.java b/server/src/com/cloud/api/commands/netapp/ModifyPoolCmd.java new file mode 100644 index 00000000000..9daf6e89f73 --- /dev/null +++ b/server/src/com/cloud/api/commands/netapp/ModifyPoolCmd.java @@ -0,0 +1,90 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *@author-aj + */ +package com.cloud.api.commands.netapp; + + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.netapp.NetappManager; +import com.cloud.server.ManagementService; +import com.cloud.server.api.response.netapp.ModifyPoolCmdResponse; +import com.cloud.utils.component.ComponentLocator; + +@Implementation(description="Modify pool", responseObject = ModifyPoolCmdResponse.class) +public class ModifyPoolCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(ModifyPoolCmd.class.getName()); + private static final String s_name = "modifypoolresponse"; + + @Parameter(name=ApiConstants.POOL_NAME, type=CommandType.STRING, required = true, description="pool name.") + private String poolName; + + @Parameter(name=ApiConstants.ALGORITHM, type=CommandType.STRING, required = true, description="algorithm.") + private String algorithm; + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException { + ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); + NetappManager netappMgr = locator.getManager(NetappManager.class); + + netappMgr.modifyPool(poolName, algorithm); + + ModifyPoolCmdResponse response = new ModifyPoolCmdResponse(); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public String getCommandName() { + // TODO Auto-generated method stub + return s_name; + } + + @Override + public long getEntityOwnerId() { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/server/src/com/cloud/baremetal/BareMetalDiscoverer.java b/server/src/com/cloud/baremetal/BareMetalDiscoverer.java new file mode 100755 index 00000000000..cf6e38d343a --- /dev/null +++ b/server/src/com/cloud/baremetal/BareMetalDiscoverer.java @@ -0,0 +1,182 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import java.net.InetAddress; +import java.net.URI; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.exception.DiscoveryException; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.resource.Discoverer; +import com.cloud.resource.DiscovererBase; +import com.cloud.resource.ServerResource; +import com.cloud.utils.component.Inject; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; + +@Local(value=Discoverer.class) +public class BareMetalDiscoverer extends DiscovererBase implements Discoverer { + private static final Logger s_logger = Logger.getLogger(BareMetalDiscoverer.class); + @Inject ClusterDao _clusterDao; + @Inject protected HostDao _hostDao; + @Inject DataCenterDao _dcDao; + + @Override + public Map> find(long dcId, Long podId, Long clusterId, URI url, String username, String password, List hostTags) + throws DiscoveryException { + Map> resources = new HashMap>(); + Map details = new HashMap(); + + if (!url.getScheme().equals("http")) { + String msg = "urlString is not http so we're not taking care of the discovery for this: " + url; + s_logger.debug(msg); + return null; + } + if (clusterId == null) { + String msg = "must specify cluster Id when add host"; + s_logger.debug(msg); + throw new RuntimeException(msg); + } + + if (podId == null) { + String msg = "must specify pod Id when add host"; + s_logger.debug(msg); + throw new RuntimeException(msg); + } + + ClusterVO cluster = _clusterDao.findById(clusterId); + if (cluster == null || (cluster.getHypervisorType() != HypervisorType.BareMetal)) { + if (s_logger.isInfoEnabled()) + s_logger.info("invalid cluster id or cluster is not for Bare Metal hosts"); + return null; + } + + DataCenterVO zone = _dcDao.findById(dcId); + if (zone == null) { + throw new RuntimeException("Cannot find zone " + dcId); + } + + try { + String hostname = url.getHost(); + InetAddress ia = InetAddress.getByName(hostname); + String agentIp = ia.getHostAddress(); + String guid = UUID.nameUUIDFromBytes(agentIp.getBytes()).toString(); + + String injectScript = "scripts/util/ipmi.py"; + String scriptPath = Script.findScript("", injectScript); + if (scriptPath == null) { + throw new CloudRuntimeException("Unable to find key ipmi script " + + injectScript); + } + + final Script command = new Script(scriptPath, s_logger); + command.add("ping"); + command.add("hostname="+agentIp); + command.add("usrname="+username); + command.add("password="+password); + final String result = command.execute(); + if (result != null) { + s_logger.warn(String.format("Can not set up ipmi connection(ip=%1$s, username=%2$s, password=%3$s, args) because %4$s", agentIp, username, password, result)); + return null; + } + + ClusterVO clu = _clusterDao.findById(clusterId); + if (clu.getGuid() == null) { + clu.setGuid("/root"); + _clusterDao.update(clusterId, clu); + } + + Map params = new HashMap(); + params.putAll(_params); + params.put("zone", Long.toString(dcId)); + params.put("pod", Long.toString(podId)); + params.put("cluster", Long.toString(clusterId)); + params.put("guid", guid); + params.put("agentIp", agentIp); + params.put("username", username); + params.put("password", password); + BareMetalResourceBase resource = new BareMetalResourceBase(); + resource.configure("Bare Metal Agent", params); + + String memCapacity = (String)params.get("memCapacity"); + String cpuCapacity = (String)params.get("cpuCapacity"); + String cpuNum = (String)params.get("cpuNum"); + String mac = (String)params.get("mac"); + if (hostTags != null && hostTags.size() != 0) { + details.put("hostTag", hostTags.get(0)); + } + details.put("memCapacity", memCapacity); + details.put("cpuCapacity", cpuCapacity); + details.put("cpuNum", cpuNum); + details.put("mac", mac); + details.put("username", username); + details.put("password", password); + details.put("agentIp", agentIp); + + resources.put(resource, details); + resource.start(); + + zone.setGatewayProvider(Network.Provider.ExternalGateWay.getName()); + zone.setDnsProvider(Network.Provider.ExternalDhcpServer.getName()); + zone.setDhcpProvider(Network.Provider.ExternalDhcpServer.getName()); + _dcDao.update(zone.getId(), zone); + + s_logger.debug(String.format("Discover Bare Metal host successfully(ip=%1$s, username=%2$s, password=%3%s," + + "cpuNum=%4$s, cpuCapacity-%5$s, memCapacity=%6$s)", agentIp, username, password, cpuNum, cpuCapacity, memCapacity)); + return resources; + } catch (Exception e) { + s_logger.warn("Can not set up bare metal agent", e); + } + + return null; + } + + @Override + public void postDiscovery(List hosts, long msId) + throws DiscoveryException { + } + + @Override + public boolean matchHypervisor(String hypervisor) { + return hypervisor.equalsIgnoreCase(Hypervisor.HypervisorType.BareMetal.toString()); + } + + @Override + public HypervisorType getHypervisorType() { + return Hypervisor.HypervisorType.BareMetal; + } + +} diff --git a/server/src/com/cloud/baremetal/BareMetalGuru.java b/server/src/com/cloud/baremetal/BareMetalGuru.java new file mode 100644 index 00000000000..40b5456d4c6 --- /dev/null +++ b/server/src/com/cloud/baremetal/BareMetalGuru.java @@ -0,0 +1,63 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import javax.ejb.Local; + +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.hypervisor.HypervisorGuru; +import com.cloud.hypervisor.HypervisorGuruBase; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.storage.GuestOSVO; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.utils.component.Inject; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value=HypervisorGuru.class) +public class BareMetalGuru extends HypervisorGuruBase implements HypervisorGuru { + @Inject GuestOSDao _guestOsDao; + + protected BareMetalGuru() { + super(); + } + + @Override + public HypervisorType getHypervisorType() { + return HypervisorType.BareMetal; + } + + @Override + public VirtualMachineTO implement(VirtualMachineProfile vm) { + VirtualMachineTO to = toVirtualMachineTO(vm); + + // Determine the VM's OS description + GuestOSVO guestOS = _guestOsDao.findById(vm.getVirtualMachine().getGuestOSId()); + to.setOs(guestOS.getDisplayName()); + + return to; + } + + @Override + public boolean trackVmHostChange() { + return false; + } +} diff --git a/server/src/com/cloud/baremetal/BareMetalPingServiceImpl.java b/server/src/com/cloud/baremetal/BareMetalPingServiceImpl.java new file mode 100644 index 00000000000..1878b305f9e --- /dev/null +++ b/server/src/com/cloud/baremetal/BareMetalPingServiceImpl.java @@ -0,0 +1,195 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import java.net.URI; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.baremetal.PreparePxeServerAnswer; +import com.cloud.agent.api.baremetal.PreparePxeServerCommand; +import com.cloud.agent.api.baremetal.prepareCreateTemplateCommand; +import com.cloud.baremetal.PxeServerManager.PxeServerType; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.HostPodVO; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.resource.ServerResource; +import com.cloud.uservm.UserVm; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.NicVO; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value=PxeServerService.class) +public class BareMetalPingServiceImpl extends BareMetalPxeServiceBase implements PxeServerService { + private static final Logger s_logger = Logger.getLogger(BareMetalPingServiceImpl.class); + + @Override + public Host addPxeServer(PxeServerProfile profile) { + Long zoneId = profile.getZoneId(); + Long podId = profile.getPodId(); + + DataCenterVO zone = _dcDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Could not find zone with ID: " + zoneId); + } + + List pxeServers = _hostDao.listBy(Host.Type.PxeServer, null, podId, zoneId); + if (pxeServers.size() != 0) { + throw new InvalidParameterValueException("Already had a PXE server in Pod: " + podId + " zone: " + zoneId); + } + + + String ipAddress = profile.getUrl(); + String username = profile.getUsername(); + String password = profile.getPassword(); + + ServerResource resource = null; + Map params = new HashMap(); + params.put("type", PxeServerType.PING.getName()); + params.put("zone", Long.toString(zoneId)); + params.put("pod", podId.toString()); + params.put("ip", ipAddress); + params.put("username", username); + params.put("password", password); + if (profile.getType().equalsIgnoreCase(PxeServerType.PING.getName())) { + String storageServerIp = profile.getPingStorageServerIp(); + if (storageServerIp == null) { + throw new InvalidParameterValueException("No IP for storage server specified"); + } + String pingDir = profile.getPingDir(); + if (pingDir == null) { + throw new InvalidParameterValueException("No direcotry for storage server specified"); + } + String tftpDir = profile.getTftpDir(); + if (tftpDir == null) { + throw new InvalidParameterValueException("No TFTP directory specified"); + } + String cifsUsername = profile.getPingCifsUserName(); + if (cifsUsername == null || cifsUsername.equalsIgnoreCase("")) { + cifsUsername = "xxx"; + } + String cifsPassword = profile.getPingCifspassword(); + if (cifsPassword == null || cifsPassword.equalsIgnoreCase("")) { + cifsPassword = "xxx"; + } + String guid = getPxeServerGuid(Long.toString(zoneId) + "-" + Long.toString(podId), PxeServerType.PING.getName(), ipAddress); + + params.put("storageServer", storageServerIp); + params.put("pingDir", pingDir); + params.put("tftpDir", tftpDir); + params.put("cifsUserName", cifsUsername); + params.put("cifsPassword", cifsPassword); + params.put("guid", guid); + + resource = new PingPxeServerResource(); + try { + resource.configure("PING PXE resource", params); + } catch (Exception e) { + s_logger.debug(e); + throw new CloudRuntimeException(e.getMessage()); + } + + } else { + throw new CloudRuntimeException("Unsupport PXE server type:" + profile.getType()); + } + + Host pxeServer = _agentMgr.addHost(zoneId, resource, Host.Type.PxeServer, params); + if (pxeServer == null) { + throw new CloudRuntimeException("Cannot add PXE server as a host"); + } + + return pxeServer; + } + + + @Override + public boolean prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationContext context, Long pxeServerId) { + List nics = profile.getNics(); + if (nics.size() == 0) { + throw new CloudRuntimeException("Cannot do PXE start without nic"); + } + + NicProfile pxeNic = nics.get(0); + String mac = pxeNic.getMacAddress(); + String ip = pxeNic.getIp4Address(); + String gateway = pxeNic.getGateway(); + String mask = pxeNic.getNetmask(); + String dns = pxeNic.getDns1(); + if (dns == null) { + dns = pxeNic.getDns2(); + } + + try { + String tpl = profile.getTemplate().getUrl(); + assert tpl != null : "How can a null template get here!!!"; + PreparePxeServerCommand cmd = new PreparePxeServerCommand(ip, mac, mask, gateway, dns, tpl, + profile.getVirtualMachine().getInstanceName(), dest.getHost().getName()); + PreparePxeServerAnswer ans = (PreparePxeServerAnswer) _agentMgr.send(pxeServerId, cmd); + return ans.getResult(); + } catch (Exception e) { + s_logger.warn("Cannot prepare PXE server", e); + return false; + } + } + + + @Override + public boolean prepareCreateTemplate(Long pxeServerId, UserVm vm, String templateUrl) { + List nics = _nicDao.listByVmId(vm.getId()); + if (nics.size() != 1) { + throw new CloudRuntimeException("Wrong nic number " + nics.size() + " of vm " + vm.getId()); + } + + /* use last host id when VM stopped */ + Long hostId = (vm.getHostId() == null ? vm.getLastHostId() : vm.getHostId()); + HostVO host = _hostDao.findById(hostId); + DataCenterVO dc = _dcDao.findById(host.getDataCenterId()); + NicVO nic = nics.get(0); + String mask = nic.getNetmask(); + String mac = nic.getMacAddress(); + String ip = nic.getIp4Address(); + String gateway = nic.getGateway(); + String dns = dc.getDns1(); + if (dns == null) { + dns = dc.getDns2(); + } + + try { + prepareCreateTemplateCommand cmd = new prepareCreateTemplateCommand(ip, mac, mask, gateway, dns, templateUrl); + Answer ans = _agentMgr.send(pxeServerId, cmd); + return ans.getResult(); + } catch (Exception e) { + s_logger.debug("Prepare for creating baremetal template failed", e); + return false; + } + } +} diff --git a/server/src/com/cloud/baremetal/BareMetalPxeServiceBase.java b/server/src/com/cloud/baremetal/BareMetalPxeServiceBase.java new file mode 100644 index 00000000000..b723133f2b6 --- /dev/null +++ b/server/src/com/cloud/baremetal/BareMetalPxeServiceBase.java @@ -0,0 +1,81 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import java.util.Map; + +import javax.naming.ConfigurationException; + +import com.cloud.agent.AgentManager; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.HostPodDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.host.Host; +import com.cloud.host.dao.HostDao; +import com.cloud.utils.component.Inject; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.NicDao; + +public abstract class BareMetalPxeServiceBase implements PxeServerService { + protected String _name; + @Inject DataCenterDao _dcDao; + @Inject HostDao _hostDao; + @Inject AgentManager _agentMgr; + @Inject ExternalDhcpManager exDhcpMgr; + @Inject HostPodDao _podDao; + @Inject NicDao _nicDao; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + + @Override + public boolean prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationContext context, Long pxeServerId) { + throw new CloudRuntimeException("Dervied class should implement this method"); + } + + protected String getPxeServerGuid(String zoneId, String name, String ip) { + return zoneId + "-" + name + "-" + ip; + } + + @Override + public abstract Host addPxeServer(PxeServerProfile profile); +} diff --git a/server/src/com/cloud/baremetal/BareMetalResourceBase.java b/server/src/com/cloud/baremetal/BareMetalResourceBase.java new file mode 100755 index 00000000000..4e2ec34d763 --- /dev/null +++ b/server/src/com/cloud/baremetal/BareMetalResourceBase.java @@ -0,0 +1,551 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import java.util.HashMap; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CheckVirtualMachineAnswer; +import com.cloud.agent.api.CheckVirtualMachineCommand; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.MaintainAnswer; +import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.MigrateAnswer; +import com.cloud.agent.api.MigrateCommand; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.PrepareForMigrationAnswer; +import com.cloud.agent.api.PrepareForMigrationCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.RebootAnswer; +import com.cloud.agent.api.RebootCommand; +import com.cloud.agent.api.StartAnswer; +import com.cloud.agent.api.StartCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.agent.api.StopAnswer; +import com.cloud.agent.api.StopCommand; +import com.cloud.agent.api.baremetal.IpmISetBootDevCommand; +import com.cloud.agent.api.baremetal.IpmISetBootDevCommand.BootDev; +import com.cloud.agent.api.baremetal.IpmiBootorResetCommand; +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.api.ApiConstants; +import com.cloud.host.Host.Type; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.hypervisor.kvm.resource.KvmDummyResourceBase; +import com.cloud.hypervisor.xen.resource.CitrixResourceBase; +import com.cloud.resource.ServerResource; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.OutputInterpreter; +import com.cloud.utils.script.Script; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.State; + +@Local(value = ServerResource.class) +public class BareMetalResourceBase implements ServerResource { + private static final Logger s_logger = Logger.getLogger(BareMetalResourceBase.class); + protected HashMap _vms = new HashMap(2); + protected String _name; + protected String _uuid; + protected String _zone; + protected String _pod; + protected String _cluster; + protected long _memCapacity; + protected long _cpuCapacity; + protected long _cpuNum; + protected String _mac; + protected String _username; + protected String _password; + protected String _ip; + protected IAgentControl _agentControl; + protected Script _pingCommand; + protected Script _setPxeBootCommand; + protected Script _setDiskBootCommand; + protected Script _rebootCommand; + protected Script _getStatusCommand; + protected Script _powerOnCommand; + protected Script _powerOffCommand; + protected Script _bootOrRebootCommand; + protected String _vmName; + + private void changeVmState(String vmName, VirtualMachine.State state) { + synchronized (_vms) { + _vms.put(vmName, state); + } + } + + private State removeVmState(String vmName) { + synchronized (_vms) { + return _vms.remove(vmName); + } + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + _uuid = (String) params.get("guid"); + try { + _memCapacity = Long.parseLong((String)params.get(ApiConstants.MEMORY)) * 1024L * 1024L; + _cpuCapacity = Long.parseLong((String)params.get(ApiConstants.CPU_SPEED)); + _cpuNum = Long.parseLong((String)params.get(ApiConstants.CPU_NUMBER)); + } catch (NumberFormatException e) { + throw new ConfigurationException(String.format("Unable to parse number of CPU or memory capacity " + + "or cpu capacity(cpu number = %1$s memCapacity=%2$s, cpuCapacity=%3$s", (String)params.get(ApiConstants.CPU_NUMBER), + (String)params.get(ApiConstants.MEMORY), (String)params.get(ApiConstants.CPU_SPEED))); + } + + _zone = (String) params.get("zone"); + _pod = (String) params.get("pod"); + _cluster = (String) params.get("cluster"); + _ip = (String)params.get("agentIp"); + _mac = (String)params.get(ApiConstants.HOST_MAC); + _username = (String)params.get("username"); + _password = (String)params.get("password"); + _vmName = (String)params.get("vmName"); + + if (_pod == null) { + throw new ConfigurationException("Unable to get the pod"); + } + + if (_cluster == null) { + throw new ConfigurationException("Unable to get the pod"); + } + + if (_ip == null) { + throw new ConfigurationException("Unable to get the host address"); + } + + if (_mac.equalsIgnoreCase("unknown")) { + throw new ConfigurationException("Unable to get the host mac address"); + } + + if (_mac.split(":").length != 6) { + throw new ConfigurationException("Wrong MAC format(" + _mac + "). It must be in format of for example 00:11:ba:33:aa:dd which is not case sensitive"); + } + + if (_uuid == null) { + throw new ConfigurationException("Unable to get the uuid"); + } + + String injectScript = "scripts/util/ipmi.py"; + String scriptPath = Script.findScript("", injectScript); + if (scriptPath == null) { + throw new ConfigurationException("Cannot find ping script " + scriptPath); + } + _pingCommand = new Script(scriptPath, s_logger); + _pingCommand.add("ping"); + _pingCommand.add("hostname="+_ip); + _pingCommand.add("usrname="+_username); + _pingCommand.add("password="+_password); + + _setPxeBootCommand = new Script(scriptPath, s_logger); + _setPxeBootCommand.add("boot_dev"); + _setPxeBootCommand.add("hostname="+_ip); + _setPxeBootCommand.add("usrname="+_username); + _setPxeBootCommand.add("password="+_password); + _setPxeBootCommand.add("dev=pxe"); + + _setDiskBootCommand = new Script(scriptPath, s_logger); + _setDiskBootCommand.add("boot_dev"); + _setDiskBootCommand.add("hostname="+_ip); + _setDiskBootCommand.add("usrname="+_username); + _setDiskBootCommand.add("password="+_password); + _setDiskBootCommand.add("dev=disk"); + + _rebootCommand = new Script(scriptPath, s_logger); + _rebootCommand.add("reboot"); + _rebootCommand.add("hostname="+_ip); + _rebootCommand.add("usrname="+_username); + _rebootCommand.add("password="+_password); + + _getStatusCommand = new Script(scriptPath, s_logger); + _getStatusCommand.add("ping"); + _getStatusCommand.add("hostname="+_ip); + _getStatusCommand.add("usrname="+_username); + _getStatusCommand.add("password="+_password); + + _powerOnCommand = new Script(scriptPath, s_logger); + _powerOnCommand.add("power"); + _powerOnCommand.add("hostname="+_ip); + _powerOnCommand.add("usrname="+_username); + _powerOnCommand.add("password="+_password); + _powerOnCommand.add("action=on"); + + _powerOffCommand = new Script(scriptPath, s_logger); + _powerOffCommand.add("power"); + _powerOffCommand.add("hostname="+_ip); + _powerOffCommand.add("usrname="+_username); + _powerOffCommand.add("password="+_password); + _powerOffCommand.add("action=soft"); + + _bootOrRebootCommand = new Script(scriptPath, s_logger); + _bootOrRebootCommand.add("boot_or_reboot"); + _bootOrRebootCommand.add("hostname="+_ip); + _bootOrRebootCommand.add("usrname="+_username); + _bootOrRebootCommand.add("password="+_password); + + return true; + } + + protected boolean doScript(Script cmd) { + return doScript(cmd, null); + } + + protected boolean doScript(Script cmd, OutputInterpreter interpreter) { + int retry = 5; + String res = null; + while (retry-- > 0) { + if (interpreter == null) { + res = cmd.execute(); + } else { + res = cmd.execute(interpreter); + } + if (res != null && res.startsWith("Error: Unable to establish LAN")) { + s_logger.warn("IPMI script timeout(" + cmd.toString() + "), will retry " + retry + " times"); + continue; + } else if (res == null) { + return true; + } else { + break; + } + } + + s_logger.warn("IPMI Scirpt failed due to " + res + "(" + cmd.toString() +")"); + return false; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public Type getType() { + return com.cloud.host.Host.Type.Routing; + } + + protected State getVmState() { + OutputInterpreter.AllLinesParser interpreter = new OutputInterpreter.AllLinesParser(); + if (!doScript(_getStatusCommand, interpreter)) { + s_logger.warn("Cannot get power status of " + _name + ", assume VM state was not changed"); + return null; + } + if (isPowerOn(interpreter.getLines())) { + return State.Running; + } else { + return State.Stopped; + } + } + + protected Map fullSync() { + Map changes = new HashMap(); + + if (_vmName != null) { + State state = getVmState(); + if (state != null) { + changes.put(_vmName, state); + } + } + + return changes; + } + + @Override + public StartupCommand[] initialize() { + StartupRoutingCommand cmd = new StartupRoutingCommand(0, 0, 0, 0, null, Hypervisor.HypervisorType.BareMetal, + new HashMap(), null); + cmd.setDataCenter(_zone); + cmd.setPod(_pod); + cmd.setCluster(_cluster); + cmd.setGuid(_uuid); + cmd.setName(_ip); + cmd.setPrivateIpAddress(_ip); + cmd.setStorageIpAddress(_ip); + cmd.setVersion(BareMetalResourceBase.class.getPackage().getImplementationVersion()); + cmd.setCpus((int)_cpuNum); + cmd.setSpeed(_cpuCapacity); + cmd.setMemory(_memCapacity); + cmd.setPrivateMacAddress(_mac); + cmd.setPublicMacAddress(_mac); + cmd.setStateChanges(fullSync()); + return new StartupCommand[] {cmd}; + } + + private boolean ipmiPing() { + return doScript(_pingCommand); + } + + @Override + public PingCommand getCurrentStatus(long id) { + try { + if (!ipmiPing()) { + Thread.sleep(1000); + if (!ipmiPing()) { + s_logger.warn("Cannot ping ipmi nic " + _ip); + return null; + } + } + } catch (Exception e) { + s_logger.debug("Cannot ping ipmi nic " + _ip, e); + return null; + } + + return new PingRoutingCommand(getType(), id, deltaSync()); + } + + protected Answer execute(IpmISetBootDevCommand cmd) { + Script bootCmd = null; + if (cmd.getBootDev() == BootDev.disk) { + bootCmd = _setDiskBootCommand; + } else if (cmd.getBootDev() == BootDev.pxe) { + bootCmd = _setPxeBootCommand; + } else { + throw new CloudRuntimeException("Unkonwn boot dev " + cmd.getBootDev()); + } + + String bootDev = cmd.getBootDev().name(); + if (!doScript(bootCmd)) { + s_logger.warn("Set " + _ip + " boot dev to " + bootDev + "failed"); + return new Answer(cmd, false, "Set " + _ip + " boot dev to " + bootDev + "failed"); + } + + s_logger.warn("Set " + _ip + " boot dev to " + bootDev + "Success"); + return new Answer(cmd, true, "Set " + _ip + " boot dev to " + bootDev + "Success"); + } + + protected MaintainAnswer execute(MaintainCommand cmd) { + return new MaintainAnswer(cmd, false); + } + + protected PrepareForMigrationAnswer execute(PrepareForMigrationCommand cmd) { + return new PrepareForMigrationAnswer(cmd); + } + + protected MigrateAnswer execute(MigrateCommand cmd) { + if (!doScript(_powerOffCommand)) { + return new MigrateAnswer(cmd, false, "IPMI power off failed", null); + } + return new MigrateAnswer(cmd, true, "success", null); + } + + protected CheckVirtualMachineAnswer execute(final CheckVirtualMachineCommand cmd) { + return new CheckVirtualMachineAnswer(cmd, State.Stopped, null); + } + + protected Answer execute(IpmiBootorResetCommand cmd) { + if (!doScript(_bootOrRebootCommand)) { + return new Answer(cmd ,false, "IPMI boot or reboot failed"); + } + return new Answer(cmd, true, "Success"); + + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof ReadyCommand) { + return execute((ReadyCommand)cmd); + } else if (cmd instanceof StartCommand) { + return execute((StartCommand)cmd); + } else if (cmd instanceof StopCommand) { + return execute((StopCommand)cmd); + } else if (cmd instanceof RebootCommand) { + return execute((RebootCommand)cmd); + } else if (cmd instanceof IpmISetBootDevCommand) { + return execute((IpmISetBootDevCommand)cmd); + } else if (cmd instanceof MaintainCommand) { + return execute((MaintainCommand)cmd); + } else if (cmd instanceof PrepareForMigrationCommand) { + return execute((PrepareForMigrationCommand)cmd); + } else if (cmd instanceof MigrateCommand) { + return execute((MigrateCommand)cmd); + } else if (cmd instanceof CheckVirtualMachineCommand) { + return execute((CheckVirtualMachineCommand)cmd); + } else if (cmd instanceof IpmiBootorResetCommand) { + return execute((IpmiBootorResetCommand)cmd); + } else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } + + protected boolean isPowerOn(String str) { + if (str.startsWith("Chassis Power is on")) { + return true; + } else if (str.startsWith("Chassis Power is off")) { + return false; + } else { + throw new CloudRuntimeException("Cannot parse IPMI power status " + str); + } + } + + protected RebootAnswer execute(final RebootCommand cmd) { + if (!doScript(_rebootCommand)) { + return new RebootAnswer(cmd, "IPMI reboot failed"); + } + + return new RebootAnswer(cmd, "reboot succeeded", null, null); + } + + protected StopAnswer execute(final StopCommand cmd) { + if (!doScript(_powerOffCommand)) { + return new StopAnswer(cmd, "IPMI power off failed"); + } + + return new StopAnswer(cmd, "Success", null, Long.valueOf(0), Long.valueOf(0)); + } + + protected StartAnswer execute(StartCommand cmd) { + VirtualMachineTO vm = cmd.getVirtualMachine(); + State state = State.Stopped; + + try { + changeVmState(vm.getName(), State.Starting); + + boolean pxeBoot = false; + String[] bootArgs = vm.getBootArgs().split(" "); + for (int i = 0; i < bootArgs.length; i++) { + if (bootArgs[i].equalsIgnoreCase("PxeBoot")) { + pxeBoot = true; + break; + } + } + + if (pxeBoot) { + if (!doScript(_setPxeBootCommand)) { + return new StartAnswer(cmd, "Set boot device to PXE failed"); + } + s_logger.debug("Set " + vm.getHostName() + " to PXE boot successfully"); + } else { + execute(new IpmISetBootDevCommand(BootDev.disk)); + } + + OutputInterpreter.AllLinesParser interpreter = new OutputInterpreter.AllLinesParser(); + if (!doScript(_getStatusCommand, interpreter)) { + return new StartAnswer(cmd, "Cannot get current power status of " + _name); + } + + if (isPowerOn(interpreter.getLines())) { + if (pxeBoot) { + if (!doScript(_rebootCommand)) { + return new StartAnswer(cmd, "IPMI reboot failed"); + } + s_logger.debug("IPMI reboot " + vm.getHostName() + " successfully"); + } else { + s_logger.warn("Machine " + _name + " is alreay power on, why we still get a Start command? ignore it"); + + } + } else { + if (!doScript(_powerOnCommand)) { + return new StartAnswer(cmd, "IPMI power on failed"); + } + } + + s_logger.debug("Start bare metal vm " + vm.getName() + "successfully"); + state = State.Running; + _vmName = vm.getName(); + return new StartAnswer(cmd); + } finally { + if (state != State.Stopped) { + changeVmState(vm.getName(), state); + } else { + removeVmState(vm.getName()); + } + } + } + + protected HashMap deltaSync() { + final HashMap changes = new HashMap(); + + if (_vmName == null) { + return null; + } + + State newState = getVmState(); + if (newState == null) { + s_logger.warn("Cannot get power state of VM " + _vmName); + return null; + } + + final State oldState = removeVmState(_vmName); + if (oldState == null) { + changeVmState(_vmName, newState); + changes.put(_vmName, newState); + } else if (oldState == State.Starting) { + if (newState == State.Running) { + changeVmState(_vmName, newState); + } else if (newState == State.Stopped) { + s_logger.debug("Ignoring vm " + _vmName + " because of a lag in starting the vm."); + } + } else if (oldState == State.Migrating) { + s_logger.warn("How can baremetal VM get into migrating state???"); + } else if (oldState == State.Stopping) { + if (newState == State.Stopped) { + changeVmState(_vmName, newState); + } else if (newState == State.Running) { + s_logger.debug("Ignoring vm " + _vmName + " because of a lag in stopping the vm. "); + } + } else if (oldState != newState) { + changeVmState(_vmName, newState); + changes.put(_vmName, newState); + } + + return changes; + + } + + protected ReadyAnswer execute(ReadyCommand cmd) { + // derived resource should check if the PXE server is ready + s_logger.debug("Bare metal resource " + _name + " is ready"); + return new ReadyAnswer(cmd); + } + + @Override + public void disconnected() { + + } + + @Override + public IAgentControl getAgentControl() { + return _agentControl; + } + + @Override + public void setAgentControl(IAgentControl agentControl) { + _agentControl = agentControl; + } + +} diff --git a/server/src/com/cloud/baremetal/BareMetalTemplateAdapter.java b/server/src/com/cloud/baremetal/BareMetalTemplateAdapter.java new file mode 100644 index 00000000000..86b52908ccf --- /dev/null +++ b/server/src/com/cloud/baremetal/BareMetalTemplateAdapter.java @@ -0,0 +1,195 @@ +package com.cloud.baremetal; + +import java.util.Date; +import java.util.List; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.api.commands.DeleteIsoCmd; +import com.cloud.api.commands.RegisterIsoCmd; +import com.cloud.api.commands.RegisterTemplateCmd; +import com.cloud.configuration.ResourceCount.ResourceType; +import com.cloud.dc.DataCenterVO; +import com.cloud.event.EventTypes; +import com.cloud.event.UsageEventVO; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.storage.VMTemplateHostVO; +import com.cloud.storage.VMTemplateZoneVO; +import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; +import com.cloud.storage.VMTemplateVO; +import com.cloud.template.HyervisorTemplateAdapter; +import com.cloud.template.TemplateAdapter; +import com.cloud.template.TemplateAdapterBase; +import com.cloud.template.TemplateProfile; +import com.cloud.user.Account; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.DB; +import com.cloud.utils.exception.CloudRuntimeException; + +@Local(value=TemplateAdapter.class) +public class BareMetalTemplateAdapter extends TemplateAdapterBase implements TemplateAdapter { + private final static Logger s_logger = Logger.getLogger(BareMetalTemplateAdapter.class); + @Inject HostDao _hostDao; + + @Override + public TemplateProfile prepare(RegisterTemplateCmd cmd) throws ResourceAllocationException { + TemplateProfile profile = super.prepare(cmd); + + // PXE server must be added before adding any template; + if (profile.getZoneId() == null || profile.getZoneId() == -1) { + List dcs = _dcDao.listAllIncludingRemoved(); + for (DataCenterVO dc : dcs) { + List pxeServers = _hostDao.listAllBy(Host.Type.PxeServer, dc.getId()); + if (pxeServers.size() == 0) { + throw new CloudRuntimeException("Please add PXE server before adding baremetal template in zone " + dc.getName()); + } + } + } else { + List pxeServers = _hostDao.listAllBy(Host.Type.PxeServer, profile.getZoneId()); + if (pxeServers.size() == 0) { + throw new CloudRuntimeException("Please add PXE server before adding baremetal template in zone " + profile.getZoneId()); + } + } + + return profile; + } + + @Override + public TemplateProfile prepare(RegisterIsoCmd cmd) throws ResourceAllocationException { + throw new CloudRuntimeException("Baremetal doesn't support ISO template"); + } + + private void templateCreateUsage(VMTemplateVO template, HostVO host) { + if (template.getAccountId() != Account.ACCOUNT_ID_SYSTEM) { + UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_TEMPLATE_CREATE, template.getAccountId(), host.getDataCenterId(), + template.getId(), template.getName(), null, template.getSourceTemplateId(), 0L); + _usageEventDao.persist(usageEvent); + } + } + + @Override + public VMTemplateVO create(TemplateProfile profile) { + VMTemplateVO template = persistTemplate(profile); + Long zoneId = profile.getZoneId(); + + /* There is no secondary storage vm for baremetal, we use pxe server id. + * Tempalte is not bound to pxeserver right now, and we assume the pxeserver + * cannot be removed once it was added. so we use host id of first found pxe + * server as reference in template_host_ref. + * This maybe a FIXME in future. + */ + VMTemplateHostVO vmTemplateHost = null; + if (zoneId == null || zoneId == -1) { + List dcs = _dcDao.listAllIncludingRemoved(); + for (DataCenterVO dc : dcs) { + HostVO pxe = _hostDao.listAllBy(Host.Type.PxeServer, dc.getId()).get(0); + + vmTemplateHost = _tmpltHostDao.findByHostTemplate(dc.getId(), template.getId()); + if (vmTemplateHost == null) { + vmTemplateHost = new VMTemplateHostVO(pxe.getId(), template.getId(), new Date(), 100, + Status.DOWNLOADED, null, null, null, null, template.getUrl()); + _tmpltHostDao.persist(vmTemplateHost); + templateCreateUsage(template, pxe); + } + } + } else { + HostVO pxe = _hostDao.listAllBy(Host.Type.PxeServer, zoneId).get(0); + vmTemplateHost = new VMTemplateHostVO(pxe.getId(), template.getId(), new Date(), 100, + Status.DOWNLOADED, null, null, null, null, template.getUrl()); + _tmpltHostDao.persist(vmTemplateHost); + templateCreateUsage(template, pxe); + } + + _accountMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); + return template; + } + + public TemplateProfile prepareDelete(DeleteIsoCmd cmd) { + throw new CloudRuntimeException("Baremetal doesn't support ISO, how the delete get here???"); + } + + @Override @DB + public boolean delete(TemplateProfile profile) { + VMTemplateVO template = profile.getTemplate(); + Long templateId = template.getId(); + boolean success = true; + String zoneName; + boolean isAllZone; + + if (!template.isCrossZones() && profile.getZoneId() != null) { + isAllZone = false; + zoneName = profile.getZoneId().toString(); + } else { + zoneName = "all zones"; + isAllZone = true; + } + + s_logger.debug("Attempting to mark template host refs for template: " + template.getName() + " as destroyed in zone: " + zoneName); + Account account = _accountDao.findByIdIncludingRemoved(template.getAccountId()); + String eventType = EventTypes.EVENT_TEMPLATE_DELETE; + List templateHostVOs = _tmpltHostDao.listByTemplateId(templateId); + + for (VMTemplateHostVO vo : templateHostVOs) { + VMTemplateHostVO lock = null; + try { + HostVO pxeServer = _hostDao.findById(vo.getHostId()); + if (!isAllZone && pxeServer.getDataCenterId() != profile.getZoneId()) { + continue; + } + + lock = _tmpltHostDao.acquireInLockTable(vo.getId()); + if (lock == null) { + s_logger.debug("Failed to acquire lock when deleting templateHostVO with ID: " + vo.getId()); + success = false; + break; + } + + vo.setDestroyed(true); + _tmpltHostDao.update(vo.getId(), vo); + VMTemplateZoneVO templateZone = _tmpltZoneDao.findByZoneTemplate(pxeServer.getDataCenterId(), templateId); + if (templateZone != null) { + _tmpltZoneDao.remove(templateZone.getId()); + } + + UsageEventVO usageEvent = new UsageEventVO(eventType, account.getId(), pxeServer.getDataCenterId(), templateId, null); + _usageEventDao.persist(usageEvent); + } finally { + if (lock != null) { + _tmpltHostDao.releaseFromLockTable(lock.getId()); + } + } + } + + s_logger.debug("Successfully marked template host refs for template: " + template.getName() + " as destroyed in zone: " + zoneName); + + // If there are no more non-destroyed template host entries for this template, delete it + if (success && (_tmpltHostDao.listByTemplateId(templateId).size() == 0)) { + long accountId = template.getAccountId(); + + VMTemplateVO lock = _tmpltDao.acquireInLockTable(templateId); + + try { + if (lock == null) { + s_logger.debug("Failed to acquire lock when deleting template with ID: " + templateId); + success = false; + } else if (_tmpltDao.remove(templateId)) { + // Decrement the number of templates + _accountMgr.decrementResourceCount(accountId, ResourceType.template); + } + + } finally { + if (lock != null) { + _tmpltDao.releaseFromLockTable(lock.getId()); + } + } + s_logger.debug("Removed template: " + template.getName() + " because all of its template host refs were marked as destroyed."); + } + + return success; + } +} diff --git a/server/src/com/cloud/baremetal/BareMetalVmManager.java b/server/src/com/cloud/baremetal/BareMetalVmManager.java new file mode 100644 index 00000000000..d47d81913a1 --- /dev/null +++ b/server/src/com/cloud/baremetal/BareMetalVmManager.java @@ -0,0 +1,25 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import com.cloud.vm.UserVmManager; + +public interface BareMetalVmManager extends UserVmManager { +} diff --git a/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java b/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java new file mode 100755 index 00000000000..c1225737859 --- /dev/null +++ b/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java @@ -0,0 +1,553 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.StopAnswer; +import com.cloud.agent.api.baremetal.IpmISetBootDevCommand; +import com.cloud.agent.api.baremetal.IpmiBootorResetCommand; +import com.cloud.agent.manager.Commands; +import com.cloud.api.commands.AttachVolumeCmd; +import com.cloud.api.commands.CreateTemplateCmd; +import com.cloud.api.commands.DeployVMCmd; +import com.cloud.api.commands.DetachVolumeCmd; +import com.cloud.api.commands.UpgradeVMCmd; +import com.cloud.baremetal.PxeServerManager.PxeServerType; +import com.cloud.configuration.ResourceCount.ResourceType; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.DataCenterVO; +import com.cloud.deploy.DataCenterDeployment; +import com.cloud.deploy.DeployDestination; +import com.cloud.domain.DomainVO; +import com.cloud.event.EventTypes; +import com.cloud.event.UsageEventVO; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.exception.StorageUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.network.NetworkVO; +import com.cloud.network.Networks.TrafficType; +import com.cloud.org.Grouping; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.storage.Storage; +import com.cloud.storage.Storage.TemplateType; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.Volume; +import com.cloud.template.TemplateAdapter; +import com.cloud.template.TemplateAdapter.TemplateAdapterType; +import com.cloud.template.TemplateProfile; +import com.cloud.user.Account; +import com.cloud.user.AccountVO; +import com.cloud.user.SSHKeyPair; +import com.cloud.user.User; +import com.cloud.user.UserContext; +import com.cloud.uservm.UserVm; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.Pair; +import com.cloud.utils.component.Adapters; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.component.Inject; +import com.cloud.utils.component.Manager; +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.db.DB; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.StateListener; +import com.cloud.utils.net.NetUtils; +import com.cloud.vm.BareMetalVmService; +import com.cloud.vm.NicProfile; +import com.cloud.vm.NicVO; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmManagerImpl; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.Event; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.VirtualMachineName; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.VirtualMachineProfile.Param; + +@Local(value={BareMetalVmManager.class, BareMetalVmService.class}) +public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMetalVmManager, BareMetalVmService, Manager, + StateListener { + private static final Logger s_logger = Logger.getLogger(BareMetalVmManagerImpl.class); + private ConfigurationDao _configDao; + @Inject PxeServerManager _pxeMgr; + + @Inject (adapter=TemplateAdapter.class) + protected Adapters _adapters; + + @Override + public boolean attachISOToVM(long vmId, long isoId, boolean attach) { + s_logger.warn("attachISOToVM is not supported by Bare Metal, just fake a true"); + return true; + } + + @Override + public Volume attachVolumeToVM(AttachVolumeCmd command) { + s_logger.warn("attachVolumeToVM is not supported by Bare Metal, return null"); + return null; + } + + @Override + public Volume detachVolumeFromVM(DetachVolumeCmd cmd) { + s_logger.warn("detachVolumeFromVM is not supported by Bare Metal, return null"); + return null; + } + + @Override + public UserVm upgradeVirtualMachine(UpgradeVMCmd cmd) { + s_logger.warn("upgradeVirtualMachine is not supported by Bare Metal, return null"); + return null; + } + + @Override + public VMTemplateVO createPrivateTemplateRecord(CreateTemplateCmd cmd) throws ResourceAllocationException { + /*Baremetal creates record after host rebooting for imaging, in createPrivateTemplate*/ + return null; + } + + @Override @DB + public VMTemplateVO createPrivateTemplate(CreateTemplateCmd cmd) throws CloudRuntimeException { + Long vmId = cmd.getVmId(); + if (vmId == null) { + throw new InvalidParameterValueException("VM ID is null"); + } + + UserVmVO vm = _vmDao.findById(vmId); + if (vm == null) { + throw new InvalidParameterValueException("Cannot find VM for ID " + vmId); + } + + Long hostId = (vm.getHostId() == null ? vm.getLastHostId() : vm.getHostId()); + HostVO host = _hostDao.findById(hostId); + if (host == null) { + throw new InvalidParameterValueException("Cannot find host with id " + hostId); + } + + List pxes = _hostDao.listBy(Host.Type.PxeServer, null, host.getPodId(), host.getDataCenterId()); + if (pxes.size() == 0) { + throw new CloudRuntimeException("Please add PXE server in Pod before taking image"); + } + + if (pxes.size() > 1) { + throw new CloudRuntimeException("Multiple PXE servers found in Pod " + host.getPodId() + " Zone " + host.getDataCenterId()); + } + + HostVO pxe = pxes.get(0); + /* + * prepare() will check if current account has right for creating + * template + */ + TemplateAdapter adapter = _adapters.get(TemplateAdapterType.BareMetal.getName()); + Long userId = UserContext.current().getCallerUserId(); + userId = (userId == null ? User.UID_SYSTEM : userId); + AccountVO account = _accountDao.findById(vm.getAccountId()); + + try { + TemplateProfile tmplProfile; + tmplProfile = adapter.prepare(false, userId, cmd.getTemplateName(), cmd.getDisplayText(), cmd.getBits(), false, false, cmd.getUrl(), cmd.isPublic(), cmd.isFeatured(), false, + "BareMetal", cmd.getOsTypeId(), pxe.getDataCenterId(), HypervisorType.BareMetal, account.getAccountName(), account.getDomainId(), "0", true); + + if (!_pxeMgr.prepareCreateTemplate(_pxeMgr.getPxeServerType(pxe), pxe.getId(), vm, cmd.getUrl())) { + throw new Exception("Prepare PXE boot file for host " + hostId + " failed"); + } + + IpmISetBootDevCommand setBootDev = new IpmISetBootDevCommand(IpmISetBootDevCommand.BootDev.pxe); + Answer ans = _agentMgr.send(hostId, setBootDev); + if (!ans.getResult()) { + throw new Exception("Set host " + hostId + " to PXE boot failed"); + } + + IpmiBootorResetCommand boot = new IpmiBootorResetCommand(); + ans = _agentMgr.send(hostId, boot); + if (!ans.getResult()) { + throw new Exception("Boot/Reboot host " + hostId + " failed"); + } + + VMTemplateVO tmpl = adapter.create(tmplProfile); + s_logger.debug("Create baremetal template for host " + hostId + " successfully, template id:" + tmpl.getId()); + return tmpl; + } catch (Exception e) { + s_logger.debug("Create baremetal tempalte for host " + hostId + " failed", e); + throw new CloudRuntimeException(e.getMessage()); + } + } + + @Override + public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, + StorageUnavailableException, ResourceAllocationException { + Account caller = UserContext.current().getCaller(); + + String accountName = cmd.getAccountName(); + Long domainId = cmd.getDomainId(); + List networkList = cmd.getNetworkIds(); + String group = cmd.getGroup(); + + Account owner = _accountDao.findActiveAccount(accountName, domainId); + if (owner == null) { + throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId); + } + + _accountMgr.checkAccess(caller, owner); + long accountId = owner.getId(); + + DataCenterVO dc = _dcDao.findById(cmd.getZoneId()); + if (dc == null) { + throw new InvalidParameterValueException("Unable to find zone: " + cmd.getZoneId()); + } + + if(Grouping.AllocationState.Disabled == dc.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())){ + throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: "+ cmd.getZoneId() ); + } + + if (dc.getDomainId() != null) { + DomainVO domain = _domainDao.findById(dc.getDomainId()); + if (domain == null) { + throw new CloudRuntimeException("Unable to find the domain " + dc.getDomainId() + " for the zone: " + dc); + } + _accountMgr.checkAccess(caller, domain); + _accountMgr.checkAccess(owner, domain); + } + + // check if account/domain is with in resource limits to create a new vm + if (_accountMgr.resourceLimitExceeded(owner, ResourceType.user_vm)) { + ResourceAllocationException rae = new ResourceAllocationException("Maximum number of virtual machines for account: " + owner.getAccountName() + + " has been exceeded."); + rae.setResourceType("vm"); + throw rae; + } + + ServiceOfferingVO offering = _serviceOfferingDao.findById(cmd.getServiceOfferingId()); + if (offering == null || offering.getRemoved() != null) { + throw new InvalidParameterValueException("Unable to find service offering: " + cmd.getServiceOfferingId()); + } + + VMTemplateVO template = _templateDao.findById(cmd.getTemplateId()); + // Make sure a valid template ID was specified + if (template == null || template.getRemoved() != null) { + throw new InvalidParameterValueException("Unable to use template " + cmd.getTemplateId()); + } + + if (template.getTemplateType().equals(TemplateType.SYSTEM)) { + throw new InvalidParameterValueException("Unable to use system template " + cmd.getTemplateId()+" to deploy a user vm"); + } + + if (template.getFormat() != Storage.ImageFormat.BAREMETAL) { + throw new InvalidParameterValueException("Unable to use non Bare Metal template" + cmd.getTemplateId() +" to deploy a bare metal vm"); + } + + String userData = cmd.getUserData(); + byte [] decodedUserData = null; + if (userData != null) { + if (userData.length() >= 2 * MAX_USER_DATA_LENGTH_BYTES) { + throw new InvalidParameterValueException("User data is too long"); + } + decodedUserData = org.apache.commons.codec.binary.Base64.decodeBase64(userData.getBytes()); + if (decodedUserData.length > MAX_USER_DATA_LENGTH_BYTES){ + throw new InvalidParameterValueException("User data is too long"); + } + if (decodedUserData.length < 1) { + throw new InvalidParameterValueException("User data is too short"); + } + } + + // Find an SSH public key corresponding to the key pair name, if one is given + String sshPublicKey = null; + if (cmd.getSSHKeyPairName() != null && !cmd.getSSHKeyPairName().equals("")) { + Account account = UserContext.current().getCaller(); + SSHKeyPair pair = _sshKeyPairDao.findByName(account.getAccountId(), account.getDomainId(), cmd.getSSHKeyPairName()); + if (pair == null) { + throw new InvalidParameterValueException("A key pair with name '" + cmd.getSSHKeyPairName() + "' was not found."); + } + + sshPublicKey = pair.getPublicKey(); + } + + _accountMgr.checkAccess(caller, template); + + DataCenterDeployment plan = new DataCenterDeployment(dc.getId()); + + s_logger.debug("Allocating in the DB for bare metal vm"); + + if (dc.getNetworkType() != NetworkType.Basic || networkList != null) { + s_logger.warn("Bare Metal only supports basical network mode now, switch to baisc network automatically"); + } + + Network defaultNetwork = _networkMgr.getSystemNetworkByZoneAndTrafficType(dc.getId(), TrafficType.Guest); + if (defaultNetwork == null) { + throw new InvalidParameterValueException("Unable to find a default network to start a vm"); + } + + networkList = new ArrayList(); + networkList.add(defaultNetwork.getId()); + + List> networks = new ArrayList>(); + short defaultNetworkNumber = 0; + for (Long networkId : networkList) { + NetworkVO network = _networkDao.findById(networkId); + if (network == null) { + throw new InvalidParameterValueException("Unable to find network by id " + networkId); + } else { + if (!network.getIsShared()) { + //Check account permissions + List networkMap = _networkDao.listBy(accountId, networkId); + if (networkMap == null || networkMap.isEmpty()) { + throw new PermissionDeniedException("Unable to create a vm using network with id " + networkId + ", permission denied"); + } + } + + if (network.isDefault()) { + defaultNetworkNumber++; + } + networks.add(new Pair(network, null)); + } + } + + //at least one network default network has to be set + if (defaultNetworkNumber == 0) { + throw new InvalidParameterValueException("At least 1 default network has to be specified for the vm"); + } else if (defaultNetworkNumber >1) { + throw new InvalidParameterValueException("Only 1 default network per vm is supported"); + } + + long id = _vmDao.getNextInSequence(Long.class, "id"); + + String hostName = cmd.getName(); + String instanceName = VirtualMachineName.getVmName(id, owner.getId(), _instance); + if (hostName == null) { + hostName = instanceName; + } else { + //verify hostName (hostname doesn't have to be unique) + if (!NetUtils.verifyDomainNameLabel(hostName, true)) { + throw new InvalidParameterValueException("Invalid name. Vm name can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + + "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit"); + } + } + + UserVmVO vm = new UserVmVO(id, instanceName, cmd.getDisplayName(), template.getId(), HypervisorType.BareMetal, + template.getGuestOSId(), offering.getOfferHA(), false, domainId, owner.getId(), offering.getId(), userData, hostName); + + if (sshPublicKey != null) { + vm.setDetail("SSH.PublicKey", sshPublicKey); + } + + if (_itMgr.allocate(vm, template, offering, null, null, networks, null, plan, cmd.getHypervisor(), owner) == null) { + return null; + } + + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Successfully allocated DB entry for " + vm); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Successfully allocated DB entry for " + vm); + } + UserContext.current().setEventDetails("Vm Id: " + vm.getId()); + UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_CREATE, accountId, cmd.getZoneId(), vm.getId(), vm.getHostName(), offering.getId(), template.getId(), HypervisorType.BareMetal.toString()); + _usageEventDao.persist(usageEvent); + + _accountMgr.incrementResourceCount(accountId, ResourceType.user_vm); + + // Assign instance to the group + try { + if (group != null) { + boolean addToGroup = addInstanceToGroup(Long.valueOf(id), group); + if (!addToGroup) { + throw new CloudRuntimeException("Unable to assign Vm to the group " + group); + } + } + } catch (Exception ex) { + throw new CloudRuntimeException("Unable to assign Vm to the group " + group); + } + + return vm; + } + + public UserVm startVirtualMachine(DeployVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException { + long vmId = cmd.getEntityId(); + UserVmVO vm = _vmDao.findById(vmId); + + List servers = _hostDao.listBy(Host.Type.PxeServer, vm.getDataCenterIdToDeployIn()); + if (servers.size() == 0) { + throw new CloudRuntimeException("Cannot find PXE server, please make sure there is one PXE server per zone"); + } + HostVO pxeServer = servers.get(0); + + VMTemplateVO template = _templateDao.findById(vm.getTemplateId()); + if (template == null || template.getFormat() != Storage.ImageFormat.BAREMETAL) { + throw new InvalidParameterValueException("Invalid template with id = " + vm.getTemplateId()); + } + + Map params = new HashMap(); + params.put(Param.PxeSeverType, _pxeMgr.getPxeServerType(pxeServer)); + + return startVirtualMachine(cmd, params); + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + + ComponentLocator locator = ComponentLocator.getCurrentLocator(); + _configDao = locator.getDao(ConfigurationDao.class); + if (_configDao == null) { + throw new ConfigurationException("Unable to get the configuration dao."); + } + + Map configs = _configDao.getConfiguration("AgentManager", params); + + _instance = configs.get("instance.name"); + if (_instance == null) { + _instance = "DEFAULT"; + } + + String workers = configs.get("expunge.workers"); + int wrks = NumbersUtil.parseInt(workers, 10); + + String time = configs.get("expunge.interval"); + _expungeInterval = NumbersUtil.parseInt(time, 86400); + + time = configs.get("expunge.delay"); + _expungeDelay = NumbersUtil.parseInt(time, _expungeInterval); + + _executor = Executors.newScheduledThreadPool(wrks, new NamedThreadFactory("UserVm-Scavenger")); + + _itMgr.registerGuru(Type.UserBareMetal, this); + VirtualMachine.State.getStateMachine().registerListener(this); + + s_logger.info("User VM Manager is configured."); + + return true; + } + + @Override + public boolean finalizeVirtualMachineProfile(VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) { + UserVmVO vm = profile.getVirtualMachine(); + Account owner = _accountDao.findById(vm.getAccountId()); + + if (owner == null || owner.getState() == Account.State.disabled) { + throw new PermissionDeniedException("The owner of " + vm + " either does not exist or is disabled: " + vm.getAccountId()); + } + + PxeServerType pxeType = (PxeServerType) profile.getParameter(Param.PxeSeverType); + if (pxeType == null) { + s_logger.debug("This is a normal IPMI start, skip prepartion of PXE server"); + return true; + } + s_logger.debug("This is a PXE start, prepare PXE server first"); + + List servers = _hostDao.listBy(Host.Type.PxeServer, vm.getDataCenterIdToDeployIn()); + if (servers.size() == 0) { + throw new CloudRuntimeException("Cannot find PXE server, please make sure there is one PXE server per zone"); + } + HostVO pxeServer = servers.get(0); + + if (!_pxeMgr.prepare(pxeType, profile, dest, context, pxeServer.getId())) { + throw new CloudRuntimeException("Pepare PXE server failed"); + } + + profile.addBootArgs("PxeBoot"); + + return true; + } + + @Override + public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) { + UserVmVO userVm = profile.getVirtualMachine(); + List nics = _nicDao.listByVmId(userVm.getId()); + for (NicVO nic : nics) { + NetworkVO network = _networkDao.findById(nic.getNetworkId()); + if (network.getTrafficType() == TrafficType.Guest) { + userVm.setPrivateIpAddress(nic.getIp4Address()); + userVm.setPrivateMacAddress(nic.getMacAddress()); + } + } + _vmDao.update(userVm.getId(), userVm); + return true; + } + + @Override + public void finalizeStop(VirtualMachineProfile profile, StopAnswer answer) { + super.finalizeStop(profile, answer); + } + + @Override + public UserVm destroyVm(long vmId) throws ResourceUnavailableException, ConcurrentOperationException { + return super.destroyVm(vmId); + } + + @Override + public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Long id) { + return true; + } + + @Override + public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Long id) { + if (newState != State.Starting && newState != State.Error && newState != State.Expunging) { + return true; + } + + if (vo.getHypervisorType() != HypervisorType.BareMetal) { + return true; + } + + HostVO host = _hostDao.findById(vo.getHostId()); + if (host == null) { + s_logger.debug("Skip oldState " + oldState + " to " + "newState " + newState + " transimtion"); + return true; + } + _hostDao.loadDetails(host); + + if (newState == State.Starting) { + host.setDetail("vmName", vo.getInstanceName()); + s_logger.debug("Add vmName " + host.getDetail("vmName") + " to host " + host.getId() + " details"); + } else { + if (host.getDetail("vmName") != null && host.getDetail("vmName").equalsIgnoreCase(vo.getInstanceName())) { + s_logger.debug("Remove vmName " + host.getDetail("vmName") + " from host " + host.getId() + " details"); + host.getDetails().remove("vmName"); + } + } + _hostDao.saveDetails(host); + + + return true; + } +} diff --git a/server/src/com/cloud/baremetal/DhcpServerResponse.java b/server/src/com/cloud/baremetal/DhcpServerResponse.java new file mode 100644 index 00000000000..caf80c2028d --- /dev/null +++ b/server/src/com/cloud/baremetal/DhcpServerResponse.java @@ -0,0 +1,38 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class DhcpServerResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) @Param(description="the ID of the Dhcp server") + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/server/src/com/cloud/baremetal/DhcpdResource.java b/server/src/com/cloud/baremetal/DhcpdResource.java new file mode 100644 index 00000000000..9aef29433de --- /dev/null +++ b/server/src/com/cloud/baremetal/DhcpdResource.java @@ -0,0 +1,133 @@ +/** + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.baremetal; + +import java.util.HashMap; +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.routing.DhcpEntryCommand; +import com.cloud.utils.script.Script; +import com.cloud.utils.ssh.SSHCmdHelper; +import com.cloud.vm.VirtualMachine.State; +import com.trilead.ssh2.SCPClient; + +public class DhcpdResource extends ExternalDhcpResourceBase { + private static final Logger s_logger = Logger.getLogger(DhcpdResource.class); + + public boolean configure(String name, Map params) throws ConfigurationException { + com.trilead.ssh2.Connection sshConnection = null; + try { + super.configure(name, params); + s_logger.debug(String.format("Trying to connect to DHCP server(IP=%1$s, username=%2$s, password=%3$s)", _ip, _username, _password)); + sshConnection = SSHCmdHelper.acquireAuthorizedConnection(_ip, _username, _password); + if (sshConnection == null) { + throw new ConfigurationException( + String.format("Cannot connect to DHCP server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, _password)); + } + + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, "[ -f '/usr/sbin/dhcpd' ]")) { + throw new ConfigurationException("Cannot find dhcpd.conf /etc/dhcpd.conf at on " + _ip); + } + + SCPClient scp = new SCPClient(sshConnection); + + String editHosts = "scripts/network/exdhcp/dhcpd_edithosts.py"; + String editHostsPath = Script.findScript("", editHosts); + if (editHostsPath == null) { + throw new ConfigurationException("Can not find script dnsmasq_edithosts.sh at " + editHosts); + } + scp.put(editHostsPath, "/usr/bin/", "0755"); + + String prepareDhcpdScript = "scripts/network/exdhcp/prepare_dhcpd.sh"; + String prepareDhcpdScriptPath = Script.findScript("", prepareDhcpdScript); + if (prepareDhcpdScriptPath == null) { + throw new ConfigurationException("Can not find prepare_dhcpd.sh at " + prepareDhcpdScriptPath); + } + scp.put(prepareDhcpdScriptPath, "/usr/bin/", "0755"); + + //TODO: tooooooooooooooo ugly here!!! + String[] ips = _ip.split("\\."); + ips[3] = "0"; + StringBuffer buf = new StringBuffer(); + int i; + for (i=0;i()); + } + } + + Answer execute(DhcpEntryCommand cmd) { + com.trilead.ssh2.Connection sshConnection = null; + try { + sshConnection = SSHCmdHelper.acquireAuthorizedConnection(_ip, _username, _password); + if (sshConnection == null) { + return new Answer(cmd, false, "ssh authenticate failed"); + } + String addDhcp = String.format("python /usr/bin/dhcpd_edithosts.py %1$s %2$s %3$s %4$s %5$s %6$s", + cmd.getVmMac(), cmd.getVmIpAddress(), cmd.getVmName(), cmd.getDns(), cmd.getGateway(), cmd.getNextServer()); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, addDhcp)) { + return new Answer(cmd, false, "add Dhcp entry failed"); + } else { + return new Answer(cmd); + } + } finally { + SSHCmdHelper.releaseSshConnection(sshConnection); + } + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof DhcpEntryCommand) { + return execute((DhcpEntryCommand)cmd); + } else { + return super.executeRequest(cmd); + } + } +} diff --git a/server/src/com/cloud/baremetal/DnsmasqResource.java b/server/src/com/cloud/baremetal/DnsmasqResource.java new file mode 100644 index 00000000000..6e0ca1914f2 --- /dev/null +++ b/server/src/com/cloud/baremetal/DnsmasqResource.java @@ -0,0 +1,123 @@ +/** + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.baremetal; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.routing.DhcpEntryCommand; +import com.cloud.utils.script.Script; +import com.cloud.utils.ssh.SSHCmdHelper; +import com.cloud.vm.VirtualMachine.State; +import com.trilead.ssh2.SCPClient; + +public class DnsmasqResource extends ExternalDhcpResourceBase { + private static final Logger s_logger = Logger.getLogger(DnsmasqResource.class); + + public boolean configure(String name, Map params) throws ConfigurationException { + com.trilead.ssh2.Connection sshConnection = null; + try { + super.configure(name, params); + s_logger.debug(String.format("Trying to connect to DHCP server(IP=%1$s, username=%2$s, password=%3$s)", _ip, _username, _password)); + sshConnection = SSHCmdHelper.acquireAuthorizedConnection(_ip, _username, _password); + if (sshConnection == null) { + throw new ConfigurationException( + String.format("Cannot connect to DHCP server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, _password)); + } + + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, "[ -f '/usr/sbin/dnsmasq' ]")) { + throw new ConfigurationException("Cannot find dnsmasq at /usr/sbin/dnsmasq on " + _ip); + } + + SCPClient scp = new SCPClient(sshConnection); + + String editHosts = "scripts/network/exdhcp/dnsmasq_edithosts.sh"; + String editHostsPath = Script.findScript("", editHosts); + if (editHostsPath == null) { + throw new ConfigurationException("Can not find script dnsmasq_edithosts.sh at " + editHosts); + } + scp.put(editHostsPath, "/usr/bin/", "0755"); + + String prepareDnsmasq = "scripts/network/exdhcp/prepare_dnsmasq.sh"; + String prepareDnsmasqPath = Script.findScript("", prepareDnsmasq); + if (prepareDnsmasqPath == null) { + throw new ConfigurationException("Can not find script prepare_dnsmasq.sh at " + prepareDnsmasq); + } + scp.put(prepareDnsmasqPath, "/usr/bin/", "0755"); + + String prepareCmd = String.format("sh /usr/bin/prepare_dnsmasq.sh %1$s %2$s %3$s", _gateway, _dns, _ip); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, prepareCmd)) { + throw new ConfigurationException("prepare dnsmasq at " + _ip + " failed"); + } + + s_logger.debug("Dnsmasq resource configure successfully"); + return true; + } catch (Exception e) { + s_logger.debug("Dnsmasq resorce configure failed", e); + throw new ConfigurationException(e.getMessage()); + } finally { + SSHCmdHelper.releaseSshConnection(sshConnection); + } + } + + @Override + public PingCommand getCurrentStatus(long id) { + com.trilead.ssh2.Connection sshConnection = SSHCmdHelper.acquireAuthorizedConnection(_ip, _username, _password); + if (sshConnection == null) { + return null; + } else { + SSHCmdHelper.releaseSshConnection(sshConnection); + return new PingRoutingCommand(getType(), id, new HashMap()); + } + } + + Answer execute(DhcpEntryCommand cmd) { + com.trilead.ssh2.Connection sshConnection = null; + try { + sshConnection = SSHCmdHelper.acquireAuthorizedConnection(_ip, _username, _password); + if (sshConnection == null) { + return new Answer(cmd, false, "ssh authenticate failed"); + } + String addDhcp = String.format("/usr/bin/dnsmasq_edithosts.sh %1$s %2$s %3$s", cmd.getVmMac(), cmd.getVmIpAddress(), cmd.getVmName()); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, addDhcp)) { + return new Answer(cmd, false, "add Dhcp entry failed"); + } else { + return new Answer(cmd); + } + } finally { + SSHCmdHelper.releaseSshConnection(sshConnection); + } + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof DhcpEntryCommand) { + return execute((DhcpEntryCommand)cmd); + } else { + return super.executeRequest(cmd); + } + } +} diff --git a/server/src/com/cloud/baremetal/ExternalDhcpEntryListener.java b/server/src/com/cloud/baremetal/ExternalDhcpEntryListener.java new file mode 100644 index 00000000000..4270a94f40b --- /dev/null +++ b/server/src/com/cloud/baremetal/ExternalDhcpEntryListener.java @@ -0,0 +1,47 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +public interface ExternalDhcpEntryListener { + public class DhcpEntryState { + String _name; + + public static final DhcpEntryState add = new DhcpEntryState("add"); + public static final DhcpEntryState old = new DhcpEntryState("old"); + public static final DhcpEntryState del = new DhcpEntryState("del"); + + public DhcpEntryState(String name) { + _name = name; + } + + public String getName() { + return _name; + } + } + + /** + * Notify that DHCP entry state change + * @param ip + * @param mac + * @param DHCP entry state + * @return: true means continuous listen on the entry, false cancels the listener + */ + public boolean notify(String ip, String mac, DhcpEntryState state, Object userData); +} diff --git a/server/src/com/cloud/baremetal/ExternalDhcpManager.java b/server/src/com/cloud/baremetal/ExternalDhcpManager.java new file mode 100644 index 00000000000..98857a5b6c9 --- /dev/null +++ b/server/src/com/cloud/baremetal/ExternalDhcpManager.java @@ -0,0 +1,57 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import com.cloud.baremetal.ExternalDhcpEntryListener.DhcpEntryState; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.network.Network; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Manager; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +public interface ExternalDhcpManager extends Manager { + public static class DhcpServerType { + private String _name; + + public static final DhcpServerType Dnsmasq = new DhcpServerType("Dnsmasq"); + public static final DhcpServerType Dhcpd = new DhcpServerType("Dhcpd"); + + public DhcpServerType(String name) { + _name = name; + } + + public String getName() { + return _name; + } + + } + + + DhcpServerResponse getApiResponse(Host dhcpServer); + + boolean addVirtualMachineIntoNetwork(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException; + + Host addDhcpServer(Long zoneId, Long podId, String type, String url, String username, String password); +} diff --git a/server/src/com/cloud/baremetal/ExternalDhcpManagerImpl.java b/server/src/com/cloud/baremetal/ExternalDhcpManagerImpl.java new file mode 100755 index 00000000000..0d6ddfff729 --- /dev/null +++ b/server/src/com/cloud/baremetal/ExternalDhcpManagerImpl.java @@ -0,0 +1,238 @@ +/** + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.baremetal; + +import java.net.URI; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.Listener; +import com.cloud.agent.api.AgentControlAnswer; +import com.cloud.agent.api.AgentControlCommand; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.routing.DhcpEntryCommand; +import com.cloud.agent.manager.Commands; +import com.cloud.baremetal.ExternalDhcpEntryListener.DhcpEntryState; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.HostPodVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.HostPodDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.ConnectionException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.Host.Type; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.resource.ServerResource; +import com.cloud.utils.component.Inject; +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.UserVmDao; + +@Local(value = {ExternalDhcpManager.class}) +public class ExternalDhcpManagerImpl implements ExternalDhcpManager { + private static final org.apache.log4j.Logger s_logger = Logger.getLogger(ExternalDhcpManagerImpl.class); + protected String _name; + @Inject DataCenterDao _dcDao; + @Inject HostDao _hostDao; + @Inject AgentManager _agentMgr; + @Inject HostPodDao _podDao; + @Inject UserVmDao _userVmDao; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + protected String getDhcpServerGuid(String zoneId, String name, String ip) { + return zoneId + "-" + name + "-" + ip; + } + + + @Override @DB + public Host addDhcpServer(Long zoneId, Long podId, String type, String url, String username, String password) { + DataCenterVO zone = _dcDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Could not find zone with ID: " + zoneId); + } + + HostPodVO pod = _podDao.findById(podId); + if (pod == null) { + throw new InvalidParameterValueException("Could not find pod with ID: " + podId); + } + + List dhcps = _hostDao.listBy(Host.Type.ExternalDhcp, null, podId, zoneId); + if (dhcps.size() != 0) { + throw new InvalidParameterValueException("Already had a DHCP server in Pod: " + podId + " zone: " + zoneId); + } + + + String ipAddress = url; + String guid = getDhcpServerGuid(Long.toString(zoneId) + "-" + Long.toString(podId), "ExternalDhcp", ipAddress); + Map params = new HashMap(); + params.put("type", type); + params.put("zone", Long.toString(zoneId)); + params.put("pod", podId.toString()); + params.put("ip", ipAddress); + params.put("username", username); + params.put("password", password); + params.put("guid", guid); + params.put("pod", Long.toString(podId)); + params.put("gateway", pod.getGateway()); + String dns = zone.getDns1(); + if (dns == null) { + dns = zone.getDns2(); + } + params.put("dns", dns); + + ServerResource resource = null; + try { + if (type.equalsIgnoreCase(DhcpServerType.Dnsmasq.getName())) { + resource = new DnsmasqResource(); + resource.configure("Dnsmasq resource", params); + } else if (type.equalsIgnoreCase(DhcpServerType.Dhcpd.getName())) { + resource = new DhcpdResource(); + resource.configure("Dhcpd resource", params); + } else { + throw new CloudRuntimeException("Unsupport DHCP server " + type); + } + } catch (Exception e) { + s_logger.debug(e); + throw new CloudRuntimeException(e.getMessage()); + } + + Host dhcpServer = _agentMgr.addHost(zoneId, resource, Host.Type.ExternalDhcp, params); + if (dhcpServer == null) { + throw new CloudRuntimeException("Cannot add external Dhcp server as a host"); + } + + Transaction txn = Transaction.currentTxn(); + txn.start(); + pod.setExternalDhcp(true); + _podDao.update(pod.getId(), pod); + txn.commit(); + return dhcpServer; + } + + @Override + public DhcpServerResponse getApiResponse(Host dhcpServer) { + DhcpServerResponse response = new DhcpServerResponse(); + response.setId(dhcpServer.getId()); + return response; + } + + private void prepareBareMetalDhcpEntry(NicProfile nic, DhcpEntryCommand cmd) { + Long vmId = nic.getVmId(); + UserVmVO vm = _userVmDao.findById(vmId); + if (vm == null || vm.getHypervisorType() != HypervisorType.BareMetal) { + s_logger.debug("VM " + vmId + " is not baremetal machine, skip preparing baremetal DHCP entry"); + return; + } + + List servers = _hostDao.listBy(Host.Type.PxeServer, null, vm.getPodIdToDeployIn(), vm.getDataCenterIdToDeployIn()); + if (servers.size() != 1) { + throw new CloudRuntimeException("Wrong number of PXE server found in zone " + vm.getDataCenterIdToDeployIn() + + " Pod " + vm.getPodIdToDeployIn() + ", number is " + servers.size()); + } + HostVO pxeServer = servers.get(0); + cmd.setNextServer(pxeServer.getPrivateIpAddress()); + s_logger.debug("Set next-server to " + pxeServer.getPrivateIpAddress() + " for VM " + vm.getId()); + } + + @Override + public boolean addVirtualMachineIntoNetwork(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, + ReservationContext context) throws ResourceUnavailableException { + Long zoneId = profile.getVirtualMachine().getDataCenterIdToDeployIn(); + Long podId = profile.getVirtualMachine().getPodIdToDeployIn(); + List hosts = _hostDao.listBy(Type.ExternalDhcp, null, podId, zoneId); + if (hosts.size() == 0) { + throw new CloudRuntimeException("No external Dhcp found in zone " + zoneId + " pod " + podId); + } + + if (hosts.size() > 1) { + throw new CloudRuntimeException("Something wrong, more than 1 external Dhcp found in zone " + zoneId + " pod " + podId); + } + + HostVO h = hosts.get(0); + String dns = nic.getDns1(); + if (dns == null) { + dns = nic.getDns2(); + } + DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), profile.getVirtualMachine().getHostName(), dns, nic.getGateway()); + String errMsg = String.format("Set dhcp entry on external DHCP %1$s failed(ip=%2$s, mac=%3$s, vmname=%4$s)", + h.getPrivateIpAddress(), nic.getIp4Address(), nic.getMacAddress(), profile.getVirtualMachine().getHostName()); + //prepareBareMetalDhcpEntry(nic, dhcpCommand); + try { + Answer ans = _agentMgr.send(h.getId(), dhcpCommand); + if (ans.getResult()) { + s_logger.debug(String.format("Set dhcp entry on external DHCP %1$s successfully(ip=%2$s, mac=%3$s, vmname=%4$s)", + h.getPrivateIpAddress(), nic.getIp4Address(), nic.getMacAddress(), profile.getVirtualMachine().getHostName())); + return true; + } else { + s_logger.debug(errMsg + " " + ans.getDetails()); + throw new ResourceUnavailableException(errMsg, DataCenter.class, zoneId); + } + } catch (Exception e) { + s_logger.debug(errMsg, e); + throw new ResourceUnavailableException(errMsg + e.getMessage(), DataCenter.class, zoneId); + } + } +} diff --git a/server/src/com/cloud/baremetal/ExternalDhcpResourceBase.java b/server/src/com/cloud/baremetal/ExternalDhcpResourceBase.java new file mode 100644 index 00000000000..0eb2bbc4c4e --- /dev/null +++ b/server/src/com/cloud/baremetal/ExternalDhcpResourceBase.java @@ -0,0 +1,171 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import java.util.HashMap; +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupExternalDhcpCommand; +import com.cloud.agent.api.StartupPxeServerCommand; +import com.cloud.host.Host.Type; +import com.cloud.resource.ServerResource; +import com.cloud.utils.script.Script; +import com.cloud.utils.ssh.SSHCmdHelper; +import com.cloud.vm.VirtualMachine.State; +import com.trilead.ssh2.SCPClient; + +public class ExternalDhcpResourceBase implements ServerResource { + private static final Logger s_logger = Logger.getLogger(ExternalDhcpResourceBase.class); + String _name; + String _guid; + String _username; + String _password; + String _ip; + String _zoneId; + String _podId; + String _gateway; + String _dns; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + _guid = (String)params.get("guid"); + _ip = (String)params.get("ip"); + _username = (String)params.get("username"); + _password = (String)params.get("password"); + _zoneId = (String)params.get("zone"); + _podId = (String)params.get("pod"); + _gateway = (String)params.get("gateway"); + _dns = (String)params.get("dns"); + + if (_guid == null) { + throw new ConfigurationException("No Guid specified"); + } + + if (_zoneId == null) { + throw new ConfigurationException("No Zone specified"); + } + + if (_podId == null) { + throw new ConfigurationException("No Pod specified"); + } + + if (_ip == null) { + throw new ConfigurationException("No IP specified"); + } + + if (_username == null) { + throw new ConfigurationException("No username specified"); + } + + if (_password == null) { + throw new ConfigurationException("No password specified"); + } + + if (_gateway == null) { + throw new ConfigurationException("No gateway specified"); + } + + if (_dns == null) { + throw new ConfigurationException("No dns specified"); + } + + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public Type getType() { + return Type.ExternalDhcp; + } + + @Override + public StartupCommand[] initialize() { + StartupExternalDhcpCommand cmd = new StartupExternalDhcpCommand(); + cmd.setName(_name); + cmd.setDataCenter(_zoneId); + cmd.setPod(_podId); + cmd.setPrivateIpAddress(_ip); + cmd.setStorageIpAddress(""); + cmd.setVersion(""); + cmd.setGuid(_guid); + return new StartupCommand[]{cmd}; + } + + @Override + public PingCommand getCurrentStatus(long id) { + //TODO: check server + return new PingRoutingCommand(getType(), id, new HashMap()); + } + + protected ReadyAnswer execute(ReadyCommand cmd) { + s_logger.debug("External DHCP resource " + _name + " is ready"); + return new ReadyAnswer(cmd); + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof ReadyCommand) { + return execute((ReadyCommand) cmd); + } else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } + + @Override + public void disconnected() { + } + + @Override + public IAgentControl getAgentControl() { + return null; + } + + @Override + public void setAgentControl(IAgentControl agentControl) { + } + +} diff --git a/server/src/com/cloud/baremetal/HttpCallException.java b/server/src/com/cloud/baremetal/HttpCallException.java new file mode 100644 index 00000000000..c0985ce0868 --- /dev/null +++ b/server/src/com/cloud/baremetal/HttpCallException.java @@ -0,0 +1,29 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import com.cloud.utils.SerialVersionUID; + +public class HttpCallException extends Exception { + private static final long serialVersionUID= SerialVersionUID.HttpCallException; + public HttpCallException(String msg) { + super(msg); + } +} diff --git a/server/src/com/cloud/baremetal/PingPxeServerResource.java b/server/src/com/cloud/baremetal/PingPxeServerResource.java new file mode 100644 index 00000000000..406a4e222f5 --- /dev/null +++ b/server/src/com/cloud/baremetal/PingPxeServerResource.java @@ -0,0 +1,199 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import java.util.HashMap; +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.baremetal.PreparePxeServerAnswer; +import com.cloud.agent.api.baremetal.PreparePxeServerCommand; +import com.cloud.agent.api.baremetal.prepareCreateTemplateCommand; +import com.cloud.utils.script.Script; +import com.cloud.utils.ssh.SSHCmdHelper; +import com.cloud.vm.VirtualMachine.State; +import com.trilead.ssh2.SCPClient; + +public class PingPxeServerResource extends PxeServerResourceBase { + private static final Logger s_logger = Logger.getLogger(PingPxeServerResource.class); + String _storageServer; + String _pingDir; + String _share; + String _dir; + String _tftpDir; + String _cifsUserName; + String _cifsPassword; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + super.configure(name, params); + + _storageServer = (String)params.get("storageServer"); + _pingDir = (String)params.get("pingDir"); + _tftpDir = (String)params.get("tftpDir"); + _cifsUserName = (String)params.get("cifsUserName"); + _cifsPassword = (String)params.get("cifsPassword"); + + if (_storageServer == null) { + throw new ConfigurationException("No stroage server specified"); + } + + if (_tftpDir == null) { + throw new ConfigurationException("No tftp directory specified"); + } + + if (_pingDir == null) { + throw new ConfigurationException("No PING directory specified"); + } + + if (_cifsUserName == null || _cifsUserName.equalsIgnoreCase("")) { + _cifsUserName = "xxx"; + } + + if (_cifsPassword == null || _cifsPassword.equalsIgnoreCase("")) { + _cifsPassword = "xxx"; + } + + String pingDirs[]= _pingDir.split("/"); + if (pingDirs.length != 2) { + throw new ConfigurationException("PING dir should have format like myshare/direcotry, eg: windows/64bit"); + } + _share = pingDirs[0]; + _dir = pingDirs[1]; + + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); + + s_logger.debug(String.format("Trying to connect to PING PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, _password)); + try { + sshConnection.connect(null, 60000, 60000); + if (!sshConnection.authenticateWithPassword(_username, _password)) { + s_logger.debug("SSH Failed to authenticate"); + throw new ConfigurationException(String.format("Cannot connect to PING PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, + _password)); + } + + String cmd = String.format("[ -f /%1$s/pxelinux.0 ] && [ -f /%2$s/kernel ] && [ -f /%3$s/initrd.gz ] ", _tftpDir, _tftpDir, _tftpDir); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, cmd)) { + throw new ConfigurationException("Miss files in TFTP directory at " + _tftpDir + " check if pxelinux.0, kernel initrd.gz are here"); + } + + SCPClient scp = new SCPClient(sshConnection); + String prepareScript = "scripts/network/ping/prepare_tftp_bootfile.py"; + String prepareScriptPath = Script.findScript("", prepareScript); + if (prepareScriptPath == null) { + throw new ConfigurationException("Can not find prepare_tftp_bootfile.py at " + prepareScriptPath); + } + scp.put(prepareScriptPath, "/usr/bin/", "0755"); + + return true; + } catch (Exception e) { + throw new ConfigurationException(e.getMessage()); + } finally { + if (sshConnection != null) { + sshConnection.close(); + } + } + } + + @Override + public PingCommand getCurrentStatus(long id) { + com.trilead.ssh2.Connection sshConnection = SSHCmdHelper.acquireAuthorizedConnection(_ip, _username, _password); + if (sshConnection == null) { + return null; + } else { + SSHCmdHelper.releaseSshConnection(sshConnection); + return new PingRoutingCommand(getType(), id, new HashMap()); + } + } + + protected PreparePxeServerAnswer execute(PreparePxeServerCommand cmd) { + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); + try { + sshConnection.connect(null, 60000, 60000); + if (!sshConnection.authenticateWithPassword(_username, _password)) { + s_logger.debug("SSH Failed to authenticate"); + throw new ConfigurationException(String.format("Cannot connect to PING PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, + _password)); + } + + String script = String.format("python /usr/bin/prepare_tftp_bootfile.py restore %1$s %2$s %3$s %4$s %5$s %6$s %7$s %8$s %9$s %10$s %11$s", + _tftpDir, cmd.getMac(), _storageServer, _share, _dir, cmd.getTemplate(), _cifsUserName, _cifsPassword, cmd.getIp(), cmd.getNetMask(), cmd.getGateWay()); + s_logger.debug("Prepare Ping PXE server successfully"); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { + return new PreparePxeServerAnswer(cmd, "prepare PING at " + _ip + " failed, command:" + script); + } + + return new PreparePxeServerAnswer(cmd); + } catch (Exception e){ + s_logger.debug("Prepare PING pxe server failed", e); + return new PreparePxeServerAnswer(cmd, e.getMessage()); + } finally { + if (sshConnection != null) { + sshConnection.close(); + } + } + } + + protected Answer execute(prepareCreateTemplateCommand cmd) { + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); + try { + sshConnection.connect(null, 60000, 60000); + if (!sshConnection.authenticateWithPassword(_username, _password)) { + s_logger.debug("SSH Failed to authenticate"); + throw new ConfigurationException(String.format("Cannot connect to PING PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, + _password)); + } + + String script = String.format("python /usr/bin/prepare_tftp_bootfile.py backup %1$s %2$s %3$s %4$s %5$s %6$s %7$s %8$s %9$s %10$s %11$s", + _tftpDir, cmd.getMac(), _storageServer, _share, _dir, cmd.getTemplate(), _cifsUserName, _cifsPassword, cmd.getIp(), cmd.getNetMask(), cmd.getGateWay()); + s_logger.debug("Prepare for creating template successfully"); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { + return new Answer(cmd, false, "prepare for creating template failed, command:" + script); + } + + return new Answer(cmd, true, "Success"); + } catch (Exception e){ + s_logger.debug("Prepare for creating baremetal template failed", e); + return new Answer(cmd, false, e.getMessage()); + } finally { + if (sshConnection != null) { + sshConnection.close(); + } + } + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof PreparePxeServerCommand) { + return execute((PreparePxeServerCommand) cmd); + } else if (cmd instanceof prepareCreateTemplateCommand) { + return execute((prepareCreateTemplateCommand)cmd); + } else { + return super.executeRequest(cmd); + } + } +} diff --git a/server/src/com/cloud/baremetal/PxeServerManager.java b/server/src/com/cloud/baremetal/PxeServerManager.java new file mode 100644 index 00000000000..2cd5b2a5dac --- /dev/null +++ b/server/src/com/cloud/baremetal/PxeServerManager.java @@ -0,0 +1,57 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import com.cloud.deploy.DeployDestination; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Manager; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachineProfile; + +public interface PxeServerManager extends Manager { + public static class PxeServerType { + private String _name; + + public static final PxeServerType PING = new PxeServerType("PING"); + public static final PxeServerType DMCD = new PxeServerType("DMCD"); + + public PxeServerType(String name) { + _name = name; + } + + public String getName() { + return _name; + } + + } + + public PxeServerResponse getApiResponse(Host pxeServer); + + public boolean prepare(PxeServerType type, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context, Long pxeServerId); + + Host addPxeServer(PxeServerProfile profile); + + public boolean prepareCreateTemplate(PxeServerType type, Long pxeServerId, UserVm vm, String templateUrl); + + public PxeServerType getPxeServerType(HostVO host); +} diff --git a/server/src/com/cloud/baremetal/PxeServerManagerImpl.java b/server/src/com/cloud/baremetal/PxeServerManagerImpl.java new file mode 100644 index 00000000000..397fda13344 --- /dev/null +++ b/server/src/com/cloud/baremetal/PxeServerManagerImpl.java @@ -0,0 +1,118 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + + +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; +import com.cloud.agent.AgentManager; +import com.cloud.baremetal.PxeServerManager.PxeServerType; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Adapters; +import com.cloud.utils.component.Inject; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.VirtualMachineProfile.Param; + +@Local(value = {PxeServerManager.class}) +public class PxeServerManagerImpl implements PxeServerManager { + private static final org.apache.log4j.Logger s_logger = Logger.getLogger(PxeServerManagerImpl.class); + protected String _name; + @Inject DataCenterDao _dcDao; + @Inject HostDao _hostDao; + @Inject AgentManager _agentMgr; + @Inject ExternalDhcpManager exDhcpMgr; + + @Inject(adapter=PxeServerService.class) + protected Adapters _services; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + protected PxeServerService getServiceByType(String type) { + PxeServerService _service; + _service = _services.get(type); + if (_service == null) { + throw new CloudRuntimeException("Cannot find PXE service for " + type); + } + return _service; + } + + + @Override + public Host addPxeServer(PxeServerProfile profile) { + return getServiceByType(profile.getType()).addPxeServer(profile); + } + + @Override + public PxeServerResponse getApiResponse(Host pxeServer) { + PxeServerResponse response = new PxeServerResponse(); + response.setId(pxeServer.getId()); + return response; + } + + @Override + public boolean prepare(PxeServerType type, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context, Long pxeServerId) { + return getServiceByType(type.getName()).prepare(profile, dest, context, pxeServerId); + } + + @Override + public boolean prepareCreateTemplate(PxeServerType type, Long pxeServerId, UserVm vm, String templateUrl) { + return getServiceByType(type.getName()).prepareCreateTemplate(pxeServerId, vm, templateUrl); + } + + @Override + public PxeServerType getPxeServerType(HostVO host) { + if (host.getResource().equalsIgnoreCase(PingPxeServerResource.class.getName())) { + return PxeServerType.PING; + } else { + throw new CloudRuntimeException("Unkown PXE server resource " + host.getResource()); + } + } +} diff --git a/server/src/com/cloud/baremetal/PxeServerProfile.java b/server/src/com/cloud/baremetal/PxeServerProfile.java new file mode 100644 index 00000000000..aee6f098fe8 --- /dev/null +++ b/server/src/com/cloud/baremetal/PxeServerProfile.java @@ -0,0 +1,74 @@ +package com.cloud.baremetal; + +public class PxeServerProfile { + Long zoneId; + Long podId; + String url; + String username; + String password; + String type; + String pingStorageServerIp; + String pingDir; + String tftpDir; + String pingCifsUserName; + String pingCifspassword; + + public PxeServerProfile (Long zoneId, Long podId, String url, String username, String password, String type, + String pingStorageServerIp, String pingDir, String tftpDir, String pingCifsUserName, String pingCifsPassword) { + this.zoneId = zoneId; + this.podId = podId; + this.url = url; + this.username = username; + this.password = password; + this.type = type; + this.pingStorageServerIp = pingStorageServerIp; + this.pingDir = pingDir; + this.tftpDir = tftpDir; + this.pingCifsUserName = pingCifsUserName; + this.pingCifspassword = pingCifsPassword; + } + + public Long getZoneId() { + return zoneId; + } + + public Long getPodId() { + return podId; + } + + public String getUrl() { + return url; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getType() { + return type; + } + + public String getPingStorageServerIp() { + return pingStorageServerIp; + } + + public String getPingDir() { + return pingDir; + } + + public String getTftpDir() { + return tftpDir; + } + + public String getPingCifsUserName() { + return pingCifsUserName; + } + + public String getPingCifspassword() { + return pingCifspassword; + } +} diff --git a/server/src/com/cloud/baremetal/PxeServerResourceBase.java b/server/src/com/cloud/baremetal/PxeServerResourceBase.java new file mode 100644 index 00000000000..5b3d27e0ad6 --- /dev/null +++ b/server/src/com/cloud/baremetal/PxeServerResourceBase.java @@ -0,0 +1,158 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupPxeServerCommand; +import com.cloud.host.Host.Type; +import com.cloud.resource.ServerResource; + +public class PxeServerResourceBase implements ServerResource { + private static final Logger s_logger = Logger.getLogger(PxeServerResourceBase.class); + String _name; + String _guid; + String _username; + String _password; + String _ip; + String _zoneId; + String _podId; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + _guid = (String)params.get("guid"); + _ip = (String)params.get("ip"); + _username = (String)params.get("username"); + _password = (String)params.get("password"); + _zoneId = (String)params.get("zone"); + _podId = (String)params.get("pod"); + + if (_guid == null) { + throw new ConfigurationException("No Guid specified"); + } + + if (_zoneId == null) { + throw new ConfigurationException("No Zone specified"); + } + + if (_podId == null) { + throw new ConfigurationException("No Pod specified"); + } + + if (_ip == null) { + throw new ConfigurationException("No IP specified"); + } + + if (_username == null) { + throw new ConfigurationException("No username specified"); + } + + if (_password == null) { + throw new ConfigurationException("No password specified"); + } + + return true; + } + + protected ReadyAnswer execute(ReadyCommand cmd) { + s_logger.debug("Pxe resource " + _name + " is ready"); + return new ReadyAnswer(cmd); + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + // TODO Auto-generated method stub + return _name; + } + + @Override + public Type getType() { + return Type.PxeServer; + } + + @Override + public StartupCommand[] initialize() { + StartupPxeServerCommand cmd = new StartupPxeServerCommand(); + cmd.setName(_name); + cmd.setDataCenter(_zoneId); + cmd.setPod(_podId); + cmd.setPrivateIpAddress(_ip); + cmd.setStorageIpAddress(""); + cmd.setVersion(""); + cmd.setGuid(_guid); + return new StartupCommand[]{cmd}; + } + + @Override + public PingCommand getCurrentStatus(long id) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void disconnected() { + // TODO Auto-generated method stub + + } + + @Override + public IAgentControl getAgentControl() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setAgentControl(IAgentControl agentControl) { + // TODO Auto-generated method stub + + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof ReadyCommand) { + return execute((ReadyCommand) cmd); + } else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } + +} diff --git a/server/src/com/cloud/baremetal/PxeServerResponse.java b/server/src/com/cloud/baremetal/PxeServerResponse.java new file mode 100644 index 00000000000..045aa7395d5 --- /dev/null +++ b/server/src/com/cloud/baremetal/PxeServerResponse.java @@ -0,0 +1,38 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class PxeServerResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) @Param(description="the ID of the PXE server") + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/server/src/com/cloud/baremetal/PxeServerService.java b/server/src/com/cloud/baremetal/PxeServerService.java new file mode 100644 index 00000000000..aab41169bd3 --- /dev/null +++ b/server/src/com/cloud/baremetal/PxeServerService.java @@ -0,0 +1,38 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.baremetal; + +import com.cloud.baremetal.PxeServerManager.PxeServerType; +import com.cloud.deploy.DeployDestination; +import com.cloud.host.Host; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Adapter; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachineProfile; + +public interface PxeServerService extends Adapter { + + public Host addPxeServer(PxeServerProfile profile); + + public boolean prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationContext context, Long pxeServerId); + + public boolean prepareCreateTemplate(Long pxeServerId, UserVm vm, String templateUrl); +} diff --git a/server/src/com/cloud/configuration/CloudZonesComponentLibrary.java b/server/src/com/cloud/configuration/CloudZonesComponentLibrary.java new file mode 100644 index 00000000000..98de851272e --- /dev/null +++ b/server/src/com/cloud/configuration/CloudZonesComponentLibrary.java @@ -0,0 +1,54 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later +version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.configuration; + +import com.cloud.agent.StartupCommandProcessor; +import com.cloud.agent.manager.authn.impl.BasicAgentAuthManager; + +import com.cloud.hypervisor.CloudZonesStartupProcessor; +import com.cloud.network.element.CloudZonesNetworkElement; +import com.cloud.network.element.NetworkElement; + + + +public class CloudZonesComponentLibrary extends PremiumComponentLibrary { + + @Override + protected void populateAdapters() { + super.populateAdapters(); + addAdapter(NetworkElement.class, "CloudZones", CloudZonesNetworkElement.class); + addAdapter(StartupCommandProcessor.class, "BasicAgentAuthorizer", BasicAgentAuthManager.class); + addAdapter(StartupCommandProcessor.class, "CloudZonesStartupProcessor", CloudZonesStartupProcessor.class); + } +} diff --git a/server/src/com/cloud/configuration/PremiumComponentLibrary.java b/server/src/com/cloud/configuration/PremiumComponentLibrary.java new file mode 100755 index 00000000000..4462fa174b8 --- /dev/null +++ b/server/src/com/cloud/configuration/PremiumComponentLibrary.java @@ -0,0 +1,84 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.configuration; + +import java.util.ArrayList; +import java.util.List; + +import com.cloud.baremetal.BareMetalPingServiceImpl; +import com.cloud.baremetal.BareMetalTemplateAdapter; +import com.cloud.baremetal.BareMetalVmManagerImpl; +import com.cloud.baremetal.ExternalDhcpManagerImpl; +import com.cloud.baremetal.PxeServerManager.PxeServerType; +import com.cloud.baremetal.PxeServerManagerImpl; +import com.cloud.baremetal.PxeServerService; +import com.cloud.ha.HighAvailabilityManagerExtImpl; +import com.cloud.hypervisor.vmware.VmwareManagerImpl; +import com.cloud.netapp.NetappManagerImpl; +import com.cloud.netapp.dao.LunDaoImpl; +import com.cloud.netapp.dao.PoolDaoImpl; +import com.cloud.netapp.dao.VolumeDaoImpl; +import com.cloud.network.ExternalNetworkManagerImpl; +import com.cloud.network.NetworkDeviceManagerImpl; +import com.cloud.network.NetworkUsageManagerImpl; +import com.cloud.secstorage.CommandExecLogDaoImpl; +import com.cloud.secstorage.PremiumSecondaryStorageManagerImpl; +import com.cloud.template.TemplateAdapter; +import com.cloud.template.TemplateAdapter.TemplateAdapterType; +import com.cloud.upgrade.PremiumDatabaseUpgradeChecker; +import com.cloud.usage.dao.UsageDaoImpl; +import com.cloud.usage.dao.UsageIPAddressDaoImpl; +import com.cloud.usage.dao.UsageJobDaoImpl; +import com.cloud.utils.component.SystemIntegrityChecker; + +public class PremiumComponentLibrary extends DefaultComponentLibrary { + @Override + protected void populateDaos() { + addDao("UsageJobDao", UsageJobDaoImpl.class); + addDao("UsageDao", UsageDaoImpl.class); + addDao("UsageIpAddressDao", UsageIPAddressDaoImpl.class); + addDao("CommandExecLogDao", CommandExecLogDaoImpl.class); + addDao("NetappPool", PoolDaoImpl.class); + addDao("NetappVolume", VolumeDaoImpl.class); + addDao("NetappLun", LunDaoImpl.class); + } + + @Override + protected void populateManagers() { + // override FOSS SSVM manager + addManager("secondary storage vm manager", PremiumSecondaryStorageManagerImpl.class); + + addManager("HA Manager", HighAvailabilityManagerExtImpl.class); + addManager("VMWareManager", VmwareManagerImpl.class); + addManager("ExternalNetworkManager", ExternalNetworkManagerImpl.class); + addManager("BareMetalVmManager", BareMetalVmManagerImpl.class); + addManager("ExternalDhcpManager", ExternalDhcpManagerImpl.class); + addManager("PxeServerManager", PxeServerManagerImpl.class); + addManager("NetworkDeviceManager", NetworkDeviceManagerImpl.class); + addManager("NetworkUsageManager", NetworkUsageManagerImpl.class); + addManager("NetappManager", NetappManagerImpl.class); + } + + @Override + protected void populateAdapters() { + super.populateAdapters(); + addAdapter(PxeServerService.class, PxeServerType.PING.getName(), BareMetalPingServiceImpl.class); + addAdapter(TemplateAdapter.class, TemplateAdapterType.BareMetal.getName(), BareMetalTemplateAdapter.class); + } +} diff --git a/server/src/com/cloud/ha/HighAvailabilityManagerExtImpl.java b/server/src/com/cloud/ha/HighAvailabilityManagerExtImpl.java new file mode 100644 index 00000000000..fd43b69ad52 --- /dev/null +++ b/server/src/com/cloud/ha/HighAvailabilityManagerExtImpl.java @@ -0,0 +1,118 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.ha; + +import java.util.Date; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import com.cloud.alert.AlertManager; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.server.ManagementServer; +import com.cloud.usage.dao.UsageJobDao; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.Transaction; + +@Local(value={HighAvailabilityManager.class}) +public class HighAvailabilityManagerExtImpl extends HighAvailabilityManagerImpl { + + @Inject + UsageJobDao _usageJobDao; + ConfigurationDao configDao; + + @Override + public boolean configure(final String name, final Map xmlParams) throws ConfigurationException { + super.configure(name, xmlParams); + + ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); + + configDao = locator.getDao(ConfigurationDao.class); + if (configDao == null) + { + s_logger.warn("Unable to get a configuration dao to check config value for enableUsageServer"); + return false; + } + + return true; + } + + @Override + public boolean start() + { + super.start(); + + + boolean enableUsage = new Boolean(configDao.getValue("enable.usage.server")); + + //By default, usage is enabled for production + //Devs might override this value to disable usage in their setup + if(enableUsage) + { + _executor.scheduleAtFixedRate(new UsageServerMonitorTask(), 60*60, 10*60, TimeUnit.SECONDS); // schedule starting in one hour to execute every 10 minutes + } + + return true; + } + + protected class UsageServerMonitorTask implements Runnable { + @Override + public void run() { + if (s_logger.isInfoEnabled()) { + s_logger.info("checking health of usage server"); + } + + try { + boolean isRunning = false; + Transaction txn = Transaction.open(Transaction.USAGE_DB); + try { + Date lastHeartbeat = _usageJobDao.getLastHeartbeat(); + if (lastHeartbeat != null) { + long sinceLastHeartbeat = System.currentTimeMillis() - lastHeartbeat.getTime(); + if (sinceLastHeartbeat <= (10 * 60 * 1000)) { + // if it's been less than 10 minutes since the last heartbeat, then it appears to be running, otherwise send an alert + isRunning = true; + } + } + if (s_logger.isDebugEnabled()) { + s_logger.debug("usage server running? " + isRunning + ", heartbeat: " + lastHeartbeat); + } + } finally { + txn.close(); + + // switch back to VMOPS db + Transaction swap = Transaction.open(Transaction.CLOUD_DB); + swap.close(); + } + + if (!isRunning) { + _alertMgr.sendAlert(AlertManager.ALERT_TYPE_USAGE_SERVER, 0, new Long(0), "No usage server process running", "No usage server process has been detected, some attention is required"); + } else { + _alertMgr.clearAlert(AlertManager.ALERT_TYPE_USAGE_SERVER, 0, 0); + } + } catch (Exception ex) { + s_logger.warn("Error while monitoring usage job", ex); + } + } + } +} diff --git a/server/src/com/cloud/hypervisor/CloudZonesStartupProcessor.java b/server/src/com/cloud/hypervisor/CloudZonesStartupProcessor.java new file mode 100644 index 00000000000..67458afca8f --- /dev/null +++ b/server/src/com/cloud/hypervisor/CloudZonesStartupProcessor.java @@ -0,0 +1,525 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later +version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.hypervisor; + +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.StartupCommandProcessor; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.agent.api.StartupStorageCommand; +import com.cloud.agent.manager.authn.AgentAuthnException; +import com.cloud.configuration.ConfigurationManager; +import com.cloud.configuration.ZoneConfig; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DcDetailVO; +import com.cloud.dc.HostPodVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.DcDetailsDao; +import com.cloud.dc.dao.HostPodDao; +import com.cloud.exception.ConnectionException; +import com.cloud.host.Host; +import com.cloud.host.Host.Type; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.storage.Storage; +import com.cloud.storage.StorageStats; +import com.cloud.storage.resource.DummySecondaryStorageResource; +import com.cloud.utils.component.Inject; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.MacAddress; +import com.cloud.utils.net.NetUtils; + +/** + * Creates a host record and supporting records such as pod and ip address + * + */ +@Local(value=StartupCommandProcessor.class) +public class CloudZonesStartupProcessor implements StartupCommandProcessor { + private static final Logger s_logger = Logger.getLogger(CloudZonesStartupProcessor.class); + @Inject ClusterDao _clusterDao = null; + @Inject ConfigurationDao _configDao = null; + @Inject DataCenterDao _zoneDao = null; + @Inject HostDao _hostDao = null; + @Inject HostPodDao _podDao = null; + @Inject DcDetailsDao _zoneDetailsDao = null; + + @Inject AgentManager _agentManager = null; + @Inject ConfigurationManager _configurationManager = null; + + long _nodeId = -1; + + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + _agentManager.registerForInitialConnects(this, false); + if (_nodeId == -1) { + // FIXME: We really should not do this like this. It should be done + // at config time and is stored as a config variable. + _nodeId = MacAddress.getMacAddress().toLong(); + } + return true; + } + + + @Override + public String getName() { + return getClass().getName(); + } + + + @Override + public boolean start() { + return true; + } + + + @Override + public boolean stop() { + return true; + } + + @Override + public boolean processInitialConnect(StartupCommand[] cmd) + throws ConnectionException { + StartupCommand startup = cmd[0]; + if (startup instanceof StartupRoutingCommand) { + return processHostStartup((StartupRoutingCommand)startup); + } else if (startup instanceof StartupStorageCommand ){ + return processStorageStartup((StartupStorageCommand)startup); + } + + return false; + } + + protected boolean processHostStartup(StartupRoutingCommand startup) throws ConnectionException{ + boolean found = false; + Type type = Host.Type.Routing; + final Map hostDetails = startup.getHostDetails(); + HostVO server = _hostDao.findByGuid(startup.getGuid()); + if (server == null) { + server = _hostDao.findByGuid(startup.getGuidWithoutResource()); + } + if (server != null && server.getRemoved() == null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Found the host " + server.getId() + " by guid: " + + startup.getGuid()); + } + found = true; + + } else { + server = new HostVO(startup.getGuid()); + } + server.setDetails(hostDetails); + + try { + updateComputeHost(server, startup, type); + } catch (AgentAuthnException e) { + throw new ConnectionException(true, "Failed to authorize host, invalid configuration", e); + } + if (!found) { + server.setHostAllocationState(Host.HostAllocationState.Enabled); + server = _hostDao.persist(server); + } else { + if (!_hostDao.connect(server, _nodeId)) { + throw new CloudRuntimeException( + "Agent cannot connect because the current state is " + + server.getStatus().toString()); + } + s_logger.info("Old " + server.getType().toString() + + " host reconnected w/ id =" + server.getId()); + } + return true; + + + } + + protected void updateComputeHost(final HostVO host, final StartupCommand startup, + final Host.Type type) throws AgentAuthnException { + + String zoneToken = startup.getDataCenter(); + if(zoneToken == null){ + s_logger.warn("No Zone Token passed in, cannot not find zone for the agent"); + throw new AgentAuthnException("No Zone Token passed in, cannot not find zone for agent"); + } + + DataCenterVO zone = _zoneDao.findByToken(zoneToken); + if (zone == null) { + zone = _zoneDao.findByName(zoneToken); + if(zone == null){ + try { + long zoneId = Long.parseLong(zoneToken); + zone = _zoneDao.findById(zoneId); + if (zone == null) { + throw new AgentAuthnException("Could not find zone for agent with token " + zoneToken); + } + } catch (NumberFormatException nfe) { + throw new AgentAuthnException("Could not find zone for agent with token " + zoneToken); + } + } + } + if(s_logger.isDebugEnabled()){ + s_logger.debug("Successfully loaded the DataCenter from the zone token passed in "); + } + + long zoneId = zone.getId(); + DcDetailVO maxHostsInZone = _zoneDetailsDao.findDetail(zoneId, ZoneConfig.MaxHosts.key()); + if(maxHostsInZone != null){ + long maxHosts = new Long(maxHostsInZone.getValue()).longValue(); + long currentCountOfHosts = _hostDao.countRoutingHostsByDataCenter(zoneId); + if(s_logger.isDebugEnabled()){ + s_logger.debug("Number of hosts in Zone:"+ currentCountOfHosts + ", max hosts limit: "+ maxHosts); + } + if(currentCountOfHosts >= maxHosts){ + throw new AgentAuthnException("Number of running Routing hosts in the Zone:"+ zone.getName() +" is already at the max limit:"+maxHosts+", cannot start one more host"); + } + } + + HostPodVO pod = null; + + if(startup.getPrivateIpAddress() == null){ + s_logger.warn("No private IP address passed in for the agent, cannot not find pod for agent"); + throw new AgentAuthnException("No private IP address passed in for the agent, cannot not find pod for agent"); + } + + if(startup.getPrivateNetmask() == null){ + s_logger.warn("No netmask passed in for the agent, cannot not find pod for agent"); + throw new AgentAuthnException("No netmask passed in for the agent, cannot not find pod for agent"); + } + + if(host.getPodId() != null){ + if(s_logger.isDebugEnabled()){ + s_logger.debug("Pod is already created for this agent, looks like agent is reconnecting..."); + } + pod = _podDao.findById(host.getPodId()); + if(!checkCIDR(type, pod, startup.getPrivateIpAddress(), + startup.getPrivateNetmask())){ + pod = null; + if(s_logger.isDebugEnabled()){ + s_logger.debug("Subnet of Pod does not match the subnet of the agent, not using this Pod: "+host.getPodId()); + } + }else{ + updatePodNetmaskIfNeeded(pod, startup.getPrivateNetmask()); + } + } + + if(pod == null){ + if(s_logger.isDebugEnabled()){ + s_logger.debug("Trying to detect the Pod to use from the agent's ip address and netmask passed in "); + } + + //deduce pod + boolean podFound = false; + List podsInZone = _podDao.listByDataCenterId(zoneId); + for (HostPodVO hostPod : podsInZone) { + if (checkCIDR(type, hostPod, startup.getPrivateIpAddress(), + startup.getPrivateNetmask())) { + pod = hostPod; + + //found the default POD having the same subnet. + updatePodNetmaskIfNeeded(pod, startup.getPrivateNetmask()); + podFound = true; + break; + } + } + + + if (!podFound) { + if(s_logger.isDebugEnabled()){ + s_logger.debug("Creating a new Pod since no default Pod found that matches the agent's ip address and netmask passed in "); + } + + if(startup.getGatewayIpAddress() == null){ + s_logger.warn("No Gateway IP address passed in for the agent, cannot create a new pod for the agent"); + throw new AgentAuthnException("No Gateway IP address passed in for the agent, cannot create a new pod for the agent"); + } + //auto-create a new pod, since pod matching the agent's ip is not found + String podName = "POD-"+ (podsInZone.size()+1); + try{ + String gateway = startup.getGatewayIpAddress(); + String cidr = NetUtils.getCidrFromGatewayAndNetmask(gateway, startup.getPrivateNetmask()); + String[] cidrPair = cidr.split("\\/"); + String cidrAddress = cidrPair[0]; + long cidrSize = Long.parseLong(cidrPair[1]); + String startIp = NetUtils.getIpRangeStartIpFromCidr(cidrAddress, cidrSize); + String endIp = NetUtils.getIpRangeEndIpFromCidr(cidrAddress, cidrSize); + pod = _configurationManager.createPod(-1, podName, zoneId, gateway, cidr, startIp, endIp, null, true); + }catch (Exception e) { + // no longer tolerate exception during the cluster creation phase + throw new CloudRuntimeException("Unable to create new Pod " + + podName + " in Zone: " + + zoneId, e); + } + + } + } + final StartupRoutingCommand scc = (StartupRoutingCommand) startup; + + ClusterVO cluster = null; + if(host.getClusterId() != null){ + if(s_logger.isDebugEnabled()){ + s_logger.debug("Cluster is already created for this agent, looks like agent is reconnecting..."); + } + cluster = _clusterDao.findById(host.getClusterId()); + } + if(cluster == null){ + //auto-create cluster - assume one host per cluster + String clusterName = "Cluster-" + startup.getPrivateIpAddress(); + ClusterVO existingCluster = _clusterDao.findBy(clusterName, pod.getId()); + if (existingCluster != null) { + cluster = existingCluster; + } else { + if(s_logger.isDebugEnabled()){ + s_logger.debug("Creating a new Cluster for this agent with name: "+clusterName + " in Pod: " +pod.getId() +", in Zone:"+zoneId); + } + + cluster = new ClusterVO(zoneId, pod.getId(), clusterName); + cluster.setHypervisorType(scc.getHypervisorType().toString()); + try { + cluster = _clusterDao.persist(cluster); + } catch (Exception e) { + // no longer tolerate exception during the cluster creation phase + throw new CloudRuntimeException("Unable to create cluster " + + clusterName + " in pod " + pod.getId() + " and data center " + + zoneId, e); + } + } + } + + if(s_logger.isDebugEnabled()){ + s_logger.debug("Detected Zone: "+zoneId+", Pod: " +pod.getId() +", Cluster:" +cluster.getId()); + } + host.setDataCenterId(zone.getId()); + host.setPodId(pod.getId()); + host.setClusterId(cluster.getId()); + host.setPrivateIpAddress(startup.getPrivateIpAddress()); + host.setPrivateNetmask(startup.getPrivateNetmask()); + host.setPrivateMacAddress(startup.getPrivateMacAddress()); + host.setPublicIpAddress(startup.getPublicIpAddress()); + host.setPublicMacAddress(startup.getPublicMacAddress()); + host.setPublicNetmask(startup.getPublicNetmask()); + host.setStorageIpAddress(startup.getStorageIpAddress()); + host.setStorageMacAddress(startup.getStorageMacAddress()); + host.setStorageNetmask(startup.getStorageNetmask()); + host.setVersion(startup.getVersion()); + host.setName(startup.getName()); + host.setType(type); + host.setStorageUrl(startup.getIqn()); + host.setLastPinged(System.currentTimeMillis() >> 10); + host.setCaps(scc.getCapabilities()); + host.setCpus(scc.getCpus()); + host.setTotalMemory(scc.getMemory()); + host.setSpeed(scc.getSpeed()); + HypervisorType hyType = scc.getHypervisorType(); + host.setHypervisorType(hyType); + + } + + private boolean checkCIDR(Host.Type type, HostPodVO pod, + String serverPrivateIP, String serverPrivateNetmask) { + if (serverPrivateIP == null) { + return true; + } + // Get the CIDR address and CIDR size + String cidrAddress = pod.getCidrAddress(); + long cidrSize = pod.getCidrSize(); + + // If the server's private IP address is not in the same subnet as the + // pod's CIDR, return false + String cidrSubnet = NetUtils.getCidrSubNet(cidrAddress, cidrSize); + String serverSubnet = NetUtils.getSubNet(serverPrivateIP, + serverPrivateNetmask); + if (!cidrSubnet.equals(serverSubnet)) { + return false; + } + return true; + } + + private void updatePodNetmaskIfNeeded(HostPodVO pod, String agentNetmask){ + // If the server's private netmask is less inclusive than the pod's CIDR + // netmask, update cidrSize of the default POD + //(reason: we are maintaining pods only for internal accounting.) + long cidrSize = pod.getCidrSize(); + String cidrNetmask = NetUtils + .getCidrSubNet("255.255.255.255", cidrSize); + long cidrNetmaskNumeric = NetUtils.ip2Long(cidrNetmask); + long serverNetmaskNumeric = NetUtils.ip2Long(agentNetmask);// + if (serverNetmaskNumeric > cidrNetmaskNumeric) { + //update pod's cidrsize + int newCidrSize = new Long(NetUtils.getCidrSize(agentNetmask)).intValue(); + pod.setCidrSize(newCidrSize); + _podDao.update(pod.getId(), pod); + } + } + + + protected boolean processStorageStartup(StartupStorageCommand startup) throws ConnectionException{ + if (startup.getResourceType() != Storage.StorageResourceType.LOCAL_SECONDARY_STORAGE) { + return false; + } + boolean found = false; + Type type = Host.Type.LocalSecondaryStorage; + final Map hostDetails = startup.getHostDetails(); + HostVO server = _hostDao.findByGuid(startup.getGuid()); + if (server == null) { + server = _hostDao.findByGuid(startup.getGuidWithoutResource()); + } + if (server != null && server.getRemoved() == null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Found the host " + server.getId() + " by guid: " + + startup.getGuid()); + } + found = true; + + } else { + server = new HostVO(startup.getGuid()); + } + server.setDetails(hostDetails); + + try { + updateSecondaryHost(server, startup, type); + } catch (AgentAuthnException e) { + throw new ConnectionException(true, "Failed to authorize host, invalid configuration", e); + } + if (!found) { + server.setHostAllocationState(Host.HostAllocationState.Enabled); + server = _hostDao.persist(server); + } else { + if (!_hostDao.connect(server, _nodeId)) { + throw new CloudRuntimeException( + "Agent cannot connect because the current state is " + + server.getStatus().toString()); + } + s_logger.info("Old " + server.getType().toString() + + " host reconnected w/ id =" + server.getId()); + } + return true; + + + } + protected void updateSecondaryHost(final HostVO host, final StartupStorageCommand startup, + final Host.Type type) throws AgentAuthnException { + + String zoneToken = startup.getDataCenter(); + if(zoneToken == null){ + s_logger.warn("No Zone Token passed in, cannot not find zone for the agent"); + throw new AgentAuthnException("No Zone Token passed in, cannot not find zone for agent"); + } + + DataCenterVO zone = _zoneDao.findByToken(zoneToken); + if (zone == null) { + zone = _zoneDao.findByName(zoneToken); + if(zone == null){ + try { + long zoneId = Long.parseLong(zoneToken); + zone = _zoneDao.findById(zoneId); + if (zone == null) { + throw new AgentAuthnException("Could not find zone for agent with token " + zoneToken); + } + } catch (NumberFormatException nfe) { + throw new AgentAuthnException("Could not find zone for agent with token " + zoneToken); + } + } + } + if(s_logger.isDebugEnabled()){ + s_logger.debug("Successfully loaded the DataCenter from the zone token passed in "); + } + + HostPodVO pod = findPod(startup, zone.getId(), Host.Type.Routing); //yes, routing + Long podId = null; + if (pod != null) { + s_logger.debug("Found pod " + pod.getName() + " for the secondary storage host " + startup.getName()); + podId = pod.getId(); + } + host.setDataCenterId(zone.getId()); + host.setPodId(podId); + host.setClusterId(null); + host.setPrivateIpAddress(startup.getPrivateIpAddress()); + host.setPrivateNetmask(startup.getPrivateNetmask()); + host.setPrivateMacAddress(startup.getPrivateMacAddress()); + host.setPublicIpAddress(startup.getPublicIpAddress()); + host.setPublicMacAddress(startup.getPublicMacAddress()); + host.setPublicNetmask(startup.getPublicNetmask()); + host.setStorageIpAddress(startup.getStorageIpAddress()); + host.setStorageMacAddress(startup.getStorageMacAddress()); + host.setStorageNetmask(startup.getStorageNetmask()); + host.setVersion(startup.getVersion()); + host.setName(startup.getName()); + host.setType(type); + host.setStorageUrl(startup.getIqn()); + host.setLastPinged(System.currentTimeMillis() >> 10); + host.setCaps(null); + host.setCpus(null); + host.setTotalMemory(0); + host.setSpeed(null); + host.setParent(startup.getParent()); + host.setTotalSize(startup.getTotalSize()); + host.setHypervisorType(HypervisorType.None); + if (startup.getNfsShare() != null) { + host.setStorageUrl(startup.getNfsShare()); + } + + } + + private HostPodVO findPod(StartupCommand startup, long zoneId, Host.Type type) { + HostPodVO pod = null; + List podsInZone = _podDao.listByDataCenterId(zoneId); + for (HostPodVO hostPod : podsInZone) { + if (checkCIDR(type, hostPod, startup.getPrivateIpAddress(), + startup.getPrivateNetmask())) { + pod = hostPod; + + //found the default POD having the same subnet. + updatePodNetmaskIfNeeded(pod, startup.getPrivateNetmask()); + + break; + } + } + return pod; + + } + +} diff --git a/server/src/com/cloud/hypervisor/hyperv/HypervServerDiscoverer.java b/server/src/com/cloud/hypervisor/hyperv/HypervServerDiscoverer.java new file mode 100755 index 00000000000..4369deea3f9 --- /dev/null +++ b/server/src/com/cloud/hypervisor/hyperv/HypervServerDiscoverer.java @@ -0,0 +1,244 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.hypervisor.hyperv; + +import java.net.InetAddress; +import java.net.URI; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Command; +import com.cloud.agent.api.StartupVMMAgentCommand; +import com.cloud.agent.transport.Request; +import com.cloud.alert.AlertManager; +import com.cloud.dc.ClusterDetailsDao; +import com.cloud.dc.ClusterVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.exception.DiscoveryException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.hypervisor.hyperv.resource.HypervDummyResourceBase; +import com.cloud.resource.Discoverer; +import com.cloud.resource.DiscovererBase; +import com.cloud.resource.ServerResource; +import com.cloud.utils.component.Inject; +import com.cloud.utils.nio.HandlerFactory; +import com.cloud.utils.nio.Link; +import com.cloud.utils.nio.NioClient; +import com.cloud.utils.nio.Task; +import com.cloud.utils.nio.Task.Type; + +@Local(value=Discoverer.class) +public class HypervServerDiscoverer extends DiscovererBase implements Discoverer, HandlerFactory{ + private static final Logger s_logger = Logger.getLogger(HypervServerDiscoverer.class); + private int _waitTime = 1; + + @Inject ClusterDao _clusterDao; + @Inject AlertManager _alertMgr; + @Inject ClusterDetailsDao _clusterDetailsDao; + @Inject HostDao _hostDao = null; + Link _link; + + @SuppressWarnings("static-access") + @Override + public Map> find(long dcId, Long podId, Long clusterId, URI url, + String username, String password, List hostTags) throws DiscoveryException { + + if(s_logger.isInfoEnabled()) { + s_logger.info("Discover host. dc: " + dcId + ", pod: " + podId + ", cluster: " + clusterId + ", uri host: " + url.getHost()); + } + + if(podId == null) { + if(s_logger.isInfoEnabled()) { + s_logger.info("No pod is assigned, skipping the discovery in Hyperv discoverer"); + } + return null; + } + + if (!url.getScheme().equals("http")) { + String msg = "urlString is not http so HypervServerDiscoverer taking care of the discovery for this: " + url; + s_logger.debug(msg); + return null; + } + + ClusterVO cluster = _clusterDao.findById(clusterId); + if(cluster == null || cluster.getHypervisorType() != HypervisorType.Hyperv) { + if(s_logger.isInfoEnabled()) { + s_logger.info("invalid cluster id or cluster is not for Hyperv hypervisors"); + } + return null; + } + String clusterName = cluster.getName(); + + try { + + String hostname = url.getHost(); + InetAddress ia = InetAddress.getByName(hostname); + String agentIp = ia.getHostAddress(); + String guid = UUID.nameUUIDFromBytes(agentIp.getBytes()).toString(); + String guidWithTail = guid + "-HypervResource";/*tail added by agent.java*/ + if (_hostDao.findByGuid(guidWithTail) != null) { + s_logger.debug("Skipping " + agentIp + " because " + guidWithTail + " is already in the database."); + return null; + } + + // bootstrap SCVMM agent to connect back to management server + NioClient _connection = new NioClient("HypervAgentClient", url.getHost(), 9000, 1, this); + _connection.start(); + + StartupVMMAgentCommand cmd = new StartupVMMAgentCommand( + dcId, + podId, + clusterName, + guid, + InetAddress.getLocalHost().getHostAddress(), + "8250", + HypervServerDiscoverer.class.getPackage().getImplementationVersion()); + + // send bootstrap command to agent running on SCVMM host + s_logger.info("sending bootstrap request to SCVMM agent on host "+ url.getHost()); + Request request = new Request(0, 0, cmd, false); + + // :FIXME without sleep link.send failing why?????? + Thread.currentThread().sleep(5000); + _link.send(request.toBytes()); + + //wait for SCVMM agent to connect back + HostVO connectedHost = waitForHostConnect(dcId, podId, clusterId, guidWithTail); + if (connectedHost == null) + { + s_logger.info("SCVMM agent did not connect back after sending bootstrap request"); + return null; + } + + //disconnect + s_logger.info("SCVMM agent connected back after sending bootstrap request"); + _connection.stop(); + + Map> resources = new HashMap>(); + Map details = new HashMap(); + Map params = new HashMap(); + HypervDummyResourceBase resource = new HypervDummyResourceBase(); + + details.put("url", url.getHost()); + details.put("username", username); + details.put("password", password); + resources.put(resource, details); + + params.put("zone", Long.toString(dcId)); + params.put("pod", Long.toString(podId)); + params.put("cluster", Long.toString(clusterId)); + + resource.configure("Hyperv", params); + return resources; + } catch (ConfigurationException e) { + _alertMgr.sendAlert(AlertManager.ALERT_TYPE_HOST, dcId, podId, "Unable to add " + url.getHost(), "Error is " + e.getMessage()); + s_logger.warn("Unable to instantiate " + url.getHost(), e); + } catch (UnknownHostException e) { + _alertMgr.sendAlert(AlertManager.ALERT_TYPE_HOST, dcId, podId, "Unable to add " + url.getHost(), "Error is " + e.getMessage()); + s_logger.warn("Unable to instantiate " + url.getHost(), e); + } catch (Exception e) { + s_logger.info("exception " + e.toString()); + } + return null; + } + + @Override + public void postDiscovery(List hosts, long msId) { + // do nothing + } + + @Override + public boolean matchHypervisor(String hypervisor) { + if(hypervisor == null) { + return true; + } + + return Hypervisor.HypervisorType.VMware.toString().equalsIgnoreCase(hypervisor); + } + + @Override + public Hypervisor.HypervisorType getHypervisorType() { + return Hypervisor.HypervisorType.Hyperv; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + super.configure(name, params); + return true; + } + + @Override + public Task create(Type type, Link link, byte[] data) { + _link = link; + return new BootStrapTakHandler(type, link, data); + } + + private HostVO waitForHostConnect(long dcId, long podId, long clusterId, String guid) { + for (int i = 0; i < _waitTime *2; i++) { + List hosts = _hostDao.listBy(Host.Type.Routing, clusterId, podId, dcId); + for (HostVO host : hosts) { + if (host.getGuid().equalsIgnoreCase(guid)) { + return host; + } + } + try { + Thread.sleep(30000); + } catch (InterruptedException e) { + s_logger.debug("Failed to sleep: " + e.toString()); + } + } + s_logger.debug("Timeout, to wait for the host connecting to mgt svr, assuming it is failed"); + return null; + } + + // class to handle the bootstrap command from the management server + public class BootStrapTakHandler extends Task { + + public BootStrapTakHandler(Task.Type type, Link link, byte[] data) { + super(type, link, data); + s_logger.info("created new BootStrapTakHandler"); + } + + protected void processRequest(final Link link, final Request request) { + final Command[] cmds = request.getCommands(); + Command cmd = cmds[0]; + } + + @Override + protected void doTask(Task task) throws Exception { + final Type type = task.getType(); + s_logger.info("recieved task of type "+type.toString() +" in BootStrapTakHandler"); + } + } +} + + diff --git a/server/src/com/cloud/netapp/LunVO.java b/server/src/com/cloud/netapp/LunVO.java new file mode 100644 index 00000000000..fa0ab641cd4 --- /dev/null +++ b/server/src/com/cloud/netapp/LunVO.java @@ -0,0 +1,139 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ + +package com.cloud.netapp; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name="netapp_lun") +public class LunVO { + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private Long id; + + @Column(name="lun_name") + private String lunName; + + @Column(name="target_iqn") + private String targetIqn; + + @Column(name="path") + private String path; + + @Column(name="volume_id") + private Long volumeId; + + public Long getSize() { + return size; + } + + public void setSize(Long size) { + this.size = size; + } + + + @Column(name="size") + private Long size; + + public LunVO(){ + + } + + public LunVO(String path, Long volumeId, Long size, String lunName, String targetIqn) { + this.path = path; + this.volumeId = volumeId; + this.size = size; + this.lunName = lunName; + this.targetIqn = targetIqn; + } + + public String getLunName() { + return lunName; + } + + public void setLunName(String lunName) { + this.lunName = lunName; + } + + public LunVO(Long id, String path, Long volumeId, Long size, String lunName, String targetIqn) { + this.id = id; + this.path = path; + this.volumeId = volumeId; + this.size = size; + this.lunName = lunName; + this.targetIqn = targetIqn; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public Long getVolumeId() { + return volumeId; + } + + public void setVolumeId(Long volumeId) { + this.volumeId = volumeId; + } + + public void setTargetIqn(String iqn){ + this.targetIqn = iqn; + } + + public String getTargetIqn(){ + return targetIqn; + } + + +} diff --git a/server/src/com/cloud/netapp/NetappAllocator.java b/server/src/com/cloud/netapp/NetappAllocator.java new file mode 100644 index 00000000000..b2e2753cb89 --- /dev/null +++ b/server/src/com/cloud/netapp/NetappAllocator.java @@ -0,0 +1,43 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ + +package com.cloud.netapp; + + +public interface NetappAllocator{ + + public NetappVolumeVO chooseVolumeFromPool(String poolName, long lunSizeGb) ; + + public NetappVolumeVO chooseLeastFullVolumeFromPool(String poolName,long lunSizeGb); +} diff --git a/server/src/com/cloud/netapp/NetappDefaultAllocatorImpl.java b/server/src/com/cloud/netapp/NetappDefaultAllocatorImpl.java new file mode 100644 index 00000000000..53b298a95d7 --- /dev/null +++ b/server/src/com/cloud/netapp/NetappDefaultAllocatorImpl.java @@ -0,0 +1,160 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ + +package com.cloud.netapp; + +import java.io.IOException; +import java.rmi.ServerException; +import java.util.HashMap; +import java.util.List; + +import org.apache.log4j.Logger; + +import netapp.manage.NaException; + +public class NetappDefaultAllocatorImpl implements NetappAllocator +{ + private static HashMap _poolNameToLastVolumeIdAllocated = new HashMap(); + private NetappManager _netappMgr; + public static final Logger s_logger = Logger.getLogger(NetappDefaultAllocatorImpl.class.getName()); + + public NetappDefaultAllocatorImpl(NetappManager netappMgr) { + _netappMgr = netappMgr; + } + + public synchronized NetappVolumeVO chooseLeastFullVolumeFromPool(String poolName, long lunSizeGb) + { + List volumesOnPoolAscending = _netappMgr.listVolumesAscending(poolName); + + if(volumesOnPoolAscending==null) + { + //no pools exist in db + return null; + } + + long maxAvailable = 0; + NetappVolumeVO selectedVol = null; + for(NetappVolumeVO vol : volumesOnPoolAscending) + { + try { + long availableBytes = _netappMgr.returnAvailableVolumeSize(vol.getVolumeName(), vol.getUsername(), vol.getPassword(), vol.getIpAddress()); + + if(lunSizeGb <= bytesToGb(availableBytes) && availableBytes>maxAvailable) + { + maxAvailable = availableBytes; //new max + selectedVol = vol; //new least loaded vol + } + } catch (ServerException se) { + s_logger.debug("Ignoring failure to obtain volume size for volume " + vol.getVolumeName()); + continue; + } + } + + return selectedVol; + } + + /** + * This method does the actual round robin allocation + * @param poolName + * @param lunSizeGb + * @return -- the selected volume to create the lun on + * @throws IOException + * @throws NaException + */ + public synchronized NetappVolumeVO chooseVolumeFromPool(String poolName, long lunSizeGb) + { + int pos = 0; //0 by default + List volumesOnPoolAscending = _netappMgr.listVolumesAscending(poolName); + + if(volumesOnPoolAscending==null) + { + //no pools exist in db + return null; + } + + //get the index of the record from the map + if(_poolNameToLastVolumeIdAllocated.get(poolName)==null) + { + pos=0; + } + else + { + pos=_poolNameToLastVolumeIdAllocated.get(poolName); + } + + //update for RR effect + _poolNameToLastVolumeIdAllocated.put(poolName, (pos+1)%volumesOnPoolAscending.size()); + + //now iterate over the records + Object[] volumesOnPoolAscendingArray = volumesOnPoolAscending.toArray(); + int counter=0; + while (counter < volumesOnPoolAscendingArray.length) + { + NetappVolumeVO vol = (NetappVolumeVO)volumesOnPoolAscendingArray[pos]; + + //check if the volume fits the bill + long availableBytes; + try { + availableBytes = _netappMgr.returnAvailableVolumeSize(vol.getVolumeName(), vol.getUsername(), vol.getPassword(), vol.getIpAddress()); + + if(lunSizeGb <= bytesToGb(availableBytes)) + { + //found one + return vol; + } + pos = (pos + 1) % volumesOnPoolAscendingArray.length; + counter++; + } catch (ServerException e) { + s_logger.debug("Ignoring failure to obtain volume size for volume " + vol.getVolumeName()); + continue; + } + } + + return null; + } + + + /** + * This method does the byte to gb conversion + * @param bytes + * @return -- converted gb + */ + private long bytesToGb(long bytes){ + long returnVal = (bytes/(1024*1024*1024)); + return returnVal; + } + +} + + \ No newline at end of file diff --git a/server/src/com/cloud/netapp/NetappManager.java b/server/src/com/cloud/netapp/NetappManager.java new file mode 100644 index 00000000000..db0253304dc --- /dev/null +++ b/server/src/com/cloud/netapp/NetappManager.java @@ -0,0 +1,84 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ + +package com.cloud.netapp; + +import java.net.UnknownHostException; +import java.rmi.ServerException; +import java.util.List; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceInUseException; +import com.cloud.utils.component.Manager; + +public interface NetappManager extends Manager { + enum AlgorithmType { + RoundRobin, + LeastFull + } + void destroyVolumeOnFiler(String ipAddress, String aggrName, String volName) throws ServerException, InvalidParameterValueException, ResourceInUseException; + + void createVolumeOnFiler(String ipAddress, String aggName, String poolName, + String volName, String volSize, String snapshotPolicy, + Integer snapshotReservation, String username, String password) + throws UnknownHostException, ServerException, InvalidParameterValueException; + + public String[] associateLun(String guestIqn, String path) throws ServerException, InvalidParameterValueException; + + + void disassociateLun(String iGroup, String path) throws ServerException, InvalidParameterValueException; + + List listLunsOnFiler(String poolName); + + void destroyLunOnFiler(String path) throws ServerException, InvalidParameterValueException; + + List listVolumesOnFiler(String poolName); + + List listVolumesAscending(String poolName); + + long returnAvailableVolumeSize(String volName, String userName, + String password, String serverIp) throws ServerException; + + void createPool(String poolName, String algorithm) throws InvalidParameterValueException; + + void modifyPool(String poolName, String algorithm) throws InvalidParameterValueException; + + void deletePool(String poolName) throws InvalidParameterValueException, ResourceInUseException; + + List listPools(); + + public String[] createLunOnFiler(String poolName, Long lunSize) throws InvalidParameterValueException, ServerException, ResourceAllocationException; + +} diff --git a/server/src/com/cloud/netapp/NetappManagerImpl.java b/server/src/com/cloud/netapp/NetappManagerImpl.java new file mode 100644 index 00000000000..bb670f7ced6 --- /dev/null +++ b/server/src/com/cloud/netapp/NetappManagerImpl.java @@ -0,0 +1,1058 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ + +package com.cloud.netapp; + +import java.io.IOException; +import java.net.UnknownHostException; +import java.rmi.ServerException; +import java.util.ArrayList; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import netapp.manage.NaAPIFailedException; +import netapp.manage.NaAuthenticationException; +import netapp.manage.NaElement; +import netapp.manage.NaException; +import netapp.manage.NaProtocolException; +import netapp.manage.NaServer; + +import org.apache.log4j.Logger; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceInUseException; +import com.cloud.netapp.dao.LunDao; +import com.cloud.netapp.dao.PoolDao; +import com.cloud.netapp.dao.VolumeDao; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; + +@Local(value = { NetappManager.class }) +public class NetappManagerImpl implements NetappManager +{ + public enum Algorithm { roundrobin,leastfull } + + protected String _name; + + public static final Logger s_logger = Logger.getLogger(NetappManagerImpl.class.getName()); + @Inject public VolumeDao _volumeDao; + @Inject public PoolDao _poolDao; + @Inject public LunDao _lunDao; + private NetappAllocator _netappAllocator = null; + + /** + * Default constructor + */ + public NetappManagerImpl(){ + } + + @Override + public void createPool(String poolName, String algorithm) throws InvalidParameterValueException + { + if(s_logger.isDebugEnabled()) + s_logger.debug("Request --> createPool "); + + PoolVO pool = null; + validAlgorithm(algorithm); + try { + pool = new PoolVO(poolName, algorithm); + _poolDao.persist(pool); + + if(s_logger.isDebugEnabled()) + s_logger.debug("Response --> createPool:success"); + + } catch (CloudRuntimeException cre){ + pool = _poolDao.findPool(poolName); + if (pool != null) { + throw new InvalidParameterValueException("Duplicate Pool Name"); + } else { + throw cre; + } + } + } + + /** + * This method validates the algorithm used for allocation of the volume + * @param algorithm -- algorithm type + * @throws InvalidParameterValueException + */ + private void validAlgorithm(String algorithm) throws InvalidParameterValueException { + //TODO: use enum + if(!algorithm.equalsIgnoreCase("roundrobin") && !algorithm.equalsIgnoreCase("leastfull")){ + throw new InvalidParameterValueException("Unknown algorithm " + algorithm); + } + } + + /** + * Utility method to get the netapp server object + * @param serverIp -- ip address of netapp box + * @param userName -- username + * @param password -- password + * @return + * @throws UnknownHostException + */ + private NaServer getServer(String serverIp, String userName, String password) throws UnknownHostException { + //Initialize connection to server, and + //request version 1.3 of the API set + NaServer s = new NaServer(serverIp, 1, 3); + s.setStyle(NaServer.STYLE_LOGIN_PASSWORD); + s.setAdminUser(userName, password); + + return s; + } + + @Override + public void modifyPool(String poolName, String algorithm) throws InvalidParameterValueException + { + validAlgorithm(algorithm); + PoolVO pool = _poolDao.findPool(poolName); + + if(pool == null){ + throw new InvalidParameterValueException("Cannot find pool " + poolName); + } + + validAlgorithm(algorithm); + + pool.setAlgorithm(algorithm); + pool.setName(poolName); + + _poolDao.update(pool.getId(), pool); + } + + @Override + public void deletePool(String poolName) throws InvalidParameterValueException, ResourceInUseException + { + if(s_logger.isDebugEnabled()) + s_logger.debug("Request --> deletePool "); + + PoolVO pool = _poolDao.findPool(poolName); + if(pool == null){ + throw new InvalidParameterValueException("Cannot find pool " + poolName); + } + //check if pool is empty + int volCount = _volumeDao.listVolumes(poolName).size(); + + if(volCount==0){ + _poolDao.remove(pool.getId()); + if(s_logger.isDebugEnabled()) + s_logger.debug("Request --> deletePool: Success "); + + } else { + throw new ResourceInUseException("Cannot delete non-empty pool"); + } + } + + @Override + public List listPools(){ + return _poolDao.listAll(); + } + + /** + * This method destroys the volume on netapp filer + * @param ipAddress -- ip address of filer + * @param aggrName -- name of containing aggregate + * @param volName -- name of volume to destroy + * @throws ResourceInUseException + * @throws NaException + * @throws IOException + * @throws NaProtocolException + * @throws NaAPIFailedException + * @throws NaAuthenticationException + */ + @Override + @DB + public void destroyVolumeOnFiler(String ipAddress, String aggrName, String volName) throws ServerException, InvalidParameterValueException, ResourceInUseException{ + NaElement xi0; + NaElement xi1; + NetappVolumeVO volume = null; + + volume = _volumeDao.findVolume(ipAddress, aggrName, volName); + + if(volume==null) + { + s_logger.warn("The volume does not exist in our system"); + throw new InvalidParameterValueException("The given tuple:"+ipAddress+","+aggrName+","+volName+" doesn't exist in our system"); + } + + List lunsOnVol = _lunDao.listLunsByVolId(volume.getId()); + + if(lunsOnVol!=null && lunsOnVol.size()>0) + { + s_logger.warn("There are luns on the volume"); + throw new ResourceInUseException("There are luns on the volume"); + } + + final Transaction txn = Transaction.currentTxn(); + txn.start(); + PoolVO pool = _poolDao.findById(volume.getPoolId()); + if (pool == null) { + throw new InvalidParameterValueException("Failed to find pool for given volume"); + //FIXME: choose a better exception. this is a db integrity exception + } + pool = _poolDao.acquireInLockTable(pool.getId()); + if (pool == null) { + throw new ConcurrentModificationException("Failed to acquire lock on pool " + volume.getPoolId()); + } + NaServer s = null; + try + { + s = getServer(volume.getIpAddress(), volume.getUsername(), volume.getPassword()); + //bring the volume down + xi0 = new NaElement("volume-offline"); + xi0.addNewChild("name",volName); + s.invokeElem(xi0); + + //now destroy it + xi1 = new NaElement("volume-destroy"); + xi1.addNewChild("name",volName); + s.invokeElem(xi1); + + //now delete from our records + _volumeDao.remove(volume.getId()); + txn.commit(); + + } catch (UnknownHostException uhe) { + s_logger.warn("Unable to delete volume on filer " , uhe); + throw new ServerException("Unable to delete volume on filer", uhe); + }catch (NaAPIFailedException naf) { + s_logger.warn("Unable to delete volume on filer " , naf); + if( naf.getErrno() == 13040 ){ + s_logger.info("Deleting the volume: " + volName); + _volumeDao.remove(volume.getId()); + txn.commit(); + } + + throw new ServerException("Unable to delete volume on filer", naf); + } + catch (NaException nae) { + txn.rollback(); + s_logger.warn("Unable to delete volume on filer " , nae); + throw new ServerException("Unable to delete volume on filer", nae); + } catch (IOException ioe) { + txn.rollback(); + s_logger.warn("Unable to delete volume on filer " , ioe); + throw new ServerException("Unable to delete volume on filer", ioe); + } + finally + { + if (pool != null) { + _poolDao.releaseFromLockTable(pool.getId()); + } + if (s != null) + s.close(); + } + + + + } + + /** + * This method creates a volume on netapp filer + * @param ipAddress -- ip address of the filer + * @param aggName -- name of aggregate + * @param poolName -- name of pool + * @param volName -- name of volume + * @param volSize -- size of volume to be created + * @param snapshotPolicy -- associated snapshot policy for volume + * @param snapshotReservation -- associated reservation for snapshots + * @param username -- username + * @param password -- password + * @throws UnknownHostException + * @throws InvalidParameterValueException + */ + @Override + @DB + public void createVolumeOnFiler(String ipAddress, String aggName, String poolName, String volName, String volSize, String snapshotPolicy, Integer snapshotReservation, String username, String password) throws UnknownHostException, ServerException, InvalidParameterValueException + { + + if(s_logger.isDebugEnabled()) + s_logger.debug("Request --> createVolume "+"serverIp:"+ipAddress); + + boolean snapPolicy = false; + boolean snapshotRes = false; + boolean volumeCreated = false; + + NaServer s = getServer(ipAddress, username, password); + + NaElement xi = new NaElement("volume-create"); + xi.addNewChild("volume", volName); + xi.addNewChild("containing-aggr-name",aggName); + xi.addNewChild("size",volSize); + + NaElement xi1 = new NaElement("snapshot-set-reserve"); + if(snapshotReservation!=null) + { + snapshotRes = true; + xi1.addNewChild("percentage",snapshotReservation.toString()); + xi1.addNewChild("volume",volName); + } + + NaElement xi2 = new NaElement("snapshot-set-schedule"); + + if(snapshotPolicy!=null) + { + snapPolicy = true; + + String weeks = null; + String days = null; + String hours = null; + String whichHours = null; + String minutes = null; + String whichMinutes = null; + + StringTokenizer s1 = new StringTokenizer(snapshotPolicy," "); + + //count=4: weeks days hours@csi mins@csi + //count=3: weeks days hours@csi + //count=2: weeks days + //count=1: weeks + + if(s1.hasMoreTokens()) + { + weeks = s1.nextToken(); + } + if(weeks!=null && s1.hasMoreTokens()) + { + days = s1.nextToken(); + } + if(days!=null && s1.hasMoreTokens()) + { + String[] hoursArr = s1.nextToken().split("@"); + hours = hoursArr[0]; + whichHours = hoursArr[1]; + } + if(hours!=null && s1.hasMoreTokens()) + { + String[] minsArr = s1.nextToken().split("@"); + minutes = minsArr[0]; + whichMinutes = minsArr[1]; + } + + if(weeks!=null) + xi2.addNewChild("weeks",weeks); + if(days!=null) + xi2.addNewChild("days",days); + if(hours!=null) + xi2.addNewChild("hours",hours); + if(minutes!=null) + xi2.addNewChild("minutes",minutes); + xi2.addNewChild("volume",volName); + + if(whichHours!=null) + xi2.addNewChild("which-hours",whichHours); + if(whichMinutes!=null) + xi2.addNewChild("which-minutes",whichMinutes); + } + Long volumeId = null; + + final Transaction txn = Transaction.currentTxn(); + txn.start(); + NetappVolumeVO volume = null; + volume = _volumeDao.findVolume(ipAddress, aggName, volName); + + if(volume != null) { + throw new InvalidParameterValueException("The volume for the given ipAddress/aggregateName/volumeName tuple already exists"); + } + PoolVO pool = _poolDao.findPool(poolName); + if (pool == null) { + throw new InvalidParameterValueException("Cannot find pool " + poolName); + } + pool = _poolDao.acquireInLockTable(pool.getId()); + if (pool == null) { + s_logger.warn("Failed to acquire lock on pool " + poolName); + throw new ConcurrentModificationException("Failed to acquire lock on pool " + poolName); + } + volume = new NetappVolumeVO(ipAddress, aggName, pool.getId(), volName, volSize, "", 0, username, password,0,pool.getName()); + volume = _volumeDao.persist(volume); + + volumeId = volume.getId(); + try { + s.invokeElem(xi); + volumeCreated = true; + + if(snapshotRes) + { + s.invokeElem(xi1); + volume.setSnapshotReservation(snapshotReservation); + _volumeDao.update(volumeId, volume); + } + + if(snapPolicy) + { + s.invokeElem(xi2); + volume.setSnapshotPolicy(snapshotPolicy); + _volumeDao.update(volumeId, volume); + } + txn.commit(); + } catch (NaException nae) { + //zapi call failed, log and throw e + s_logger.warn("Failed to create volume on the netapp filer:", nae); + txn.rollback(); + if(volumeCreated) { + try { + deleteRogueVolume(volName, s);//deletes created volume on filer + } catch (NaException e) { + s_logger.warn("Failed to cleanup created volume whilst rolling back on the netapp filer:", e); + throw new ServerException("Unable to create volume via cloudtools."+"Failed to cleanup created volume on netapp filer whilst rolling back on the cloud db:", e); + } catch (IOException e) { + s_logger.warn("Failed to cleanup created volume whilst rolling back on the netapp filer:", e); + throw new ServerException("Unable to create volume via cloudtools."+"Failed to cleanup created volume on netapp filer whilst rolling back on the cloud db:", e); + } + } + throw new ServerException("Unable to create volume", nae); + } catch (IOException ioe) { + s_logger.warn("Failed to create volume on the netapp filer:", ioe); + txn.rollback(); + if(volumeCreated) { + try { + deleteRogueVolume(volName, s);//deletes created volume on filer + } catch (NaException e) { + s_logger.warn("Failed to cleanup created volume whilst rolling back on the netapp filer:", e); + throw new ServerException("Unable to create volume via cloudtools."+"Failed to cleanup created volume on netapp filer whilst rolling back on the cloud db:", e); + } catch (IOException e) { + s_logger.warn("Failed to cleanup created volume whilst rolling back on the netapp filer:", e); + throw new ServerException("Unable to create volume via cloudtools."+"Failed to cleanup created volume on netapp filer whilst rolling back on the cloud db:", e); + } + } + throw new ServerException("Unable to create volume", ioe); + } + finally{ + if (s != null) + s.close(); + if (pool != null) + _poolDao.releaseFromLockTable(pool.getId()); + + } + } + + /** + * This method is primarily used to cleanup volume created on the netapp filer, when createVol api command fails at snapshot reservation. + * We roll back the db record, but the record on the netapp box still exists. We clean up that record using this helper method. + * @param volName + * @param s -- server reference + * @throws NaException + * @throws IOException + */ + private void deleteRogueVolume(String volName, NaServer s) throws NaException, IOException { + //bring the volume down + NaElement xi0 = new NaElement("volume-offline"); + xi0.addNewChild("name",volName); + s.invokeElem(xi0); + + //now destroy it + NaElement xi1 = new NaElement("volume-destroy"); + xi1.addNewChild("name",volName); + s.invokeElem(xi1); + } + + /** + * This method lists all the volumes by pool name + * @param poolName + * @return -- volumes in that pool + */ + @Override + public List listVolumesOnFiler(String poolName){ + + List vols = _volumeDao.listVolumesAscending(poolName); + + for(NetappVolumeVO vol : vols){ + try { + String snapScheduleOnFiler = returnSnapshotSchedule(vol); + vol.setSnapshotPolicy(snapScheduleOnFiler); + + } catch (ServerException e) { + s_logger.warn("Error trying to get snapshot schedule for volume"+vol.getVolumeName()); + } + } + return vols; + } + + + /** + * Utility method to return snapshot schedule for a volume + * @param vol -- volume for the snapshot schedule creation + * @return -- the snapshot schedule + * @throws ServerException + */ + private String returnSnapshotSchedule(NetappVolumeVO vol) throws ServerException{ + + NaElement xi = new NaElement("snapshot-get-schedule"); + xi.addNewChild("volume", vol.getVolumeName()); + NaServer s = null; + try { + s = getServer(vol.getIpAddress(), vol.getUsername(), vol.getPassword()); + NaElement xo = s.invokeElem(xi); + String weeks = xo.getChildContent("weeks"); + String days = xo.getChildContent("days"); + String hours = xo.getChildContent("hours"); + String minutes = xo.getChildContent("minutes"); + String whichHours = xo.getChildContent("which-hours"); + String whichMinutes = xo.getChildContent("which-minutes"); + + StringBuilder sB = new StringBuilder(); + sB.append(weeks).append(" ").append(days).append(" ").append(hours).append("@").append(whichHours).append(" ").append(minutes).append("@").append(whichMinutes); + return sB.toString(); + } catch (NaException nae) { + s_logger.warn("Failed to get volume size ", nae); + throw new ServerException("Failed to get volume size", nae); + } catch (IOException ioe) { + s_logger.warn("Failed to get volume size ", ioe); + throw new ServerException("Failed to get volume size", ioe); + } + finally{ + if (s != null) + s.close(); + } + } + /** + * This method returns the ascending order list of volumes based on their ids + * @param poolName -- name of pool + * @return -- ascending ordered list of volumes based on ids + */ + @Override + public List listVolumesAscending(String poolName){ + return _volumeDao.listVolumesAscending(poolName); + } + + /** + * This method returns the available size on the volume in terms of bytes + * @param volName -- name of volume + * @param userName -- username + * @param password -- password + * @param serverIp -- ip address of filer + * @throws UnknownHostException + * @return-- available size on the volume in terms of bytes; return -1 if volume is offline + * @throws ServerException + */ + @Override + public long returnAvailableVolumeSize(String volName, String userName, String password, String serverIp) throws ServerException { + long availableSize = 0; + + NaElement xi = new NaElement("volume-list-info"); + xi.addNewChild("volume", volName); + NaServer s = null; + String volumeState = null; + try { + s = getServer(serverIp, userName, password); + NaElement xo = s.invokeElem(xi); + List volList = xo.getChildByName("volumes").getChildren(); + Iterator volIter = volList.iterator(); + while(volIter.hasNext()){ + NaElement volInfo=(NaElement)volIter.next(); + availableSize = volInfo.getChildLongValue("size-available", -1); + volumeState = volInfo.getChildContent("state"); + } + + if (volumeState != null) { + return volumeState.equalsIgnoreCase("online") ? availableSize : -1; //return -1 if volume is offline + } + else { + //catch all + //volume state unreported + return -1; // as good as volume offline + } + + + } catch (NaException nae) { + s_logger.warn("Failed to get volume size ", nae); + throw new ServerException("Failed to get volume size", nae); + } catch (IOException ioe) { + s_logger.warn("Failed to get volume size ", ioe); + throw new ServerException("Failed to get volume size", ioe); + } + finally{ + if (s != null) + s.close(); + } + } + + /** + * This method creates a lun on the netapp filer + * @param poolName -- name of the pool + * @param lunSize -- size of the lun to be created + * @return -- lun path + * @throws IOException + * @throws ResourceAllocationException + * @throws NaException + */ + @Override + @DB + public String[] createLunOnFiler(String poolName, Long lunSize) throws ServerException, InvalidParameterValueException, ResourceAllocationException { + String[] result = new String[3]; + StringBuilder lunName = new StringBuilder("lun-"); + LunVO lun = null; + final Transaction txn = Transaction.currentTxn(); + txn.start(); + PoolVO pool = _poolDao.findPool(poolName); + + if(pool == null){ + throw new InvalidParameterValueException("Cannot find pool " + poolName); + } + + if (lunSize <= 0) { + throw new InvalidParameterValueException("Please specify a valid lun size in Gb"); + } + + String algorithm = pool.getAlgorithm(); + NetappVolumeVO selectedVol = null; + + //sanity check + int numVolsInPool = _volumeDao.listVolumes(poolName).size(); + + if (numVolsInPool == 0) + { + throw new InvalidParameterValueException("No volumes exist in the given pool"); + } + pool = _poolDao.acquireInLockTable(pool.getId()); + if (pool == null) { + s_logger.warn("Failed to acquire lock on the pool " + poolName); + return result; + } + NaServer s = null; + + try + { + if(algorithm == null || algorithm.equals(Algorithm.roundrobin.toString())) + { + selectedVol = _netappAllocator.chooseVolumeFromPool(poolName, lunSize); + } + else if(algorithm.equals(Algorithm.leastfull.toString())) + { + + selectedVol = _netappAllocator.chooseLeastFullVolumeFromPool(poolName, lunSize); + } + + if(selectedVol == null) + { + throw new ResourceAllocationException("Could not find a suitable volume to create lun on"); + } + + if(s_logger.isDebugEnabled()) + s_logger.debug("Request --> createLun "+"serverIp:"+selectedVol.getIpAddress()); + + StringBuilder exportPath = new StringBuilder("/vol/"); + exportPath.append(selectedVol.getVolumeName()); + exportPath.append("/"); + + lun = new LunVO(exportPath.toString(), selectedVol.getId(), lunSize, "",""); + lun = _lunDao.persist(lun); + + //Lun id created: 6 digits right justified eg. 000045 + String lunIdStr = lun.getId().toString(); + String zeroStr = "000000"; + int length = lunIdStr.length(); + int offset = 6-length; + StringBuilder lunIdOnPath = new StringBuilder(); + lunIdOnPath.append(zeroStr.substring(0, offset)); + lunIdOnPath.append(lunIdStr); + exportPath.append("lun-").append(lunIdOnPath.toString()); + + lunName.append(lunIdOnPath.toString()); + + //update lun name + lun.setLunName(lunName.toString()); + _lunDao.update(lun.getId(), lun); + + NaElement xi; + NaElement xi1; + + long lSizeBytes = 1L*lunSize*1024*1024*1024; //This prevents integer overflow + Long lunSizeBytes = new Long(lSizeBytes); + + s = getServer(selectedVol.getIpAddress(), selectedVol.getUsername(),selectedVol.getPassword()); + + //create lun + xi = new NaElement("lun-create-by-size"); + xi.addNewChild("ostype","linux"); + xi.addNewChild("path",exportPath.toString()); + xi.addNewChild("size", (lunSizeBytes.toString())); + + s.invokeElem(xi); + + try + { + //now create an igroup + xi1 = new NaElement("igroup-create"); + xi1.addNewChild("initiator-group-name", lunName .toString()); + xi1.addNewChild("initiator-group-type", "iscsi"); + xi1.addNewChild("os-type", "linux"); + s.invokeElem(xi1); + }catch(NaAPIFailedException e) + { + if(e.getErrno() == 9004) + { + //igroup already exists hence no error + s_logger.warn("Igroup already exists"); + } + } + + //get target iqn + NaElement xi4 = new NaElement("iscsi-node-get-name"); + NaElement xo = s.invokeElem(xi4); + String iqn = xo.getChildContent("node-name"); + + lun.setTargetIqn(iqn); + _lunDao.update(lun.getId(), lun); + + //create lun mapping + //now map the lun to the igroup + NaElement xi3 = new NaElement("lun-map"); + xi3.addNewChild("force", "true"); + xi3.addNewChild("initiator-group", lunName.toString()); + xi3.addNewChild("path", lun.getPath() + lun.getLunName()); + + xi3.addNewChild("lun-id", lunIdStr); + s.invokeElem(xi3); + + txn.commit(); + //set the result + result[0] = lunName.toString();//lunname + result[1] = iqn;//iqn + result[2] = selectedVol.getIpAddress(); + + return result; + + } + catch (NaAPIFailedException naf) + { + if(naf.getErrno() == 9023){ //lun is already mapped to this group + result[0] = lunName.toString();//lunname; + result[1] = lun.getTargetIqn();//iqn + result[2] = selectedVol.getIpAddress(); + return result; + } + if(naf.getErrno() == 9024){ //another lun mapped at this group + result[0] = lunName.toString();//lunname; + result[1] = lun.getTargetIqn();//iqn + result[2] = selectedVol.getIpAddress(); + return result; + } + } + catch (NaException nae) + { + txn.rollback(); + throw new ServerException("Unable to create LUN", nae); + } + catch (IOException ioe) + { + txn.rollback(); + throw new ServerException("Unable to create LUN", ioe); + } + finally + { + if (pool != null) { + _poolDao.releaseFromLockTable(pool.getId()); + } + if (s != null) { + s.close(); + } + } + + return result; + } + + /** + * This method destroys a lun on the netapp filer + * @param lunName -- name of the lun to be destroyed + */ + @Override + @DB + public void destroyLunOnFiler(String lunName) throws InvalidParameterValueException, ServerException{ + + final Transaction txn = Transaction.currentTxn(); + txn.start(); + + LunVO lun = _lunDao.findByName(lunName); + + if(lun == null) + throw new InvalidParameterValueException("Cannot find lun"); + + NetappVolumeVO vol = _volumeDao.acquireInLockTable(lun.getVolumeId()); + if (vol == null) { + s_logger.warn("Failed to lock volume id= " + lun.getVolumeId()); + return; + } + NaServer s = null; + try { + s = getServer(vol.getIpAddress(), vol.getUsername(), vol.getPassword()); + + if(s_logger.isDebugEnabled()) + s_logger.debug("Request --> destroyLun "+":serverIp:"+vol.getIpAddress()); + + try { + //Unmap lun + NaElement xi2 = new NaElement("lun-unmap"); + xi2.addNewChild("initiator-group", lunName); + xi2.addNewChild("path", lun.getPath()+lun.getLunName()); + s.invokeElem(xi2); + } catch (NaAPIFailedException naf) { + if(naf.getErrno()==9016) + s_logger.warn("no map exists excpn 9016 caught in deletelun, continuing with delete"); + } + + //destroy lun + NaElement xi = new NaElement("lun-destroy"); + xi.addNewChild("force","true"); + xi.addNewChild("path", lun.getPath()+lun.getLunName()); + s.invokeElem(xi); + + //destroy igroup + NaElement xi1 = new NaElement("igroup-destroy"); + //xi1.addNewChild("force","true"); + xi1.addNewChild("initiator-group-name",lunName); + s.invokeElem(xi1); + + _lunDao.remove(lun.getId()); + txn.commit(); + } catch(UnknownHostException uhe) { + txn.rollback(); + s_logger.warn("Failed to delete lun", uhe); + throw new ServerException("Failed to delete lun", uhe); + } catch ( IOException ioe) { + txn.rollback(); + s_logger.warn("Failed to delete lun", ioe); + throw new ServerException("Failed to delete lun", ioe); + }catch (NaAPIFailedException naf) { + if(naf.getErrno() == 9017){//no such group exists excpn + s_logger.warn("no such group exists excpn 9017 caught in deletelun, continuing with delete"); + _lunDao.remove(lun.getId()); + txn.commit(); + }else if(naf.getErrno() == 9029){//LUN maps for this initiator group exist + s_logger.warn("LUN maps for this initiator group exist errno 9029 caught in deletelun, continuing with delete"); + _lunDao.remove(lun.getId()); + txn.commit(); + }else{ + txn.rollback(); + s_logger.warn("Failed to delete lun", naf); + throw new ServerException("Failed to delete lun", naf); + } + + } + catch (NaException nae) { + txn.rollback(); + s_logger.warn("Failed to delete lun", nae); + throw new ServerException("Failed to delete lun", nae); + } + finally{ + if (vol != null) { + _volumeDao.releaseFromLockTable(vol.getId()); + } + if (s != null) + s.close(); + } + + } + + /** + * This method lists the luns on the netapp filer + * @param volId -- id of the containing volume + * @return -- list of netapp luns + * @throws NaException + * @throws IOException + */ + @Override + public List listLunsOnFiler(String poolName) + { + if(s_logger.isDebugEnabled()) + s_logger.debug("Request --> listLunsOnFiler "); + + List luns = new ArrayList(); + + List vols = _volumeDao.listVolumes(poolName); + + for(NetappVolumeVO vol : vols) + { + luns.addAll(_lunDao.listLunsByVolId(vol.getId())); + } + + if(s_logger.isDebugEnabled()) + s_logger.debug("Response --> listLunsOnFiler:success"); + + return luns; + } + + + /** + * This method disassociates a lun from the igroup on the filer + * @param iGroup -- igroup name + * @param lunName -- lun name + */ + @Override + public void disassociateLun(String iGroup, String lunName) throws ServerException, InvalidParameterValueException + { + NaElement xi; + LunVO lun = _lunDao.findByName(lunName); + + if(lun == null) + throw new InvalidParameterValueException("Cannot find LUN " + lunName); + + NetappVolumeVO vol = _volumeDao.findById(lun.getVolumeId()); + NaServer s = null; + try { + s = getServer(vol.getIpAddress(), vol.getUsername(), vol.getPassword()); + + if(s_logger.isDebugEnabled()) + s_logger.debug("Request --> disassociateLun "+":serverIp:"+vol.getIpAddress()); + + xi = new NaElement("igroup-remove"); + xi.addNewChild("force", "true"); + xi.addNewChild("initiator", iGroup); + xi.addNewChild("initiator-group-name", lunName); + s.invokeElem(xi); + + } catch(UnknownHostException uhe) { + throw new ServerException("Failed to disassociate lun", uhe); + } catch ( IOException ioe) { + throw new ServerException("Failed to disassociate lun", ioe); + } catch (NaException nae) { + throw new ServerException("Failed to disassociate lun", nae); + } finally{ + if (s != null) + s.close(); + } + + } + + /** + * This method associates a lun to a particular igroup + * @param iqn + * @param iGroup + * @param lunName + */ + @Override + public String[] associateLun(String guestIqn, String lunName) throws ServerException, InvalidParameterValueException + + { + NaElement xi2; + + //get lun id from path + String[] splitLunName = lunName.split("-"); + String[] returnVal = new String[3]; + if(splitLunName.length != 2) + throw new InvalidParameterValueException("The lun id is malformed"); + + String lunIdStr = splitLunName[1]; + + Long lId = new Long(lunIdStr); + + LunVO lun = _lunDao.findById(lId); + + if(lun == null) + throw new InvalidParameterValueException("Cannot find LUN " + lunName); + + NetappVolumeVO vol = _volumeDao.findById(lun.getVolumeId()); + + //assert(vol != null); + + returnVal[0] = lunIdStr; + returnVal[1] = lun.getTargetIqn(); + returnVal[2] = vol.getIpAddress(); + + NaServer s = null; + + try + { + s = getServer(vol.getIpAddress(), vol.getUsername(), vol.getPassword()); + + if(s_logger.isDebugEnabled()) + s_logger.debug("Request --> associateLun "+":serverIp:"+vol.getIpAddress()); + + //add iqn to the group + xi2 = new NaElement("igroup-add"); + xi2.addNewChild("force", "true"); + xi2.addNewChild("initiator", guestIqn); + xi2.addNewChild("initiator-group-name", lunName); + s.invokeElem(xi2); + + return returnVal; + } catch (UnknownHostException uhe) { + s_logger.warn("Unable to associate LUN " , uhe); + throw new ServerException("Unable to associate LUN", uhe); + }catch (NaAPIFailedException naf){ + if(naf.getErrno() == 9008){ //initiator group already contains node + return returnVal; + } + s_logger.warn("Unable to associate LUN " , naf); + throw new ServerException("Unable to associate LUN", naf); + } + catch (NaException nae) { + s_logger.warn("Unable to associate LUN " , nae); + throw new ServerException("Unable to associate LUN", nae); + } catch (IOException ioe) { + s_logger.warn("Unable to associate LUN " , ioe); + throw new ServerException("Unable to associate LUN", ioe); + } + finally{ + if (s != null) + s.close(); + } + + } + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + + _name = name; + + _netappAllocator = new NetappDefaultAllocatorImpl( this ); + + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + +} diff --git a/server/src/com/cloud/netapp/NetappVolumeVO.java b/server/src/com/cloud/netapp/NetappVolumeVO.java new file mode 100644 index 00000000000..7435eb83312 --- /dev/null +++ b/server/src/com/cloud/netapp/NetappVolumeVO.java @@ -0,0 +1,201 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ + +package com.cloud.netapp; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name="netapp_volume") +public class NetappVolumeVO { + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private Long id; + + @Column(name="ip_address") + private String ipAddress; + + @Column(name="aggregate_name") + private String aggregateName; + + @Column(name="pool_id") + private Long poolId; + + @Column(name="pool_name") + private String poolName; + + @Column(name="volume_name") + private String volumeName; + + @Column(name="username") + private String username; + + @Column(name="password") + private String password; + + @Column(name="snapshot_policy") + private String snapshotPolicy; + + @Column(name="snapshot_reservation") + private Integer snapshotReservation; + + @Column(name="volume_size") + private String volumeSize; + + @Column(name="round_robin_marker") + private int roundRobinMarker; + + public NetappVolumeVO(){ + + } + + public NetappVolumeVO(String ipAddress, String aggName, Long poolId, String volName, String volSize, String snapshotPolicy, int snapshotReservation, String username, String password, int roundRobinMarker, String poolName) { + this.ipAddress = ipAddress; + this.aggregateName = aggName; + this.poolId = poolId; + this.username = username; + this.password = password; + this.volumeName = volName; + this.volumeSize = volSize; + this.snapshotPolicy = snapshotPolicy; + this.snapshotReservation = snapshotReservation; + this.roundRobinMarker = roundRobinMarker; + this.poolName = poolName; + } + + + public String getPoolName() { + return poolName; + } + + public void setPoolName(String poolName) { + this.poolName = poolName; + } + + public int getRoundRobinMarker() { + return roundRobinMarker; + } + + public void setRoundRobinMarker(int roundRobinMarker) { + this.roundRobinMarker = roundRobinMarker; + } + + public String getVolumeName() { + return volumeName; + } + + public void setVolumeName(String volumeName) { + this.volumeName = volumeName; + } + + public String getSnapshotPolicy() { + return snapshotPolicy; + } + + public void setSnapshotPolicy(String snapshotPolicy) { + this.snapshotPolicy = snapshotPolicy; + } + + public Integer getSnapshotReservation() { + return snapshotReservation; + } + + public void setSnapshotReservation(Integer snapshotReservation) { + this.snapshotReservation = snapshotReservation; + } + + public String getVolumeSize() { + return volumeSize; + } + + public void setVolumeSize(String volumeSize) { + this.volumeSize = volumeSize; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public String getAggregateName() { + return aggregateName; + } + + public void setAggregateName(String aggregateName) { + this.aggregateName = aggregateName; + } + + public Long getPoolId() { + return poolId; + } + + public void setPoolId(Long poolId) { + this.poolId = poolId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + +} diff --git a/server/src/com/cloud/netapp/PoolVO.java b/server/src/com/cloud/netapp/PoolVO.java new file mode 100644 index 00000000000..90fa0b43f0e --- /dev/null +++ b/server/src/com/cloud/netapp/PoolVO.java @@ -0,0 +1,92 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ + +package com.cloud.netapp; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name="netapp_pool") +public class PoolVO { + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private Long id; + + @Column(name="name") + private String name; + + @Column(name="algorithm") + private String algorithm; + + public PoolVO(){ + + } + + public PoolVO(String name, String algorithm) { + this.name = name; + this.algorithm = algorithm; + } + +} diff --git a/server/src/com/cloud/netapp/dao/LunDao.java b/server/src/com/cloud/netapp/dao/LunDao.java new file mode 100644 index 00000000000..b62cf1a4d1f --- /dev/null +++ b/server/src/com/cloud/netapp/dao/LunDao.java @@ -0,0 +1,49 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ + +package com.cloud.netapp.dao; + +import java.util.List; + +import com.cloud.netapp.LunVO; +import com.cloud.netapp.NetappVolumeVO; +import com.cloud.netapp.PoolVO; +import com.cloud.utils.db.GenericDao; + +public interface LunDao extends GenericDao { + + List listLunsByVolId(Long volId); + + LunVO findByName(String name); +} diff --git a/server/src/com/cloud/netapp/dao/LunDaoImpl.java b/server/src/com/cloud/netapp/dao/LunDaoImpl.java new file mode 100644 index 00000000000..776fdfd01c5 --- /dev/null +++ b/server/src/com/cloud/netapp/dao/LunDaoImpl.java @@ -0,0 +1,88 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ + +package com.cloud.netapp.dao; + +import java.util.List; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.netapp.LunVO; +import com.cloud.netapp.NetappVolumeVO; +import com.cloud.netapp.PoolVO; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Local(value={LunDao.class}) +public class LunDaoImpl extends GenericDaoBase implements LunDao { + private static final Logger s_logger = Logger.getLogger(PoolDaoImpl.class); + + protected final SearchBuilder LunSearch; + protected final SearchBuilder LunNameSearch; + + protected LunDaoImpl() { + + LunSearch = createSearchBuilder(); + LunSearch.and("volumeId", LunSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + LunSearch.done(); + + LunNameSearch = createSearchBuilder(); + LunNameSearch.and("name", LunNameSearch.entity().getLunName(), SearchCriteria.Op.EQ); + LunNameSearch.done(); + + } + + @Override + public List listLunsByVolId(Long volId) { + Filter searchFilter = new Filter(LunVO.class, "id", Boolean.TRUE, Long.valueOf(0), Long.valueOf(10000)); + + SearchCriteria sc = LunSearch.create(); + sc.setParameters("volumeId", volId); + List lunList = listBy(sc,searchFilter); + + return lunList; + } + + + @Override + public LunVO findByName(String name) { + SearchCriteria sc = LunNameSearch.create(); + sc.setParameters("name", name); + return findOneBy(sc); + } +} diff --git a/server/src/com/cloud/netapp/dao/PoolDao.java b/server/src/com/cloud/netapp/dao/PoolDao.java new file mode 100644 index 00000000000..40f5ceb74fe --- /dev/null +++ b/server/src/com/cloud/netapp/dao/PoolDao.java @@ -0,0 +1,46 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ + +package com.cloud.netapp.dao; + +import java.util.List; + +import com.cloud.netapp.PoolVO; +import com.cloud.utils.db.GenericDao; + +public interface PoolDao extends GenericDao { + + PoolVO findPool(String poolName); + List listPools(); +} diff --git a/server/src/com/cloud/netapp/dao/PoolDaoImpl.java b/server/src/com/cloud/netapp/dao/PoolDaoImpl.java new file mode 100644 index 00000000000..c6c9f025940 --- /dev/null +++ b/server/src/com/cloud/netapp/dao/PoolDaoImpl.java @@ -0,0 +1,85 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ + +package com.cloud.netapp.dao; + +import java.util.List; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.netapp.PoolVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Local(value={PoolDao.class}) +public class PoolDaoImpl extends GenericDaoBase implements PoolDao { + private static final Logger s_logger = Logger.getLogger(PoolDaoImpl.class); + + protected final SearchBuilder PoolSearch; + + protected PoolDaoImpl() { + + PoolSearch = createSearchBuilder(); + PoolSearch.and("name", PoolSearch.entity().getName(), SearchCriteria.Op.EQ); + PoolSearch.done(); + + } + + @Override + public PoolVO findPool(String poolName) { + SearchCriteria sc = PoolSearch.create(); + sc.setParameters("name", poolName); + List poolList = listBy(sc); + + return(poolList.size()>0?poolList.get(0):null); + } + + @Override + public List listPools() { + // TODO Auto-generated method stub + return null; + } + + +// @Override +// public List listVolumes(String poolName) { +// SearchCriteria sc = NetappListVolumeSearch.create(); +// sc.setParameters("poolName", poolName); +// return listBy(sc); +// } + +} diff --git a/server/src/com/cloud/netapp/dao/VolumeDao.java b/server/src/com/cloud/netapp/dao/VolumeDao.java new file mode 100644 index 00000000000..fa1f20a7f4e --- /dev/null +++ b/server/src/com/cloud/netapp/dao/VolumeDao.java @@ -0,0 +1,48 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ + +package com.cloud.netapp.dao; + +import java.util.List; + +import com.cloud.netapp.NetappVolumeVO; +import com.cloud.utils.db.GenericDao; + +public interface VolumeDao extends GenericDao { + + NetappVolumeVO findVolume(String ipAddress, String aggregateName, String volumeName); + List listVolumes(String poolName); + NetappVolumeVO returnRoundRobinMarkerInPool(String poolName,int roundRobinMarker); + List listVolumesAscending(String poolName); +} diff --git a/server/src/com/cloud/netapp/dao/VolumeDaoImpl.java b/server/src/com/cloud/netapp/dao/VolumeDaoImpl.java new file mode 100644 index 00000000000..ba73e902f55 --- /dev/null +++ b/server/src/com/cloud/netapp/dao/VolumeDaoImpl.java @@ -0,0 +1,117 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @author-aj + */ + +package com.cloud.netapp.dao; + +import java.util.List; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.netapp.NetappVolumeVO; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Local(value={VolumeDao.class}) +public class VolumeDaoImpl extends GenericDaoBase implements VolumeDao { + private static final Logger s_logger = Logger.getLogger(VolumeDaoImpl.class); + + protected final SearchBuilder NetappVolumeSearch; + protected final SearchBuilder NetappListVolumeSearch; + protected final SearchBuilder NetappRoundRobinMarkerSearch; + + @Override + public NetappVolumeVO findVolume(String ipAddress, String aggregateName, String volumeName) { + SearchCriteria sc = NetappVolumeSearch.create(); + sc.setParameters("ipAddress", ipAddress); + sc.setParameters("aggregateName", aggregateName); + sc.setParameters("volumeName", volumeName); + + ListvolList = listBy(sc); + + return(volList.size()==0?null:volList.get(0)); + } + + protected VolumeDaoImpl() { + NetappVolumeSearch = createSearchBuilder(); + NetappVolumeSearch.and("ipAddress", NetappVolumeSearch.entity().getIpAddress(), SearchCriteria.Op.EQ); + NetappVolumeSearch.and("aggregateName", NetappVolumeSearch.entity().getAggregateName(), SearchCriteria.Op.EQ); + NetappVolumeSearch.and("volumeName", NetappVolumeSearch.entity().getVolumeName(), SearchCriteria.Op.EQ); + NetappVolumeSearch.done(); + + NetappListVolumeSearch = createSearchBuilder(); + NetappListVolumeSearch.and("poolName", NetappListVolumeSearch.entity().getPoolName(), SearchCriteria.Op.EQ); + NetappListVolumeSearch.done(); + + NetappRoundRobinMarkerSearch = createSearchBuilder(); + NetappRoundRobinMarkerSearch.and("roundRobinMarker", NetappRoundRobinMarkerSearch.entity().getRoundRobinMarker(), SearchCriteria.Op.EQ); + NetappRoundRobinMarkerSearch.and("poolName", NetappRoundRobinMarkerSearch.entity().getPoolName(), SearchCriteria.Op.EQ); + NetappRoundRobinMarkerSearch.done(); + } + + @Override + public List listVolumes(String poolName) { + SearchCriteria sc = NetappListVolumeSearch.create(); + sc.setParameters("poolName", poolName); + return listBy(sc); + } + + @Override + public NetappVolumeVO returnRoundRobinMarkerInPool(String poolName, int roundRobinMarker) { + SearchCriteria sc = NetappRoundRobinMarkerSearch.create(); + sc.setParameters("roundRobinMarker", roundRobinMarker); + sc.setParameters("poolName", poolName); + + List marker = listBy(sc); + + if(marker.size()>0) + return marker.get(0); + else + return null; + } + + @Override + public List listVolumesAscending(String poolName) + { + Filter searchFilter = new Filter(NetappVolumeVO.class, "id", Boolean.TRUE, Long.valueOf(0), Long.valueOf(10000)); + + SearchCriteria sc = NetappListVolumeSearch.create(); + sc.setParameters("poolName", poolName); + + return listBy(sc, searchFilter); + } +} diff --git a/server/src/com/cloud/network/ExternalFirewallManager.java b/server/src/com/cloud/network/ExternalFirewallManager.java new file mode 100644 index 00000000000..54dbedabd68 --- /dev/null +++ b/server/src/com/cloud/network/ExternalFirewallManager.java @@ -0,0 +1,35 @@ +/** + * Copyright (C) 2011 Cloud.com, Inc. All rights reserved. + */ + +package com.cloud.network; + +import java.util.List; + +import com.cloud.api.commands.AddExternalFirewallCmd; +import com.cloud.api.commands.DeleteExternalFirewallCmd; +import com.cloud.api.commands.ListExternalFirewallsCmd; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.network.rules.FirewallRule; +import com.cloud.offering.NetworkOffering; +import com.cloud.server.api.response.ExternalFirewallResponse; + +public interface ExternalFirewallManager extends ExternalNetworkManager { + + public Host addExternalFirewall(AddExternalFirewallCmd cmd); + + public boolean deleteExternalFirewall(DeleteExternalFirewallCmd cmd); + + public List listExternalFirewalls(ListExternalFirewallsCmd cmd); + + public ExternalFirewallResponse getApiResponse(Host externalFirewall); + + public boolean manageGuestNetwork(boolean add, Network network, NetworkOffering offering) throws ResourceUnavailableException; + + public boolean applyFirewallRules(Network network, List rules) throws ResourceUnavailableException; + + public boolean applyIps(Network network, List ipAddresses) throws ResourceUnavailableException; + +} diff --git a/server/src/com/cloud/network/ExternalLoadBalancerManager.java b/server/src/com/cloud/network/ExternalLoadBalancerManager.java new file mode 100644 index 00000000000..fdbd7426869 --- /dev/null +++ b/server/src/com/cloud/network/ExternalLoadBalancerManager.java @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2011 Cloud.com, Inc. All rights reserved. + */ + +package com.cloud.network; + +import java.util.List; + +import com.cloud.api.commands.AddExternalLoadBalancerCmd; +import com.cloud.api.commands.DeleteExternalLoadBalancerCmd; +import com.cloud.api.commands.ListExternalLoadBalancersCmd; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.network.rules.FirewallRule; +import com.cloud.server.api.response.ExternalFirewallResponse; +import com.cloud.server.api.response.ExternalLoadBalancerResponse; +public interface ExternalLoadBalancerManager extends ExternalNetworkManager { + + public Host addExternalLoadBalancer(AddExternalLoadBalancerCmd cmd); + + public boolean deleteExternalLoadBalancer(DeleteExternalLoadBalancerCmd cmd); + + public List listExternalLoadBalancers(ListExternalLoadBalancersCmd cmd); + + public ExternalLoadBalancerResponse getApiResponse(Host externalLoadBalancer); + + public boolean manageGuestNetwork(boolean add, Network guestConfig) throws ResourceUnavailableException; + + public boolean applyLoadBalancerRules(Network network, List rules) throws ResourceUnavailableException; + +} diff --git a/server/src/com/cloud/network/ExternalNetworkManager.java b/server/src/com/cloud/network/ExternalNetworkManager.java new file mode 100644 index 00000000000..6e62bba7f5a --- /dev/null +++ b/server/src/com/cloud/network/ExternalNetworkManager.java @@ -0,0 +1,96 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.network; + +import java.util.List; + +import com.cloud.api.commands.AddExternalFirewallCmd; +import com.cloud.api.commands.AddExternalLoadBalancerCmd; +import com.cloud.api.commands.DeleteExternalFirewallCmd; +import com.cloud.api.commands.DeleteExternalLoadBalancerCmd; +import com.cloud.api.commands.ListExternalFirewallsCmd; +import com.cloud.api.commands.ListExternalLoadBalancersCmd; +import com.cloud.dc.DataCenter; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.network.rules.FirewallRule; +import com.cloud.offering.NetworkOffering; +import com.cloud.server.api.response.ExternalFirewallResponse; +import com.cloud.server.api.response.ExternalLoadBalancerResponse; +import com.cloud.utils.component.Manager; + +public interface ExternalNetworkManager extends Manager { + + public static class ExternalNetworkDeviceType { + private String _name; + + public static final ExternalNetworkDeviceType F5BigIP = new ExternalNetworkDeviceType("F5BigIP"); + public static final ExternalNetworkDeviceType JuniperSRX = new ExternalNetworkDeviceType("JuniperSRX"); + + public ExternalNetworkDeviceType(String name) { + _name = name; + } + + public String getName() { + return _name; + } + } + + // External Firewall methods + + public Host addExternalFirewall(AddExternalFirewallCmd cmd); + + public boolean deleteExternalFirewall(DeleteExternalFirewallCmd cmd); + + public List listExternalFirewalls(ListExternalFirewallsCmd cmd); + + public ExternalFirewallResponse createExternalFirewallResponse(Host externalFirewall); + + public boolean manageGuestNetworkWithExternalFirewall(boolean add, Network network, NetworkOffering offering) throws ResourceUnavailableException; + + public boolean applyFirewallRules(Network network, List rules) throws ResourceUnavailableException; + + public boolean applyIps(Network network, List ipAddresses) throws ResourceUnavailableException; + + public boolean manageRemoteAccessVpn(boolean create, Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException; + + public boolean manageRemoteAccessVpnUsers(Network network, RemoteAccessVpn vpn, List users) throws ResourceUnavailableException; + + // External Load balancer methods + + public Host addExternalLoadBalancer(AddExternalLoadBalancerCmd cmd); + + public boolean deleteExternalLoadBalancer(DeleteExternalLoadBalancerCmd cmd); + + public List listExternalLoadBalancers(ListExternalLoadBalancersCmd cmd); + + public ExternalLoadBalancerResponse createExternalLoadBalancerResponse(Host externalLoadBalancer); + + public boolean manageGuestNetworkWithExternalLoadBalancer(boolean add, Network guestConfig) throws ResourceUnavailableException; + + public boolean applyLoadBalancerRules(Network network, List rules) throws ResourceUnavailableException; + + // General methods + + public int getVlanOffset(DataCenter zone, int vlanTag); + + public int getGloballyConfiguredCidrSize(); +} diff --git a/server/src/com/cloud/network/ExternalNetworkManagerImpl.java b/server/src/com/cloud/network/ExternalNetworkManagerImpl.java new file mode 100644 index 00000000000..14ed6f9b28d --- /dev/null +++ b/server/src/com/cloud/network/ExternalNetworkManagerImpl.java @@ -0,0 +1,1277 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.network; + +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.ExternalNetworkResourceUsageAnswer; +import com.cloud.agent.api.ExternalNetworkResourceUsageCommand; +import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.LoadBalancerConfigCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetStaticNatRulesCommand; +import com.cloud.agent.api.routing.VpnUsersCfgCommand; +import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.LoadBalancerTO; +import com.cloud.agent.api.to.PortForwardingRuleTO; +import com.cloud.agent.api.to.StaticNatRuleTO; +import com.cloud.api.commands.AddExternalFirewallCmd; +import com.cloud.api.commands.AddExternalLoadBalancerCmd; +import com.cloud.api.commands.DeleteExternalFirewallCmd; +import com.cloud.api.commands.DeleteExternalLoadBalancerCmd; +import com.cloud.api.commands.ListExternalFirewallsCmd; +import com.cloud.api.commands.ListExternalLoadBalancersCmd; +import com.cloud.configuration.Config; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.Vlan; +import com.cloud.dc.VlanVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.VlanDao; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.DetailVO; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.host.dao.HostDetailsDao; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.InlineLoadBalancerNicMapDao; +import com.cloud.network.dao.LoadBalancerDao; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.VpnUserDao; +import com.cloud.network.lb.LoadBalancingRule; +import com.cloud.network.lb.LoadBalancingRule.LbDestination; +import com.cloud.network.resource.F5BigIpResource; +import com.cloud.network.resource.JuniperSrxResource; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.rules.PortForwardingRule; +import com.cloud.network.rules.PortForwardingRuleVO; +import com.cloud.network.rules.StaticNatRule; +import com.cloud.network.rules.StaticNatRuleImpl; +import com.cloud.network.rules.dao.PortForwardingRulesDao; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.resource.ServerResource; +import com.cloud.server.api.response.ExternalFirewallResponse; +import com.cloud.server.api.response.ExternalLoadBalancerResponse; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserContext; +import com.cloud.user.UserStatisticsVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.UserStatisticsDao; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.component.Inject; +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.db.GlobalLock; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.exception.ExecutionException; +import com.cloud.utils.net.NetUtils; +import com.cloud.utils.net.UrlUtil; +import com.cloud.vm.DomainRouterVO; +import com.cloud.vm.Nic.ReservationStrategy; +import com.cloud.vm.Nic.State; +import com.cloud.vm.NicVO; +import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.dao.NicDao; + +@Local(value = {ExternalNetworkManager.class}) +public class ExternalNetworkManagerImpl implements ExternalNetworkManager { + public enum ExternalNetworkResourceName { + JuniperSrx, + F5BigIp; + } + + @Inject AgentManager _agentMgr; + @Inject NetworkManager _networkMgr; + @Inject HostDao _hostDao; + @Inject DataCenterDao _dcDao; + @Inject AccountDao _accountDao; + @Inject DomainRouterDao _routerDao; + @Inject IPAddressDao _ipAddressDao; + @Inject VlanDao _vlanDao; + @Inject UserStatisticsDao _userStatsDao; + @Inject NetworkDao _networkDao; + @Inject PortForwardingRulesDao _portForwardingRulesDao; + @Inject LoadBalancerDao _loadBalancerDao; + @Inject ConfigurationDao _configDao; + @Inject HostDetailsDao _detailsDao; + @Inject NetworkOfferingDao _networkOfferingDao; + @Inject NicDao _nicDao; + @Inject VpnUserDao _vpnUsersDao; + @Inject InlineLoadBalancerNicMapDao _inlineLoadBalancerNicMapDao; + @Inject AccountManager _accountMgr; + + ScheduledExecutorService _executor; + int _externalNetworkStatsInterval; + + private static final org.apache.log4j.Logger s_logger = Logger.getLogger(ExternalNetworkManagerImpl.class); + protected String _name; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + _externalNetworkStatsInterval = NumbersUtil.parseInt(_configDao.getValue(Config.RouterStatsInterval.key()), 300); + if (_externalNetworkStatsInterval > 0){ + _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("ExternalNetworkMonitor")); + } + return true; + } + + @Override + public boolean start() { + if (_externalNetworkStatsInterval > 0){ + _executor.scheduleAtFixedRate(new ExternalNetworkUsageTask(), _externalNetworkStatsInterval, _externalNetworkStatsInterval, TimeUnit.SECONDS); + } + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + public String getExternalNetworkResourceGuid(long zoneId, ExternalNetworkResourceName name, String ip) { + return zoneId + "-" + name + "-" + ip; + } + + protected HostVO getExternalNetworkAppliance(long zoneId, Host.Type type) { + DataCenterVO zone = _dcDao.findById(zoneId); + if (!_networkMgr.zoneIsConfiguredForExternalNetworking(zoneId)) { + s_logger.debug("Zone " + zone.getName() + " is not configured for external networking."); + return null; + } else { + List externalNetworkAppliancesInZone = _hostDao.listBy(type, zoneId); + if (externalNetworkAppliancesInZone.size() != 1) { + return null; + } else { + return externalNetworkAppliancesInZone.get(0); + } + } + } + + @Override + public Host addExternalLoadBalancer(AddExternalLoadBalancerCmd cmd) { + long zoneId = cmd.getZoneId(); + ServerResource resource =null; + String guid; + String deviceType; + + DataCenterVO zone = _dcDao.findById(zoneId); + String zoneName; + if (zone == null) { + throw new InvalidParameterValueException("Could not find zone with ID: " + zoneId); + } else { + zoneName = zone.getName(); + } + + List externalLoadBalancersInZone = _hostDao.listByTypeDataCenter(Host.Type.ExternalLoadBalancer, zoneId); + if (externalLoadBalancersInZone.size() != 0) { + throw new InvalidParameterValueException("Already found an external load balancer in zone: " + zoneName); + } + + URI uri; + try { + uri = new URI(cmd.getUrl()); + } catch (Exception e) { + s_logger.debug(e); + throw new InvalidParameterValueException(e.getMessage()); + } + + String ipAddress = uri.getHost(); + String username = cmd.getUsername(); + String password = cmd.getPassword(); + Map params = new HashMap(); + UrlUtil.parseQueryParameters(uri.getQuery(), true, params); + String publicInterface = params.get("publicinterface"); + String privateInterface = params.get("privateinterface"); + String numRetries = params.get("numretries"); + boolean inline = Boolean.parseBoolean(params.get("inline")); + + if (publicInterface == null) { + throw new InvalidParameterValueException("Please specify a public interface."); + } + + if (privateInterface == null) { + throw new InvalidParameterValueException("Please specify a private interface."); + } + + if (numRetries == null) { + numRetries = "1"; + } + + deviceType = cmd.getType(); + if (deviceType ==null) { + deviceType = ExternalNetworkDeviceType.F5BigIP.getName(); //default it to F5 for now + } + + if (deviceType.equalsIgnoreCase(ExternalNetworkDeviceType.F5BigIP.getName())) { + resource = new F5BigIpResource(); + guid = getExternalNetworkResourceGuid(zoneId, ExternalNetworkResourceName.F5BigIp, ipAddress); + } else { + throw new CloudRuntimeException("An unsupported networt device type is added as external load balancer."); + } + + + Map hostDetails = new HashMap(); + hostDetails.put("zoneId", String.valueOf(zoneId)); + hostDetails.put("ip", ipAddress); + hostDetails.put("username", username); + hostDetails.put("password", password); + hostDetails.put("publicInterface", publicInterface); + hostDetails.put("privateInterface", privateInterface); + hostDetails.put("numRetries", numRetries); + hostDetails.put("guid", guid); + hostDetails.put("name", guid); + hostDetails.put("inline", String.valueOf(inline)); + + try { + resource.configure(guid, hostDetails); + } catch (ConfigurationException e) { + throw new CloudRuntimeException(e.getMessage()); + } + + Host host = _agentMgr.addHost(zoneId, resource, Host.Type.ExternalLoadBalancer, hostDetails); + if (host != null) { + if (deviceType.equalsIgnoreCase(ExternalNetworkDeviceType.F5BigIP.getName())) { + zone.setLoadBalancerProvider(Network.Provider.F5BigIp.getName()); + } + _dcDao.update(zone.getId(), zone); + return host; + } else { + return null; + } + } + + @Override + public boolean deleteExternalLoadBalancer(DeleteExternalLoadBalancerCmd cmd) { + long hostId = cmd.getId(); + User caller = _accountMgr.getActiveUser(UserContext.current().getCallerUserId()); + HostVO externalLoadBalancer = _hostDao.findById(hostId); + if (externalLoadBalancer == null) { + throw new InvalidParameterValueException("Could not find an external load balancer with ID: " + hostId); + } + + try { + if (_agentMgr.maintain(hostId) && _agentMgr.deleteHost(hostId, false, false, caller)) { + DataCenterVO zone = _dcDao.findById(externalLoadBalancer.getDataCenterId()); + + if (zone.getNetworkType().equals(NetworkType.Advanced)) { + zone.setLoadBalancerProvider(Network.Provider.VirtualRouter.getName()); + } else if (zone.getNetworkType().equals(NetworkType.Basic)) { + zone.setLoadBalancerProvider(null); + } + + return _dcDao.update(zone.getId(), zone); + } else { + return false; + } + } catch (AgentUnavailableException e) { + s_logger.debug(e); + return false; + } + } + + @Override + public List listExternalLoadBalancers(ListExternalLoadBalancersCmd cmd) { + long zoneId = cmd.getZoneId(); + return _hostDao.listByTypeDataCenter(Host.Type.ExternalLoadBalancer, zoneId); + } + + @Override + public ExternalLoadBalancerResponse createExternalLoadBalancerResponse(Host externalLoadBalancer) { + Map lbDetails = _detailsDao.findDetails(externalLoadBalancer.getId()); + ExternalLoadBalancerResponse response = new ExternalLoadBalancerResponse(); + response.setId(externalLoadBalancer.getId()); + response.setIpAddress(externalLoadBalancer.getPrivateIpAddress()); + response.setUsername(lbDetails.get("username")); + response.setPublicInterface(lbDetails.get("publicInterface")); + response.setPrivateInterface(lbDetails.get("privateInterface")); + response.setNumRetries(lbDetails.get("numRetries")); + return response; + } + + @Override + public boolean manageGuestNetworkWithExternalLoadBalancer(boolean add, Network guestConfig) throws ResourceUnavailableException { + if (guestConfig.getTrafficType() != TrafficType.Guest) { + s_logger.trace("External load balancer can only be user for add/remove guest networks."); + return false; + } + + // Find the external load balancer in this zone + long zoneId = guestConfig.getDataCenterId(); + DataCenterVO zone = _dcDao.findById(zoneId); + HostVO externalLoadBalancer = getExternalNetworkAppliance(zoneId, Host.Type.ExternalLoadBalancer); + + if (externalLoadBalancer == null) { + return false; + } + + // Send a command to the external load balancer to implement or shutdown the guest network + long guestVlanTag = Long.parseLong(guestConfig.getBroadcastUri().getHost()); + String selfIp = NetUtils.long2Ip(NetUtils.ip2Long(guestConfig.getGateway()) + 1); + String guestVlanNetmask = NetUtils.cidr2Netmask(guestConfig.getCidr()); + Integer networkRate = _networkMgr.getNetworkRate(guestConfig.getId(), null); + + IpAddressTO ip = new IpAddressTO(guestConfig.getAccountId(), null, add, false, true, String.valueOf(guestVlanTag), selfIp, guestVlanNetmask, null, null, networkRate, false); + IpAddressTO[] ips = new IpAddressTO[1]; + ips[0] = ip; + IpAssocCommand cmd = new IpAssocCommand(ips); + Answer answer = _agentMgr.easySend(externalLoadBalancer.getId(), cmd); + + if (answer == null || !answer.getResult()) { + String action = add ? "implement" : "shutdown"; + String answerDetails = (answer != null) ? answer.getDetails() : "answer was null"; + String msg = "External load balancer was unable to " + action + " the guest network on the external load balancer in zone " + zone.getName() + " due to " + answerDetails; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, zoneId); + } + + List reservedIpAddressesForGuestNetwork = _nicDao.listIpAddressInNetwork(guestConfig.getId()); + if (add && (!reservedIpAddressesForGuestNetwork.contains(selfIp))) { + // Insert a new NIC for this guest network to reserve the self IP + savePlaceholderNic(guestConfig, selfIp); + } + + Account account = _accountDao.findByIdIncludingRemoved(guestConfig.getAccountId()); + String action = add ? "implemented" : "shut down"; + s_logger.debug("External load balancer has " + action + " the guest network for account " + account.getAccountName() + "(id = " + account.getAccountId() + ") with VLAN tag " + guestVlanTag); + + return true; + } + + @Override + public boolean applyLoadBalancerRules(Network network, List rules) throws ResourceUnavailableException { + // Find the external load balancer in this zone + long zoneId = network.getDataCenterId(); + DataCenterVO zone = _dcDao.findById(zoneId); + HostVO externalLoadBalancer = getExternalNetworkAppliance(zoneId, Host.Type.ExternalLoadBalancer); + + if (externalLoadBalancer == null) { + return false; + } + + // If the load balancer is inline, find the external firewall in this zone + boolean externalLoadBalancerIsInline = externalLoadBalancerIsInline(externalLoadBalancer); + HostVO externalFirewall = null; + if (externalLoadBalancerIsInline) { + externalFirewall = getExternalNetworkAppliance(zoneId, Host.Type.ExternalFirewall); + if (externalFirewall == null) { + String msg = "External load balancer in zone " + zone.getName() + " is inline, but no external firewall in this zone."; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, network.getDataCenterId()); + } + } + + if (network.getState() == Network.State.Allocated) { + s_logger.debug("External load balancer was asked to apply LB rules for network with ID " + network.getId() + "; this network is not implemented. Skipping backend commands."); + return true; + } + + List loadBalancingRules = new ArrayList(); + + for (FirewallRule rule : rules) { + if (rule.getPurpose().equals(Purpose.LoadBalancing)) { + loadBalancingRules.add((LoadBalancingRule) rule); + } + } + + List loadBalancersToApply = new ArrayList(); + for (int i = 0; i < loadBalancingRules.size(); i++) { + LoadBalancingRule rule = loadBalancingRules.get(i); + + boolean revoked = (rule.getState().equals(FirewallRule.State.Revoke)); + String protocol = rule.getProtocol(); + String algorithm = rule.getAlgorithm(); + String srcIp = _networkMgr.getIp(rule.getSourceIpAddressId()).getAddress().addr(); + int srcPort = rule.getSourcePortStart(); + List destinations = rule.getDestinations(); + List sourceCidrs = rule.getSourceCidrList(); + + if (externalLoadBalancerIsInline) { + InlineLoadBalancerNicMapVO mapping = _inlineLoadBalancerNicMapDao.findByPublicIpAddress(srcIp); + NicVO loadBalancingIpNic = null; + if (!revoked) { + if (mapping == null) { + // Acquire a new guest IP address and save it as the load balancing IP address + String loadBalancingIpAddress = _networkMgr.acquireGuestIpAddress(network, null); + + if (loadBalancingIpAddress == null) { + String msg = "Ran out of guest IP addresses."; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, network.getDataCenterId()); + } + + // If a NIC doesn't exist for the load balancing IP address, create one + loadBalancingIpNic = _nicDao.findByIp4Address(loadBalancingIpAddress); + if (loadBalancingIpNic == null) { + loadBalancingIpNic = savePlaceholderNic(network, loadBalancingIpAddress); + } + + // Save a mapping between the source IP address and the load balancing IP address NIC + mapping = new InlineLoadBalancerNicMapVO(rule.getId(), srcIp, loadBalancingIpNic.getId()); + _inlineLoadBalancerNicMapDao.persist(mapping); + + // On the external firewall, create a static NAT rule between the source IP address and the load balancing IP address + applyStaticNatRuleForInlineLBRule(zone, network, externalFirewall, revoked, srcIp, loadBalancingIpNic.getIp4Address()); + } else { + loadBalancingIpNic = _nicDao.findById(mapping.getNicId()); + } + } else { + if (mapping != null) { + // Find the NIC that the mapping refers to + loadBalancingIpNic = _nicDao.findById(mapping.getNicId()); + + // On the external firewall, delete the static NAT rule between the source IP address and the load balancing IP address + applyStaticNatRuleForInlineLBRule(zone, network, externalFirewall, revoked, srcIp, loadBalancingIpNic.getIp4Address()); + + // Delete the mapping between the source IP address and the load balancing IP address + _inlineLoadBalancerNicMapDao.expunge(mapping.getId()); + + // Delete the NIC + _nicDao.expunge(loadBalancingIpNic.getId()); + } else { + s_logger.debug("Revoking a rule for an inline load balancer that has not been programmed yet."); + continue; + } + } + + // Change the source IP address for the load balancing rule to be the load balancing IP address + srcIp = loadBalancingIpNic.getIp4Address(); + } + + if (destinations != null && !destinations.isEmpty()) { + LoadBalancerTO loadBalancer = new LoadBalancerTO(srcIp, srcPort, protocol, algorithm, revoked, false, destinations); + loadBalancersToApply.add(loadBalancer); + } + } + + if (loadBalancersToApply.size() > 0) { + int numLoadBalancersForCommand = loadBalancersToApply.size(); + LoadBalancerTO[] loadBalancersForCommand = loadBalancersToApply.toArray(new LoadBalancerTO[numLoadBalancersForCommand]); + LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(loadBalancersForCommand); + long guestVlanTag = Integer.parseInt(network.getBroadcastUri().getHost()); + cmd.setAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG, String.valueOf(guestVlanTag)); + Answer answer = _agentMgr.easySend(externalLoadBalancer.getId(), cmd); + if (answer == null || !answer.getResult()) { + String details = (answer != null) ? answer.getDetails() : "details unavailable"; + String msg = "Unable to apply load balancer rules to the F5 BigIp appliance in zone " + zone.getName() + " due to: " + details + "."; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, network.getDataCenterId()); + } + } + + return true; + } + + @Override + public Host addExternalFirewall(AddExternalFirewallCmd cmd) { + long zoneId = cmd.getZoneId(); + String deviceType; + + DataCenterVO zone = _dcDao.findById(zoneId); + String zoneName; + if (zone == null) { + throw new InvalidParameterValueException("Could not find zone with ID: " + zoneId); + } else { + zoneName = zone.getName(); + } + + List externalFirewallsInZone = _hostDao.listByTypeDataCenter(Host.Type.ExternalFirewall, zoneId); + if (externalFirewallsInZone.size() != 0) { + throw new InvalidParameterValueException("Already added an external firewall in zone: " + zoneName); + } + + URI uri; + try { + uri = new URI(cmd.getUrl()); + } catch (Exception e) { + s_logger.debug(e); + throw new InvalidParameterValueException(e.getMessage()); + } + + String ipAddress = uri.getHost(); + String username = cmd.getUsername(); + String password = cmd.getPassword(); + Map params = new HashMap(); + UrlUtil.parseQueryParameters(uri.getQuery(), true, params); + String publicInterface = params.get("publicinterface"); + String usageInterface = params.get("usageinterface"); + String privateInterface = params.get("privateinterface"); + String publicZone = params.get("publiczone"); + String privateZone = params.get("privatezone"); + String numRetries = params.get("numretries"); + String timeout = params.get("timeout"); + ServerResource resource; + String guid; + + if (publicInterface == null) { + throw new InvalidParameterValueException("Please specify a public interface."); + } + + if (usageInterface != null) { + if (!usageInterface.contains(".")) { + usageInterface += ".0"; + } + } + + if (privateInterface != null) { + if (privateInterface.contains(".")) { + throw new InvalidParameterValueException("The private interface name must not have a unit identifier."); + } + } else { + throw new InvalidParameterValueException("Please specify a private interface."); + } + + if (publicZone == null) { + publicZone = "untrust"; + } + + if (privateZone == null) { + privateZone = "trust"; + } + + if (numRetries == null) { + numRetries = "1"; + } + + if (timeout == null) { + timeout = "300"; + } + + deviceType = cmd.getType(); + if (deviceType ==null) { + deviceType = ExternalNetworkDeviceType.JuniperSRX.getName(); //default it to Juniper for now + } + if (deviceType.equalsIgnoreCase(ExternalNetworkDeviceType.JuniperSRX.getName())) { + resource = new JuniperSrxResource(); + guid = getExternalNetworkResourceGuid(zoneId, ExternalNetworkResourceName.JuniperSrx, ipAddress); + } else { + throw new CloudRuntimeException("An unsupported networt device type is added as external firewall."); + } + + Map hostDetails = new HashMap(); + hostDetails.put("zoneId", String.valueOf(zoneId)); + hostDetails.put("ip", ipAddress); + hostDetails.put("username", username); + hostDetails.put("password", password); + hostDetails.put("publicInterface", publicInterface); + hostDetails.put("privateInterface", privateInterface); + hostDetails.put("publicZone", publicZone); + hostDetails.put("privateZone", privateZone); + hostDetails.put("numRetries", numRetries); + hostDetails.put("timeout", timeout); + hostDetails.put("guid", guid); + hostDetails.put("name", guid); + + if (usageInterface != null) { + hostDetails.put("usageInterface", usageInterface); + } + + try { + resource.configure(guid, hostDetails); + } catch (ConfigurationException e) { + throw new CloudRuntimeException(e.getMessage()); + } + + Host externalFirewall = _agentMgr.addHost(zoneId, resource, Host.Type.ExternalFirewall, hostDetails); + if (externalFirewall != null) { + zone.setFirewallProvider(Network.Provider.JuniperSRX.getName()); + zone.setUserDataProvider(Network.Provider.DhcpServer.getName()); + zone.setVpnProvider(null); + + if (zone.getGatewayProvider() == null || !zone.getGatewayProvider().equals(Network.Provider.ExternalGateWay)) { + zone.setGatewayProvider(Network.Provider.JuniperSRX.getName()); + } + + if (zone.getDnsProvider() == null || !zone.getDnsProvider().equals(Network.Provider.ExternalDhcpServer)) { + zone.setDnsProvider(Network.Provider.DhcpServer.getName()); + } + + if (zone.getDhcpProvider() == null || !zone.getDhcpProvider().equals(Network.Provider.ExternalDhcpServer)) { + zone.setDhcpProvider(Network.Provider.DhcpServer.getName()); + } + + if (zone.getLoadBalancerProvider() == null || !zone.getLoadBalancerProvider().equals(Network.Provider.F5BigIp.getName())) { + zone.setLoadBalancerProvider(Network.Provider.None.getName()); + } + + _dcDao.update(zone.getId(), zone); + return externalFirewall; + } else { + return null; + } + } + + @Override + public boolean deleteExternalFirewall(DeleteExternalFirewallCmd cmd) { + long hostId = cmd.getId(); + User caller = _accountMgr.getActiveUser(UserContext.current().getCallerUserId()); + HostVO externalFirewall = _hostDao.findById(hostId); + if (externalFirewall == null) { + throw new InvalidParameterValueException("Could not find an external firewall with ID: " + hostId); + } + + try { + if (_agentMgr.maintain(hostId) && _agentMgr.deleteHost(hostId, false, false, caller)) { + DataCenterVO zone = _dcDao.findById(externalFirewall.getDataCenterId()); + zone.setFirewallProvider(Network.Provider.VirtualRouter.getName()); + zone.setUserDataProvider(Network.Provider.VirtualRouter.getName()); + zone.setVpnProvider(Network.Provider.VirtualRouter.getName()); + + if (zone.getGatewayProvider() != null && !zone.getGatewayProvider().equals(Network.Provider.ExternalGateWay)) { + zone.setGatewayProvider(Network.Provider.VirtualRouter.getName()); + } + + if (zone.getDnsProvider() != null && !zone.getDnsProvider().equals(Network.Provider.ExternalDhcpServer)) { + zone.setDnsProvider(Network.Provider.VirtualRouter.getName()); + } + + if (zone.getDhcpProvider() != null && !zone.getDhcpProvider().equals(Network.Provider.ExternalDhcpServer)) { + zone.setDhcpProvider(Network.Provider.VirtualRouter.getName()); + } + + if (zone.getLoadBalancerProvider() != null && zone.getLoadBalancerProvider().equals(Network.Provider.None)) { + if (zone.getNetworkType().equals(NetworkType.Advanced)) { + zone.setLoadBalancerProvider(Network.Provider.VirtualRouter.getName()); + } else if (zone.getNetworkType().equals(NetworkType.Basic)) { + zone.setLoadBalancerProvider(null); + } + } + + return _dcDao.update(zone.getId(), zone); + } else { + return false; + } + } catch (AgentUnavailableException e) { + s_logger.debug(e); + return false; + } + } + + @Override + public List listExternalFirewalls(ListExternalFirewallsCmd cmd) { + long zoneId = cmd.getZoneId(); + return _hostDao.listByTypeDataCenter(Host.Type.ExternalFirewall, zoneId); + } + + @Override + public ExternalFirewallResponse createExternalFirewallResponse(Host externalFirewall) { + Map fwDetails = _detailsDao.findDetails(externalFirewall.getId()); + ExternalFirewallResponse response = new ExternalFirewallResponse(); + response.setId(externalFirewall.getId()); + response.setIpAddress(externalFirewall.getPrivateIpAddress()); + response.setUsername(fwDetails.get("username")); + response.setPublicInterface(fwDetails.get("publicInterface")); + response.setUsageInterface(fwDetails.get("usageInterface")); + response.setPrivateInterface(fwDetails.get("privateInterface")); + response.setPublicZone(fwDetails.get("publicZone")); + response.setPrivateZone(fwDetails.get("privateZone")); + response.setNumRetries(fwDetails.get("numRetries")); + response.setTimeout(fwDetails.get("timeout")); + return response; + } + + @Override + public boolean manageGuestNetworkWithExternalFirewall(boolean add, Network network, NetworkOffering offering) throws ResourceUnavailableException { + if (network.getTrafficType() != TrafficType.Guest) { + s_logger.trace("External firewall can only be used for add/remove guest networks."); + return false; + } + + // Find the external firewall in this zone + long zoneId = network.getDataCenterId(); + DataCenterVO zone = _dcDao.findById(zoneId); + HostVO externalFirewall = getExternalNetworkAppliance(zoneId, Host.Type.ExternalFirewall); + + if (externalFirewall == null) { + return false; + } + + Account account = _accountDao.findByIdIncludingRemoved(network.getAccountId()); + boolean sharedSourceNat = offering.isSharedSourceNatService(); + IPAddressVO sourceNatIp = null; + if (!sharedSourceNat) { + // Get the source NAT IP address for this network + List sourceNatIps = _networkMgr.listPublicIpAddressesInVirtualNetwork(network.getAccountId(), zoneId, true, null); + + if (sourceNatIps.size() != 1) { + String errorMsg = "External firewall was unable to find the source NAT IP address for account " + account.getAccountName(); + s_logger.error(errorMsg); + return true; + } else { + sourceNatIp = sourceNatIps.get(0); + } + } + + // Send a command to the external firewall to implement or shutdown the guest network + long guestVlanTag = Long.parseLong(network.getBroadcastUri().getHost()); + String guestVlanGateway = network.getGateway(); + String guestVlanCidr = network.getCidr(); + String sourceNatIpAddress = sourceNatIp.getAddress().addr(); + + VlanVO publicVlan = _vlanDao.findById(sourceNatIp.getVlanId()); + String publicVlanTag = publicVlan.getVlanTag(); + + // Get network rate + Integer networkRate = _networkMgr.getNetworkRate(network.getId(), null); + + IpAddressTO ip = new IpAddressTO(account.getAccountId(), sourceNatIpAddress, add, false, !sharedSourceNat, publicVlanTag, null, null, null, null, networkRate, sourceNatIp.isOneToOneNat()); + IpAddressTO[] ips = new IpAddressTO[1]; + ips[0] = ip; + IpAssocCommand cmd = new IpAssocCommand(ips); + cmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_GATEWAY, guestVlanGateway); + cmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR, guestVlanCidr); + cmd.setAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG, String.valueOf(guestVlanTag)); + Answer answer = _agentMgr.easySend(externalFirewall.getId(), cmd); + + if (answer == null || !answer.getResult()) { + String action = add ? "implement" : "shutdown"; + String answerDetails = (answer != null) ? answer.getDetails() : "answer was null"; + String msg = "External firewall was unable to " + action + " the guest network on the external firewall in zone " + zone.getName() + " due to " + answerDetails; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, zoneId); + } + + List reservedIpAddressesForGuestNetwork = _nicDao.listIpAddressInNetwork(network.getId()); + if (add && (!reservedIpAddressesForGuestNetwork.contains(network.getGateway()))) { + // Insert a new NIC for this guest network to reserve the gateway address + savePlaceholderNic(network, network.getGateway()); + } + + // Delete any mappings used for inline external load balancers in this network + List nicsInNetwork = _nicDao.listByNetworkId(network.getId()); + for (NicVO nic : nicsInNetwork) { + InlineLoadBalancerNicMapVO mapping = _inlineLoadBalancerNicMapDao.findByNicId(nic.getId()); + + if (mapping != null) { + _nicDao.expunge(mapping.getNicId()); + _inlineLoadBalancerNicMapDao.expunge(mapping.getId()); + + } + } + + + String action = add ? "implemented" : "shut down"; + s_logger.debug("External firewall has " + action + " the guest network for account " + account.getAccountName() + "(id = " + account.getAccountId() + ") with VLAN tag " + guestVlanTag); + + return true; + } + + @Override + public boolean applyFirewallRules(Network network, List rules) throws ResourceUnavailableException { + // Find the external firewall in this zone + long zoneId = network.getDataCenterId(); + DataCenterVO zone = _dcDao.findById(zoneId); + HostVO externalFirewall = getExternalNetworkAppliance(zoneId, Host.Type.ExternalFirewall); + + if (externalFirewall == null) { + return false; + } + + if (network.getState() == Network.State.Allocated) { + s_logger.debug("External firewall was asked to apply firewall rules for network with ID " + network.getId() + "; this network is not implemented. Skipping backend commands."); + return true; + } + + List staticNatRules = new ArrayList(); + List portForwardingRules = new ArrayList(); + + for (FirewallRule rule : rules) { + IpAddress sourceIp = _networkMgr.getIp(rule.getSourceIpAddressId()); + Vlan vlan = _vlanDao.findById(sourceIp.getVlanId()); + + if (rule.getPurpose() == Purpose.StaticNat) { + StaticNatRule staticNatRule = (StaticNatRule) rule; + StaticNatRuleTO ruleTO = new StaticNatRuleTO(staticNatRule, vlan.getVlanTag(), sourceIp.getAddress().addr(), staticNatRule.getDestIpAddress()); + staticNatRules.add(ruleTO); + } else if (rule.getPurpose() == Purpose.PortForwarding) { + PortForwardingRuleTO ruleTO = new PortForwardingRuleTO((PortForwardingRule) rule, vlan.getVlanTag(), sourceIp.getAddress().addr()); + portForwardingRules.add(ruleTO); + } + } + + // Apply static nat rules + applyStaticNatRules(staticNatRules, zone, externalFirewall.getId()); + + // apply port forwarding rules + applyPortForwardingRules(portForwardingRules, zone, externalFirewall.getId()); + + return true; + } + + protected void applyStaticNatRules(List staticNatRules, DataCenter zone, long externalFirewallId) throws ResourceUnavailableException { + if (!staticNatRules.isEmpty()) { + SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(staticNatRules); + Answer answer = _agentMgr.easySend(externalFirewallId, cmd); + if (answer == null || !answer.getResult()) { + String details = (answer != null) ? answer.getDetails() : "details unavailable"; + String msg = "External firewall was unable to apply static nat rules to the SRX appliance in zone " + zone.getName() + " due to: " + details + "."; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, zone.getId()); + } + } + } + + protected void applyPortForwardingRules(List portForwardingRules, DataCenter zone, long externalFirewallId) throws ResourceUnavailableException { + if (!portForwardingRules.isEmpty()) { + SetPortForwardingRulesCommand cmd = new SetPortForwardingRulesCommand(portForwardingRules); + Answer answer = _agentMgr.easySend(externalFirewallId, cmd); + if (answer == null || !answer.getResult()) { + String details = (answer != null) ? answer.getDetails() : "details unavailable"; + String msg = "External firewall was unable to apply port forwarding rules to the SRX appliance in zone " + zone.getName() + " due to: " + details + "."; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, zone.getId()); + } + } + } + + @Override + public boolean applyIps(Network network, List ipAddresses) throws ResourceUnavailableException { + return true; + } + + + public boolean manageRemoteAccessVpn(boolean create, Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException { + HostVO externalFirewall = getExternalNetworkAppliance(network.getDataCenterId(), Host.Type.ExternalFirewall); + + if (externalFirewall == null) { + return false; + } + + // Create/delete VPN + IpAddress ip = _networkMgr.getIp(vpn.getServerAddressId()); + + // Mask the IP range with the network's VLAN tag + String[] ipRange = vpn.getIpRange().split("-"); + DataCenterVO zone = _dcDao.findById(network.getDataCenterId()); + int vlanTag = Integer.parseInt(network.getBroadcastUri().getHost()); + int offset = getVlanOffset(zone, vlanTag); + int cidrSize = getGloballyConfiguredCidrSize(); + + for (int i = 0; i < 2; i++) { + ipRange[i] = NetUtils.long2Ip((NetUtils.ip2Long(ipRange[i]) & 0xff000000) | (offset << (32 - cidrSize))); + } + + String maskedIpRange = ipRange[0] + "-" + ipRange[1]; + + RemoteAccessVpnCfgCommand createVpnCmd = new RemoteAccessVpnCfgCommand(create, ip.getAddress().addr(), vpn.getLocalIp(), maskedIpRange, vpn.getIpsecPresharedKey()); + createVpnCmd.setAccessDetail(NetworkElementCommand.ACCOUNT_ID, String.valueOf(network.getAccountId())); + createVpnCmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR, network.getCidr()); + Answer answer = _agentMgr.easySend(externalFirewall.getId(), createVpnCmd); + if (answer == null || !answer.getResult()) { + String details = (answer != null) ? answer.getDetails() : "details unavailable"; + String msg = "External firewall was unable to create a remote access VPN in zone " + zone.getName() + " due to: " + details + "."; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, zone.getId()); + } + + // Add/delete users + List vpnUsers = _vpnUsersDao.listByAccount(vpn.getAccountId()); + return manageRemoteAccessVpnUsers(network, vpn, vpnUsers); + } + + public boolean manageRemoteAccessVpnUsers(Network network, RemoteAccessVpn vpn, List vpnUsers) throws ResourceUnavailableException { + HostVO externalFirewall = getExternalNetworkAppliance(network.getDataCenterId(), Host.Type.ExternalFirewall); + + if (externalFirewall == null) { + return false; + } + + List addUsers = new ArrayList(); + List removeUsers = new ArrayList(); + for (VpnUser user : vpnUsers) { + if (user.getState() == VpnUser.State.Add || + user.getState() == VpnUser.State.Active) { + addUsers.add(user); + } else if (user.getState() == VpnUser.State.Revoke) { + removeUsers.add(user); + } + } + + VpnUsersCfgCommand addUsersCmd = new VpnUsersCfgCommand(addUsers, removeUsers); + addUsersCmd.setAccessDetail(NetworkElementCommand.ACCOUNT_ID, String.valueOf(network.getAccountId())); + addUsersCmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR, network.getCidr()); + + Answer answer = _agentMgr.easySend(externalFirewall.getId(), addUsersCmd); + if (answer == null || !answer.getResult()) { + String details = (answer != null) ? answer.getDetails() : "details unavailable"; + DataCenterVO zone = _dcDao.findById(network.getDataCenterId()); + String msg = "External firewall was unable to add remote access users in zone " + zone.getName() + " due to: " + details + "."; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, zone.getId()); + } + + return true; + } + + private void applyStaticNatRuleForInlineLBRule(DataCenterVO zone, Network network, HostVO externalFirewall, boolean revoked, String publicIp, String privateIp) throws ResourceUnavailableException { + List staticNatRules = new ArrayList(); + IPAddressVO ipVO = _ipAddressDao.listByDcIdIpAddress(zone.getId(), publicIp).get(0); + VlanVO vlan = _vlanDao.findById(ipVO.getVlanId()); + FirewallRuleVO fwRule = new FirewallRuleVO(null, ipVO.getId(), -1, -1, "any", network.getId(), network.getAccountId(), network.getDomainId(), Purpose.StaticNat, null, null, null, null); + FirewallRule.State state = !revoked ? FirewallRule.State.Add : FirewallRule.State.Revoke; + fwRule.setState(state); + StaticNatRule rule = new StaticNatRuleImpl(fwRule, privateIp); + StaticNatRuleTO ruleTO = new StaticNatRuleTO(rule, vlan.getVlanTag(), publicIp, privateIp); + staticNatRules.add(ruleTO); + + applyStaticNatRules(staticNatRules, zone, externalFirewall.getId()); + } + + private boolean externalLoadBalancerIsInline(HostVO externalLoadBalancer) { + DetailVO detail = _detailsDao.findDetail(externalLoadBalancer.getId(), "inline"); + return (detail != null && detail.getValue().equals("true")); + } + + public int getVlanOffset(DataCenter zone, int vlanTag) { + if (zone.getVnet() == null) { + throw new CloudRuntimeException("Could not find vlan range for zone " + zone.getName() + "."); + } + + String vlanRange[] = zone.getVnet().split("-"); + int lowestVlanTag = Integer.valueOf(vlanRange[0]); + return vlanTag - lowestVlanTag; + } + + private NicVO savePlaceholderNic(Network network, String ipAddress) { + NicVO nic = new NicVO(null, null, network.getId(), null); + nic.setIp4Address(ipAddress); + nic.setReservationStrategy(ReservationStrategy.PlaceHolder); + nic.setState(State.Reserved); + return _nicDao.persist(nic); + } + + public int getGloballyConfiguredCidrSize() { + try { + String globalVlanBits = _configDao.getValue(Config.GuestVlanBits.key()); + return 8 + Integer.parseInt(globalVlanBits); + } catch (Exception e) { + throw new CloudRuntimeException("Failed to read the globally configured VLAN bits size."); + } + } + + protected class ExternalNetworkUsageTask implements Runnable { + + public ExternalNetworkUsageTask() { + } + + private boolean updateBytes(UserStatisticsVO userStats, long newCurrentBytesSent, long newCurrentBytesReceived) { + long oldNetBytesSent = userStats.getNetBytesSent(); + long oldNetBytesReceived = userStats.getNetBytesReceived(); + long oldCurrentBytesSent = userStats.getCurrentBytesSent(); + long oldCurrentBytesReceived = userStats.getCurrentBytesReceived(); + String warning = "Received an external network stats byte count that was less than the stored value. Zone ID: " + userStats.getDataCenterId() + ", account ID: " + userStats.getAccountId() + "."; + + userStats.setCurrentBytesSent(newCurrentBytesSent); + if (oldCurrentBytesSent > newCurrentBytesSent) { + s_logger.warn(warning + "Stored bytes sent: " + oldCurrentBytesSent + ", new bytes sent: " + newCurrentBytesSent + "."); + userStats.setNetBytesSent(oldNetBytesSent + oldCurrentBytesSent); + } + + userStats.setCurrentBytesReceived(newCurrentBytesReceived); + if (oldCurrentBytesReceived > newCurrentBytesReceived) { + s_logger.warn(warning + "Stored bytes received: " + oldCurrentBytesReceived + ", new bytes received: " + newCurrentBytesReceived + "."); + userStats.setNetBytesReceived(oldNetBytesReceived + oldCurrentBytesReceived); + } + + return _userStatsDao.update(userStats.getId(), userStats); + } + + /* + * Creates a new stats entry for the specified parameters, if one doesn't already exist. + */ + private boolean createStatsEntry(long accountId, long zoneId, long networkId, String publicIp, long hostId) { + HostVO host = _hostDao.findById(hostId); + UserStatisticsVO userStats = _userStatsDao.findBy(accountId, zoneId, networkId, publicIp, hostId, host.getType().toString()); + if (userStats == null) { + return (_userStatsDao.persist(new UserStatisticsVO(accountId, zoneId, publicIp, hostId, host.getType().toString(), networkId)) != null); + } else { + return true; + } + } + + /* + * Updates an existing stats entry with new data from the specified usage answer. + */ + private boolean updateStatsEntry(long accountId, long zoneId, long networkId, String publicIp, long hostId, ExternalNetworkResourceUsageAnswer answer) { + AccountVO account = _accountDao.findById(accountId); + DataCenterVO zone = _dcDao.findById(zoneId); + NetworkVO network = _networkDao.findById(networkId); + HostVO host = _hostDao.findById(hostId); + String statsEntryIdentifier = "account " + account.getAccountName() + ", zone " + zone.getName() + ", network ID " + networkId + ", host ID " + host.getName(); + + long newCurrentBytesSent = 0; + long newCurrentBytesReceived = 0; + + if (publicIp != null) { + long[] bytesSentAndReceived = null; + statsEntryIdentifier += ", public IP: " + publicIp; + + if (host.getType().equals(Host.Type.ExternalLoadBalancer) && externalLoadBalancerIsInline(host)) { + // Look up stats for the guest IP address that's mapped to the public IP address + InlineLoadBalancerNicMapVO mapping = _inlineLoadBalancerNicMapDao.findByPublicIpAddress(publicIp); + + if (mapping != null) { + NicVO nic = _nicDao.findById(mapping.getNicId()); + String loadBalancingIpAddress = nic.getIp4Address(); + bytesSentAndReceived = answer.ipBytes.get(loadBalancingIpAddress); + } + } else { + bytesSentAndReceived = answer.ipBytes.get(publicIp); + } + + if (bytesSentAndReceived == null) { + s_logger.debug("Didn't get an external network usage answer for public IP " + publicIp); + } else { + newCurrentBytesSent += bytesSentAndReceived[0]; + newCurrentBytesReceived += bytesSentAndReceived[1]; + } + } else { + URI broadcastURI = network.getBroadcastUri(); + if (broadcastURI == null) { + s_logger.debug("Not updating stats for guest network with ID " + network.getId() + " because the network is not implemented."); + return true; + } else { + long vlanTag = Integer.parseInt(broadcastURI.getHost()); + long[] bytesSentAndReceived = answer.guestVlanBytes.get(String.valueOf(vlanTag)); + + if (bytesSentAndReceived == null) { + s_logger.warn("Didn't get an external network usage answer for guest VLAN " + vlanTag); + } else { + newCurrentBytesSent += bytesSentAndReceived[0]; + newCurrentBytesReceived += bytesSentAndReceived[1]; + } + } + } + + UserStatisticsVO userStats; + try { + userStats = _userStatsDao.lock(accountId, zoneId, networkId, publicIp, hostId, host.getType().toString()); + } catch (Exception e) { + s_logger.warn("Unable to find user stats entry for " + statsEntryIdentifier); + return false; + } + + if (updateBytes(userStats, newCurrentBytesSent, newCurrentBytesReceived)) { + s_logger.debug("Successfully updated stats for " + statsEntryIdentifier); + return true; + } else { + s_logger.debug("Failed to update stats for " + statsEntryIdentifier); + return false; + } + } + + private boolean createOrUpdateStatsEntry(boolean create, long accountId, long zoneId, long networkId, String publicIp, long hostId, ExternalNetworkResourceUsageAnswer answer) { + if (create) { + return createStatsEntry(accountId, zoneId, networkId, publicIp, hostId); + } else { + return updateStatsEntry(accountId, zoneId, networkId, publicIp, hostId, answer); + } + } + + /* + * Creates/updates all necessary stats entries for an account and zone. + * Stats entries are created for source NAT IP addresses, static NAT rules, port forwarding rules, and load balancing rules + */ + private boolean manageStatsEntries(boolean create, long accountId, long zoneId, + HostVO externalFirewall, ExternalNetworkResourceUsageAnswer firewallAnswer, + HostVO externalLoadBalancer, ExternalNetworkResourceUsageAnswer lbAnswer) { + String accountErrorMsg = "Failed to update external network stats entry. Details: account ID = " + accountId; + Transaction txn = Transaction.open(Transaction.CLOUD_DB); + try { + txn.start(); + + List networksForAccount = _networkDao.listBy(accountId, zoneId, Network.GuestIpType.Virtual); + + for (NetworkVO network : networksForAccount) { + String networkErrorMsg = accountErrorMsg + ", network ID = " + network.getId(); + NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId()); + + if (!offering.isSharedSourceNatService()) { + // Manage the entry for this network's source NAT IP address + List sourceNatIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), true); + if (sourceNatIps.size() == 1) { + String publicIp = sourceNatIps.get(0).getAddress().addr(); + if (!createOrUpdateStatsEntry(create, accountId, zoneId, network.getId(), publicIp, externalFirewall.getId(), firewallAnswer)) { + throw new ExecutionException(networkErrorMsg + ", source NAT IP = " + publicIp); + } + } + + // Manage one entry for each static NAT rule in this network + List staticNatIps = _ipAddressDao.listStaticNatPublicIps(network.getId()); + for (IPAddressVO staticNatIp : staticNatIps) { + String publicIp = staticNatIp.getAddress().addr(); + if (!createOrUpdateStatsEntry(create, accountId, zoneId, network.getId(), publicIp, externalFirewall.getId(), firewallAnswer)) { + throw new ExecutionException(networkErrorMsg + ", static NAT rule public IP = " + publicIp); + } + } + + // Manage one entry for each port forwarding rule in this network + List portForwardingRules = _portForwardingRulesDao.listByNetwork(network.getId()); + for (PortForwardingRuleVO portForwardingRule : portForwardingRules) { + String publicIp = _networkMgr.getIp(portForwardingRule.getSourceIpAddressId()).getAddress().addr(); + if (!createOrUpdateStatsEntry(create, accountId, zoneId, network.getId(), publicIp, externalFirewall.getId(), firewallAnswer)) { + throw new ExecutionException(networkErrorMsg + ", port forwarding rule public IP = " + publicIp); + } + } + } else { + // Manage the account-wide entry for the external firewall + if (!createOrUpdateStatsEntry(create, accountId, zoneId, network.getId(), null, externalFirewall.getId(), firewallAnswer)) { + throw new ExecutionException(networkErrorMsg); + } + } + + // If an external load balancer is added, manage one entry for each load balancing rule in this network + if (externalLoadBalancer != null && lbAnswer != null) { + List loadBalancers = _loadBalancerDao.listByNetworkId(network.getId()); + for (LoadBalancerVO loadBalancer : loadBalancers) { + String publicIp = _networkMgr.getIp(loadBalancer.getSourceIpAddressId()).getAddress().addr(); + if (!createOrUpdateStatsEntry(create, accountId, zoneId, network.getId(), publicIp, externalLoadBalancer.getId(), lbAnswer)) { + throw new ExecutionException(networkErrorMsg + ", load balancing rule public IP = " + publicIp); + } + } + } + + } + + return txn.commit(); + } catch (Exception e) { + s_logger.warn("Exception: ", e); + txn.rollback(); + return false; + } finally { + txn.close(); + } + } + + private void runExternalNetworkUsageTask() { + s_logger.debug("External network stats collector is running..."); + for (DataCenterVO zone : _dcDao.listAll()) { + // Make sure the zone is configured for external networking + if (!_networkMgr.zoneIsConfiguredForExternalNetworking(zone.getId())) { + s_logger.debug("Zone " + zone.getName() + " is not configured for external networking, so skipping usage check."); + continue; + } + + // Only collect stats if there is an external firewall in this zone + HostVO externalFirewall = getExternalNetworkAppliance(zone.getId(), Host.Type.ExternalFirewall); + HostVO externalLoadBalancer = getExternalNetworkAppliance(zone.getId(), Host.Type.ExternalLoadBalancer); + + if (externalFirewall == null) { + s_logger.debug("Skipping usage check for zone " + zone.getName()); + continue; + } + + s_logger.debug("Collecting external network stats for zone " + zone.getName()); + + ExternalNetworkResourceUsageCommand cmd = new ExternalNetworkResourceUsageCommand(); + + // Get network stats from the external firewall + ExternalNetworkResourceUsageAnswer firewallAnswer = (ExternalNetworkResourceUsageAnswer) _agentMgr.easySend(externalFirewall.getId(), cmd); + if (firewallAnswer == null || !firewallAnswer.getResult()) { + String details = (firewallAnswer != null) ? firewallAnswer.getDetails() : "details unavailable"; + String msg = "Unable to get external firewall stats for " + zone.getName() + " due to: " + details + "."; + s_logger.error(msg); + continue; + } + + ExternalNetworkResourceUsageAnswer lbAnswer = null; + if (externalLoadBalancer != null) { + // Get network stats from the external load balancer + lbAnswer = (ExternalNetworkResourceUsageAnswer) _agentMgr.easySend(externalLoadBalancer.getId(), cmd); + if (lbAnswer == null || !lbAnswer.getResult()) { + String details = (lbAnswer != null) ? lbAnswer.getDetails() : "details unavailable"; + String msg = "Unable to get external load balancer stats for " + zone.getName() + " due to: " + details + "."; + s_logger.error(msg); + } + } + + List domainRoutersInZone = _routerDao.listByDataCenter(zone.getId()); + for (DomainRouterVO domainRouter : domainRoutersInZone) { + long accountId = domainRouter.getAccountId(); + long zoneId = domainRouter.getDataCenterIdToDeployIn(); + + AccountVO account = _accountDao.findById(accountId); + if (account == null) { + s_logger.debug("Skipping stats update for account with ID " + accountId); + continue; + } + + if (!manageStatsEntries(true, accountId, zoneId, externalFirewall, firewallAnswer, externalLoadBalancer, lbAnswer)) { + continue; + } + + manageStatsEntries(false, accountId, zoneId, externalFirewall, firewallAnswer, externalLoadBalancer, lbAnswer); + } + } + } + + @Override + public void run() { + GlobalLock scanLock = GlobalLock.getInternLock("ExternalNetworkManagerImpl"); + try { + if (scanLock.lock(20)) { + try { + runExternalNetworkUsageTask(); + } finally { + scanLock.unlock(); + } + } + } catch (Exception e) { + s_logger.warn("Problems while getting external network usage", e); + } finally { + scanLock.releaseRef(); + } + } + } +} diff --git a/server/src/com/cloud/network/F5BigIpManagerImpl.java b/server/src/com/cloud/network/F5BigIpManagerImpl.java new file mode 100644 index 00000000000..28781e618e0 --- /dev/null +++ b/server/src/com/cloud/network/F5BigIpManagerImpl.java @@ -0,0 +1,307 @@ +/** + * Copyright (C) 2011 Cloud.com, Inc. All rights reserved. + */ + +package com.cloud.network; + +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.LoadBalancerConfigCommand; +import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.LoadBalancerTO; +import com.cloud.api.commands.AddExternalLoadBalancerCmd; +import com.cloud.api.commands.DeleteExternalLoadBalancerCmd; +import com.cloud.api.commands.ListExternalLoadBalancersCmd; +import com.cloud.configuration.ConfigurationManager; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.lb.LoadBalancingRule; +import com.cloud.network.lb.LoadBalancingRule.LbDestination; +import com.cloud.network.resource.F5BigIpResource; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.server.api.response.ExternalLoadBalancerResponse; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.User; +import com.cloud.user.UserContext; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.component.Inject; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.NetUtils; +import com.cloud.utils.net.UrlUtil; +import com.cloud.vm.Nic; +import com.cloud.vm.Nic.ReservationStrategy; +import com.cloud.vm.NicVO; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.UserVmDao; + +@Local(value = { ExternalLoadBalancerManager.class }) +public class F5BigIpManagerImpl extends ExternalNetworkManagerImpl implements ExternalLoadBalancerManager { + + @Inject + NetworkManager _networkMgr; + @Inject + NicDao _nicDao; + @Inject + AccountDao _accountDao; + @Inject + DataCenterDao _dcDao; + @Inject + UserVmDao _vmDao; + @Inject + ConfigurationManager _configMgr; + @Inject + AccountManager _accountMgr; + + private static final org.apache.log4j.Logger s_logger = Logger.getLogger(F5BigIpManagerImpl.class); + + @Override + public Host addExternalLoadBalancer(AddExternalLoadBalancerCmd cmd) { + long zoneId = cmd.getZoneId(); + + DataCenterVO zone = _dcDao.findById(zoneId); + String zoneName; + if (zone == null) { + throw new InvalidParameterValueException("Could not find zone with ID: " + zoneId); + } else { + zoneName = zone.getName(); + } + + List externalLoadBalancersInZone = _hostDao.listByTypeDataCenter(Host.Type.ExternalLoadBalancer, zoneId); + if (externalLoadBalancersInZone.size() != 0) { + throw new InvalidParameterValueException("Already found an external load balancer in zone: " + zoneName); + } + + URI uri; + try { + uri = new URI(cmd.getUrl()); + } catch (Exception e) { + s_logger.debug(e); + throw new InvalidParameterValueException(e.getMessage()); + } + + String ipAddress = uri.getHost(); + String username = cmd.getUsername(); + String password = cmd.getPassword(); + Map params = new HashMap(); + UrlUtil.parseQueryParameters(uri.getQuery(), true, params); + String publicInterface = params.get("publicinterface"); + String privateInterface = params.get("privateinterface"); + String numRetries = params.get("numretries"); + + if (publicInterface == null) { + throw new InvalidParameterValueException("Please specify a public interface."); + } + + if (privateInterface == null) { + throw new InvalidParameterValueException("Please specify a private interface."); + } + + if (numRetries == null) { + numRetries = "1"; + } + + F5BigIpResource resource = new F5BigIpResource(); + String guid = getExternalNetworkResourceGuid(zoneId, ExternalNetworkResourceName.F5BigIp, ipAddress); + + Map hostDetails = new HashMap(); + hostDetails.put("zoneId", String.valueOf(zoneId)); + hostDetails.put("ip", ipAddress); + hostDetails.put("username", username); + hostDetails.put("password", password); + hostDetails.put("publicInterface", publicInterface); + hostDetails.put("privateInterface", privateInterface); + hostDetails.put("numRetries", numRetries); + hostDetails.put("guid", guid); + hostDetails.put("name", guid); + + try { + resource.configure(guid, hostDetails); + } catch (ConfigurationException e) { + throw new CloudRuntimeException(e.getMessage()); + } + + Host host = _agentMgr.addHost(zoneId, resource, Host.Type.ExternalLoadBalancer, hostDetails); + if (host != null) { + zone.setLoadBalancerProvider(Network.Provider.F5BigIp.getName()); + _dcDao.update(zone.getId(), zone); + return host; + } else { + return null; + } + } + + @Override + public boolean deleteExternalLoadBalancer(DeleteExternalLoadBalancerCmd cmd) { + long hostId = cmd.getId(); + User caller = _accountMgr.getActiveUser(UserContext.current().getCallerUserId()); + HostVO externalLoadBalancer = _hostDao.findById(hostId); + if (externalLoadBalancer == null) { + throw new InvalidParameterValueException("Could not find an external load balancer with ID: " + hostId); + } + + try { + if (_agentMgr.maintain(hostId) && _agentMgr.deleteHost(hostId, false, false, caller)) { + DataCenterVO zone = _dcDao.findById(externalLoadBalancer.getDataCenterId()); + + if (zone.getNetworkType().equals(NetworkType.Advanced)) { + zone.setLoadBalancerProvider(Network.Provider.VirtualRouter.getName()); + } else if (zone.getNetworkType().equals(NetworkType.Basic)) { + zone.setLoadBalancerProvider(null); + } + + return _dcDao.update(zone.getId(), zone); + } else { + return false; + } + } catch (AgentUnavailableException e) { + s_logger.debug(e); + return false; + } + } + + @Override + public List listExternalLoadBalancers(ListExternalLoadBalancersCmd cmd) { + long zoneId = cmd.getZoneId(); + return _hostDao.listByTypeDataCenter(Host.Type.ExternalLoadBalancer, zoneId); + } + + @Override + public ExternalLoadBalancerResponse getApiResponse(Host externalLoadBalancer) { + Map lbDetails = _detailsDao.findDetails(externalLoadBalancer.getId()); + ExternalLoadBalancerResponse response = new ExternalLoadBalancerResponse(); + response.setId(externalLoadBalancer.getId()); + response.setIpAddress(externalLoadBalancer.getPrivateIpAddress()); + response.setUsername(lbDetails.get("username")); + response.setPublicInterface(lbDetails.get("publicInterface")); + response.setPrivateInterface(lbDetails.get("privateInterface")); + response.setNumRetries(lbDetails.get("numRetries")); + return response; + } + + @Override + public boolean manageGuestNetwork(boolean add, Network guestConfig) throws ResourceUnavailableException { + if (guestConfig.getTrafficType() != TrafficType.Guest) { + s_logger.trace("F5BigIpManager can only add/remove guest networks."); + return false; + } + + // Find the external load balancer in this zone + long zoneId = guestConfig.getDataCenterId(); + DataCenterVO zone = _dcDao.findById(zoneId); + HostVO externalLoadBalancer = getExternalNetworkAppliance(zoneId, Host.Type.ExternalLoadBalancer); + + if (externalLoadBalancer == null) { + return false; + } + + // Send a command to the external load balancer to implement or shutdown the guest network + long guestVlanTag = Long.parseLong(guestConfig.getBroadcastUri().getHost()); + String selfIp = NetUtils.long2Ip(NetUtils.ip2Long(guestConfig.getGateway()) + 1); + String guestVlanNetmask = NetUtils.cidr2Netmask(guestConfig.getCidr()); + Integer networkRate = _networkMgr.getNetworkRate(guestConfig.getId(), null); + + IpAddressTO ip = new IpAddressTO(guestConfig.getAccountId(), null, add, false, true, String.valueOf(guestVlanTag), selfIp, guestVlanNetmask, null, null, networkRate, false); + IpAddressTO[] ips = new IpAddressTO[1]; + ips[0] = ip; + IpAssocCommand cmd = new IpAssocCommand(ips); + Answer answer = _agentMgr.easySend(externalLoadBalancer.getId(), cmd); + + if (answer == null || !answer.getResult()) { + String action = add ? "implement" : "shutdown"; + String answerDetails = (answer != null) ? answer.getDetails() : "answer was null"; + String msg = "F5BigIpManager was unable to " + action + " the guest network on the external load balancer in zone " + zone.getName() + " due to " + answerDetails; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, zoneId); + } + + List reservedIpAddressesForGuestNetwork = _nicDao.listIpAddressInNetwork(guestConfig.getId()); + if (add && (!reservedIpAddressesForGuestNetwork.contains(selfIp))) { + // Insert a new NIC for this guest network to reserve the self IP + NicVO nic = new NicVO(null, null, guestConfig.getId(), null); + nic.setIp4Address(selfIp); + nic.setReservationStrategy(ReservationStrategy.PlaceHolder); + nic.setState(Nic.State.Reserved); + _nicDao.persist(nic); + } + + Account account = _accountDao.findByIdIncludingRemoved(guestConfig.getAccountId()); + String action = add ? "implemented" : "shut down"; + s_logger.debug("F5BigIpManager has " + action + " the guest network for account " + account.getAccountName() + "(id = " + account.getAccountId() + ") with VLAN tag " + guestVlanTag); + + return true; + } + + @Override + public boolean applyLoadBalancerRules(Network network, List rules) throws ResourceUnavailableException { + // Find the external load balancer in this zone + long zoneId = network.getDataCenterId(); + DataCenterVO zone = _dcDao.findById(zoneId); + HostVO externalLoadBalancer = getExternalNetworkAppliance(zoneId, Host.Type.ExternalLoadBalancer); + + if (externalLoadBalancer == null) { + return false; + } + + if (network.getState() == Network.State.Allocated) { + s_logger.debug("F5BigIpManager was asked to apply LB rules for network with ID " + network.getId() + "; this network is not implemented. Skipping backend commands."); + return true; + } + + List loadBalancingRules = new ArrayList(); + + for (FirewallRule rule : rules) { + if (rule.getPurpose().equals(Purpose.LoadBalancing)) { + loadBalancingRules.add((LoadBalancingRule) rule); + } + } + + LoadBalancerTO[] loadBalancers = new LoadBalancerTO[loadBalancingRules.size()]; + for (int i = 0; i < loadBalancingRules.size(); i++) { + LoadBalancingRule rule = loadBalancingRules.get(i); + + boolean revoked = (rule.getState().equals(FirewallRule.State.Revoke)); + String protocol = rule.getProtocol(); + String algorithm = rule.getAlgorithm(); + String srcIp = _networkMgr.getIp(rule.getSourceIpAddressId()).getAddress().addr(); + int srcPort = rule.getSourcePortStart(); + List destinations = rule.getDestinations(); + + LoadBalancerTO loadBalancer = new LoadBalancerTO(srcIp, srcPort, protocol, algorithm, revoked, false, destinations); + loadBalancers[i] = loadBalancer; + } + + if (loadBalancers.length > 0) { + LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(loadBalancers); + Answer answer = _agentMgr.easySend(externalLoadBalancer.getId(), cmd); + if (answer == null || !answer.getResult()) { + String details = (answer != null) ? answer.getDetails() : "details unavailable"; + String msg = "Unable to apply load balancer rules to the F5 BigIp appliance in zone " + zone.getName() + " due to: " + details + "."; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, network.getDataCenterId()); + } + } + + return true; + } +} diff --git a/server/src/com/cloud/network/JuniperSrxManagerImpl.java b/server/src/com/cloud/network/JuniperSrxManagerImpl.java new file mode 100644 index 00000000000..a461826b2a1 --- /dev/null +++ b/server/src/com/cloud/network/JuniperSrxManagerImpl.java @@ -0,0 +1,425 @@ +/** + * Copyright (C) 2011 Cloud.com, Inc. All rights reserved. + */ + +package com.cloud.network; + +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetStaticNatRulesCommand; +import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.PortForwardingRuleTO; +import com.cloud.agent.api.to.StaticNatRuleTO; +import com.cloud.api.commands.AddExternalFirewallCmd; +import com.cloud.api.commands.DeleteExternalFirewallCmd; +import com.cloud.api.commands.ListExternalFirewallsCmd; +import com.cloud.configuration.ConfigurationManager; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.dao.FirewallRulesDao; +import com.cloud.network.resource.JuniperSrxResource; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.network.rules.PortForwardingRule; +import com.cloud.network.rules.StaticNatRule; +import com.cloud.offering.NetworkOffering; +import com.cloud.server.api.response.ExternalFirewallResponse; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.User; +import com.cloud.user.UserContext; +import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.UserStatisticsDao; +import com.cloud.utils.component.Inject; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.NetUtils; +import com.cloud.utils.net.UrlUtil; +import com.cloud.vm.Nic.ReservationStrategy; +import com.cloud.vm.Nic.State; +import com.cloud.vm.NicVO; +import com.cloud.vm.dao.NicDao; + +@Local(value = { ExternalFirewallManager.class }) +public class JuniperSrxManagerImpl extends ExternalNetworkManagerImpl implements ExternalFirewallManager { + + @Inject + NetworkManager _networkMgr; + @Inject + NicDao _nicDao; + @Inject + AccountDao _accountDao; + @Inject + DataCenterDao _dcDao; + @Inject + FirewallRulesDao _firewallRulesDao; + @Inject + UserStatisticsDao _userStatsDao; + @Inject + ConfigurationManager _configMgr; + @Inject + AccountManager _accountMgr; + + private static final org.apache.log4j.Logger s_logger = Logger.getLogger(JuniperSrxManagerImpl.class); + + @Override + public Host addExternalFirewall(AddExternalFirewallCmd cmd) { + long zoneId = cmd.getZoneId(); + + DataCenterVO zone = _dcDao.findById(zoneId); + String zoneName; + if (zone == null) { + throw new InvalidParameterValueException("Could not find zone with ID: " + zoneId); + } else { + zoneName = zone.getName(); + } + + List externalFirewallsInZone = _hostDao.listByTypeDataCenter(Host.Type.ExternalFirewall, zoneId); + if (externalFirewallsInZone.size() != 0) { + throw new InvalidParameterValueException("Already added an external firewall in zone: " + zoneName); + } + + URI uri; + try { + uri = new URI(cmd.getUrl()); + } catch (Exception e) { + s_logger.debug(e); + throw new InvalidParameterValueException(e.getMessage()); + } + + String ipAddress = uri.getHost(); + String username = cmd.getUsername(); + String password = cmd.getPassword(); + Map params = new HashMap(); + UrlUtil.parseQueryParameters(uri.getQuery(), true, params); + String publicInterface = params.get("publicinterface"); + String usageInterface = params.get("usageinterface"); + String privateInterface = params.get("privateinterface"); + String publicZone = params.get("publiczone"); + String privateZone = params.get("privatezone"); + String numRetries = params.get("numretries"); + String timeout = params.get("timeout"); + + if (publicInterface != null) { + if (!publicInterface.contains(".")) { + publicInterface += ".0"; + } + } else { + throw new InvalidParameterValueException("Please specify a public interface."); + } + + if (usageInterface != null) { + if (!usageInterface.contains(".")) { + usageInterface += ".0"; + } + } + + if (privateInterface != null) { + if (privateInterface.contains(".")) { + throw new InvalidParameterValueException("The private interface name must not have a unit identifier."); + } + } else { + throw new InvalidParameterValueException("Please specify a private interface."); + } + + if (publicZone == null) { + publicZone = "untrust"; + } + + if (privateZone == null) { + privateZone = "trust"; + } + + if (numRetries == null) { + numRetries = "1"; + } + + if (timeout == null) { + timeout = "300"; + } + + JuniperSrxResource resource = new JuniperSrxResource(); + String guid = getExternalNetworkResourceGuid(zoneId, ExternalNetworkResourceName.JuniperSrx, ipAddress); + + Map hostDetails = new HashMap(); + hostDetails.put("zoneId", String.valueOf(zoneId)); + hostDetails.put("ip", ipAddress); + hostDetails.put("username", username); + hostDetails.put("password", password); + hostDetails.put("publicInterface", publicInterface); + hostDetails.put("privateInterface", privateInterface); + hostDetails.put("publicZone", publicZone); + hostDetails.put("privateZone", privateZone); + hostDetails.put("numRetries", numRetries); + hostDetails.put("timeout", timeout); + hostDetails.put("guid", guid); + hostDetails.put("name", guid); + + if (usageInterface != null) { + hostDetails.put("usageInterface", usageInterface); + } + + try { + resource.configure(guid, hostDetails); + } catch (ConfigurationException e) { + throw new CloudRuntimeException(e.getMessage()); + } + + Host externalFirewall = _agentMgr.addHost(zoneId, resource, Host.Type.ExternalFirewall, hostDetails); + if (externalFirewall != null) { + zone.setFirewallProvider(Network.Provider.JuniperSRX.getName()); + zone.setUserDataProvider(Network.Provider.DhcpServer.getName()); + zone.setVpnProvider(null); + + if (zone.getGatewayProvider() == null || !zone.getGatewayProvider().equals(Network.Provider.ExternalGateWay)) { + zone.setGatewayProvider(Network.Provider.JuniperSRX.getName()); + } + + if (zone.getDnsProvider() == null || !zone.getDnsProvider().equals(Network.Provider.ExternalDhcpServer)) { + zone.setDnsProvider(Network.Provider.DhcpServer.getName()); + } + + if (zone.getDhcpProvider() == null || !zone.getDhcpProvider().equals(Network.Provider.ExternalDhcpServer)) { + zone.setDhcpProvider(Network.Provider.DhcpServer.getName()); + } + + if (zone.getLoadBalancerProvider() == null || !zone.getLoadBalancerProvider().equals(Network.Provider.F5BigIp.getName())) { + zone.setLoadBalancerProvider(Network.Provider.None.getName()); + } + + _dcDao.update(zone.getId(), zone); + return externalFirewall; + } else { + return null; + } + } + + @Override + public boolean deleteExternalFirewall(DeleteExternalFirewallCmd cmd) { + long hostId = cmd.getId(); + User caller = _accountMgr.getActiveUser(UserContext.current().getCallerUserId()); + HostVO externalFirewall = _hostDao.findById(hostId); + if (externalFirewall == null) { + throw new InvalidParameterValueException("Could not find an external firewall with ID: " + hostId); + } + + try { + if (_agentMgr.maintain(hostId) && _agentMgr.deleteHost(hostId, false, false, caller)) { + DataCenterVO zone = _dcDao.findById(externalFirewall.getDataCenterId()); + zone.setFirewallProvider(Network.Provider.VirtualRouter.getName()); + zone.setUserDataProvider(Network.Provider.VirtualRouter.getName()); + zone.setVpnProvider(Network.Provider.VirtualRouter.getName()); + + if (zone.getGatewayProvider() != null && !zone.getGatewayProvider().equals(Network.Provider.ExternalGateWay)) { + zone.setGatewayProvider(Network.Provider.VirtualRouter.getName()); + } + + if (zone.getDnsProvider() != null && !zone.getDnsProvider().equals(Network.Provider.ExternalDhcpServer)) { + zone.setDnsProvider(Network.Provider.VirtualRouter.getName()); + } + + if (zone.getDhcpProvider() != null && !zone.getDhcpProvider().equals(Network.Provider.ExternalDhcpServer)) { + zone.setDhcpProvider(Network.Provider.VirtualRouter.getName()); + } + + if (zone.getLoadBalancerProvider() != null && zone.getLoadBalancerProvider().equals(Network.Provider.None)) { + if (zone.getNetworkType().equals(NetworkType.Advanced)) { + zone.setLoadBalancerProvider(Network.Provider.VirtualRouter.getName()); + } else if (zone.getNetworkType().equals(NetworkType.Basic)) { + zone.setLoadBalancerProvider(null); + } + } + + return _dcDao.update(zone.getId(), zone); + } else { + return false; + } + } catch (AgentUnavailableException e) { + s_logger.debug(e); + return false; + } + } + + @Override + public List listExternalFirewalls(ListExternalFirewallsCmd cmd) { + long zoneId = cmd.getZoneId(); + return _hostDao.listByTypeDataCenter(Host.Type.ExternalFirewall, zoneId); + } + + @Override + public ExternalFirewallResponse getApiResponse(Host externalFirewall) { + Map fwDetails = _detailsDao.findDetails(externalFirewall.getId()); + ExternalFirewallResponse response = new ExternalFirewallResponse(); + response.setId(externalFirewall.getId()); + response.setIpAddress(externalFirewall.getPrivateIpAddress()); + response.setUsername(fwDetails.get("username")); + response.setPublicInterface(fwDetails.get("publicInterface")); + response.setUsageInterface(fwDetails.get("usageInterface")); + response.setPrivateInterface(fwDetails.get("privateInterface")); + response.setPublicZone(fwDetails.get("publicZone")); + response.setPrivateZone(fwDetails.get("privateZone")); + response.setNumRetries(fwDetails.get("numRetries")); + response.setTimeout(fwDetails.get("timeout")); + return response; + } + + @Override + public boolean manageGuestNetwork(boolean add, Network network, NetworkOffering offering) throws ResourceUnavailableException { + if (network.getTrafficType() != TrafficType.Guest) { + s_logger.trace("JuniperSrxManager can only add/remove guest networks."); + return false; + } + + // Find the external firewall in this zone + long zoneId = network.getDataCenterId(); + DataCenterVO zone = _dcDao.findById(zoneId); + HostVO externalFirewall = getExternalNetworkAppliance(zoneId, Host.Type.ExternalFirewall); + + if (externalFirewall == null) { + return false; + } + + Account account = _accountDao.findByIdIncludingRemoved(network.getAccountId()); + boolean sharedSourceNat = offering.isSharedSourceNatService(); + String sourceNatIp = null; + if (!sharedSourceNat) { + // Get the source NAT IP address for this network + List sourceNatIps = _networkMgr.listPublicIpAddressesInVirtualNetwork(network.getAccountId(), zoneId, true, null); + + if (sourceNatIps.size() != 1) { + String errorMsg = "JuniperSrxManager was unable to find the source NAT IP address for account " + account.getAccountName(); + return true; + } else { + sourceNatIp = sourceNatIps.get(0).getAddress().addr(); + } + } + + // Send a command to the external firewall to implement or shutdown the guest network + long guestVlanTag = Long.parseLong(network.getBroadcastUri().getHost()); + String guestVlanGateway = network.getGateway(); + String guestVlanNetmask = NetUtils.cidr2Netmask(network.getCidr()); + + // Get network rate + Integer networkRate = _networkMgr.getNetworkRate(network.getId(), null); + + IpAddressTO ip = new IpAddressTO(network.getAccountId(), sourceNatIp, add, false, !sharedSourceNat, String.valueOf(guestVlanTag), guestVlanGateway, guestVlanNetmask, null, null, networkRate, false); + IpAddressTO[] ips = new IpAddressTO[1]; + ips[0] = ip; + IpAssocCommand cmd = new IpAssocCommand(ips); + Answer answer = _agentMgr.easySend(externalFirewall.getId(), cmd); + + if (answer == null || !answer.getResult()) { + String action = add ? "implement" : "shutdown"; + String answerDetails = (answer != null) ? answer.getDetails() : "answer was null"; + String msg = "JuniperSrxManager was unable to " + action + " the guest network on the external firewall in zone " + zone.getName() + " due to " + answerDetails; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, zoneId); + } + + List reservedIpAddressesForGuestNetwork = _nicDao.listIpAddressInNetwork(network.getId()); + if (add && (!reservedIpAddressesForGuestNetwork.contains(network.getGateway()))) { + // Insert a new NIC for this guest network to reserve the gateway address + NicVO nic = new NicVO(null, null, network.getId(), null); + nic.setIp4Address(network.getGateway()); + nic.setReservationStrategy(ReservationStrategy.PlaceHolder); + nic.setState(State.Reserved); + _nicDao.persist(nic); + } + + String action = add ? "implemented" : "shut down"; + s_logger.debug("JuniperSrxManager has " + action + " the guest network for account " + account.getAccountName() + "(id = " + account.getAccountId() + ") with VLAN tag " + guestVlanTag); + + return true; + } + + @Override + public boolean applyFirewallRules(Network network, List rules) throws ResourceUnavailableException { + // Find the external firewall in this zone + long zoneId = network.getDataCenterId(); + DataCenterVO zone = _dcDao.findById(zoneId); + HostVO externalFirewall = getExternalNetworkAppliance(zoneId, Host.Type.ExternalFirewall); + + if (externalFirewall == null) { + return false; + } + + if (network.getState() == Network.State.Allocated) { + s_logger.debug("JuniperSrxManager was asked to apply firewall rules for network with ID " + network.getId() + "; this network is not implemented. Skipping backend commands."); + return true; + } + + List staticNatRules = new ArrayList(); + List portForwardingRules = new ArrayList(); + + for (FirewallRule rule : rules) { + IpAddress sourceIp = _networkMgr.getIp(rule.getSourceIpAddressId()); + if (rule.getPurpose() == Purpose.StaticNat) { + StaticNatRule staticNatRule = (StaticNatRule) rule; + StaticNatRuleTO ruleTO = new StaticNatRuleTO(staticNatRule, sourceIp.getAddress().addr(), staticNatRule.getDestIpAddress()); + staticNatRules.add(ruleTO); + } else if (rule.getPurpose() == Purpose.PortForwarding) { + PortForwardingRuleTO ruleTO = new PortForwardingRuleTO((PortForwardingRule) rule, null, sourceIp.getAddress().addr()); + portForwardingRules.add(ruleTO); + } + } + + // Apply static nat rules + applyStaticNatRules(staticNatRules, zone, externalFirewall.getId()); + + // apply port forwarding rules + applyPortForwardingRules(portForwardingRules, zone, externalFirewall.getId()); + + return true; + } + + protected void applyStaticNatRules(List staticNatRules, DataCenter zone, long externalFirewallId) throws ResourceUnavailableException { + if (!staticNatRules.isEmpty()) { + SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(staticNatRules); + Answer answer = _agentMgr.easySend(externalFirewallId, cmd); + if (answer == null || !answer.getResult()) { + String details = (answer != null) ? answer.getDetails() : "details unavailable"; + String msg = "JuniperSrxManager was unable to apply static nat rules to the SRX appliance in zone " + zone.getName() + " due to: " + details + "."; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, zone.getId()); + } + } + } + + protected void applyPortForwardingRules(List portForwardingRules, DataCenter zone, long externalFirewallId) throws ResourceUnavailableException { + if (!portForwardingRules.isEmpty()) { + SetPortForwardingRulesCommand cmd = new SetPortForwardingRulesCommand(portForwardingRules); + Answer answer = _agentMgr.easySend(externalFirewallId, cmd); + if (answer == null || !answer.getResult()) { + String details = (answer != null) ? answer.getDetails() : "details unavailable"; + String msg = "JuniperSrxManager was unable to apply port forwarding rules to the SRX appliance in zone " + zone.getName() + " due to: " + details + "."; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, zone.getId()); + } + } + } + + @Override + public boolean applyIps(Network network, List ipAddresses) throws ResourceUnavailableException { + return true; + } + +} diff --git a/server/src/com/cloud/network/NetworkDeviceManager.java b/server/src/com/cloud/network/NetworkDeviceManager.java new file mode 100644 index 00000000000..666bc10b1d4 --- /dev/null +++ b/server/src/com/cloud/network/NetworkDeviceManager.java @@ -0,0 +1,35 @@ +package com.cloud.network; + +import java.util.List; + +import com.cloud.api.commands.AddNetworkDeviceCmd; +import com.cloud.api.commands.DeleteNetworkDeviceCmd; +import com.cloud.api.commands.ListNetworkDeviceCmd; +import com.cloud.host.Host; +import com.cloud.server.api.response.NetworkDeviceResponse; +import com.cloud.utils.component.Manager; + +public interface NetworkDeviceManager extends Manager { + public static class NetworkDeviceType { + private String _name; + + public static final NetworkDeviceType ExternalDhcp = new NetworkDeviceType("ExternalDhcp"); + public static final NetworkDeviceType PxeServer = new NetworkDeviceType("PxeServer"); + + public NetworkDeviceType(String name) { + _name = name; + } + + public String getName() { + return _name; + } + } + + public Host addNetworkDevice(AddNetworkDeviceCmd cmd); + + public NetworkDeviceResponse getApiResponse(Host device); + + public List listNetworkDevice(ListNetworkDeviceCmd cmd); + + public boolean deleteNetworkDevice(DeleteNetworkDeviceCmd cmd); +} diff --git a/server/src/com/cloud/network/NetworkDeviceManagerImpl.java b/server/src/com/cloud/network/NetworkDeviceManagerImpl.java new file mode 100644 index 00000000000..222c6f2f3a6 --- /dev/null +++ b/server/src/com/cloud/network/NetworkDeviceManagerImpl.java @@ -0,0 +1,193 @@ +package com.cloud.network; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.ServerApiException; +import com.cloud.api.commands.AddNetworkDeviceCmd; +import com.cloud.api.commands.DeleteNetworkDeviceCmd; +import com.cloud.api.commands.ListNetworkDeviceCmd; +import com.cloud.baremetal.ExternalDhcpManager; +import com.cloud.baremetal.PxeServerManager; +import com.cloud.baremetal.PxeServerManager.PxeServerType; +import com.cloud.baremetal.PxeServerProfile; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.server.api.response.NetworkDeviceResponse; +import com.cloud.server.api.response.NwDeviceDhcpResponse; +import com.cloud.server.api.response.PxePingResponse; +import com.cloud.utils.component.Inject; +import com.cloud.utils.exception.CloudRuntimeException; + +@Local(value={NetworkDeviceManager.class}) +public class NetworkDeviceManagerImpl implements NetworkDeviceManager { + public static final Logger s_logger = Logger.getLogger(NetworkDeviceManagerImpl.class); + String _name; + @Inject ExternalDhcpManager _dhcpMgr; + @Inject PxeServerManager _pxeMgr; + @Inject HostDao _hostDao; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public Host addNetworkDevice(AddNetworkDeviceCmd cmd) { + Map paramList = cmd.getParamList(); + if (paramList == null) { + throw new CloudRuntimeException("Parameter list is null"); + } + + Collection paramsCollection = paramList.values(); + HashMap params = (HashMap) (paramsCollection.toArray())[0]; + if (cmd.getType().equalsIgnoreCase(NetworkDeviceType.ExternalDhcp.getName())) { + Long zoneId = Long.parseLong((String) params.get(ApiConstants.ZONE_ID)); + Long podId = Long.parseLong((String)params.get(ApiConstants.POD_ID)); + String type = (String) params.get(ApiConstants.DHCP_SERVER_TYPE); + String url = (String) params.get(ApiConstants.URL); + String username = (String) params.get(ApiConstants.USERNAME); + String password = (String) params.get(ApiConstants.PASSWORD); + + return _dhcpMgr.addDhcpServer(zoneId, podId, type, url, username, password); + } else if (cmd.getType().equalsIgnoreCase(NetworkDeviceType.PxeServer.getName())) { + Long zoneId = Long.parseLong((String) params.get(ApiConstants.ZONE_ID)); + Long podId = Long.parseLong((String)params.get(ApiConstants.POD_ID)); + String type = (String) params.get(ApiConstants.PXE_SERVER_TYPE); + String url = (String) params.get(ApiConstants.URL); + String username = (String) params.get(ApiConstants.USERNAME); + String password = (String) params.get(ApiConstants.PASSWORD); + String pingStorageServerIp = (String) params.get(ApiConstants.PING_STORAGE_SERVER_IP); + String pingDir = (String) params.get(ApiConstants.PING_DIR); + String tftpDir = (String) params.get(ApiConstants.TFTP_DIR); + String pingCifsUsername = (String) params.get(ApiConstants.PING_CIFS_USERNAME); + String pingCifsPassword = (String) params.get(ApiConstants.PING_CIFS_PASSWORD); + PxeServerProfile profile = new PxeServerProfile(zoneId, podId, url, username, password, type, pingStorageServerIp, pingDir, tftpDir, + pingCifsUsername, pingCifsPassword); + return _pxeMgr.addPxeServer(profile); + + } else { + throw new CloudRuntimeException("Unsupported network device type:" + cmd.getType()); + } + } + + @Override + public NetworkDeviceResponse getApiResponse(Host device) { + NetworkDeviceResponse response; + HostVO host = (HostVO)device; + _hostDao.loadDetails(host); + if (host.getType() == Host.Type.ExternalDhcp) { + NwDeviceDhcpResponse r = new NwDeviceDhcpResponse(); + r.setZoneId(host.getDataCenterId()); + r.setPodId(host.getPodId()); + r.setUrl(host.getPrivateIpAddress()); + r.setType(host.getDetail("type")); + response = r; + } else if (host.getType() == Host.Type.PxeServer) { + String pxeType = host.getDetail("type"); + if (pxeType.equalsIgnoreCase(PxeServerType.PING.getName())) { + PxePingResponse r = new PxePingResponse(); + r.setZoneId(host.getDataCenterId()); + r.setPodId(host.getPodId()); + r.setUrl(host.getPrivateIpAddress()); + r.setType(pxeType); + r.setStorageServerIp(host.getDetail("storageServer")); + r.setPingDir(host.getDetail("pingDir")); + r.setTftpDir(host.getDetail("tftpDir")); + response = r; + } else { + throw new CloudRuntimeException("Unsupported PXE server type:" + pxeType); + } + } else { + throw new CloudRuntimeException("Unsupported network device type:" + host.getType()); + } + + response.setId(device.getId()); + return response; + } + + private List listNetworkDevice(Long zoneId, Long podId, Host.Type type) { + List res = new ArrayList(); + if (podId != null) { + List devs = _hostDao.listBy(type, null, podId, zoneId); + if (devs.size() == 1) { + res.add(devs.get(0)); + } else { + s_logger.debug("List " + type + ": " + devs.size() + " found"); + } + } else { + List devs = _hostDao.listBy(type, zoneId); + res.addAll(devs); + } + + return res; + } + + @Override + public List listNetworkDevice(ListNetworkDeviceCmd cmd) { + Map paramList = cmd.getParamList(); + if (paramList == null) { + throw new CloudRuntimeException("Parameter list is null"); + } + + List res; + Collection paramsCollection = paramList.values(); + HashMap params = (HashMap) (paramsCollection.toArray())[0]; + if (NetworkDeviceType.ExternalDhcp.getName().equalsIgnoreCase(cmd.getType())) { + Long zoneId = Long.parseLong((String) params.get(ApiConstants.ZONE_ID)); + Long podId = Long.parseLong((String)params.get(ApiConstants.POD_ID)); + res = listNetworkDevice(zoneId, podId, Host.Type.ExternalDhcp); + } else if (NetworkDeviceType.PxeServer.getName().equalsIgnoreCase(cmd.getType())) { + Long zoneId = Long.parseLong((String) params.get(ApiConstants.ZONE_ID)); + Long podId = Long.parseLong((String)params.get(ApiConstants.POD_ID)); + res = listNetworkDevice(zoneId, podId, Host.Type.PxeServer); + } else if (cmd.getType() == null){ + Long zoneId = Long.parseLong((String) params.get(ApiConstants.ZONE_ID)); + Long podId = Long.parseLong((String)params.get(ApiConstants.POD_ID)); + List res1 = listNetworkDevice(zoneId, podId, Host.Type.PxeServer); + List res2 = listNetworkDevice(zoneId, podId, Host.Type.ExternalDhcp); + List res3 = new ArrayList(); + res3.addAll(res1); + res3.addAll(res2); + res = res3; + } else { + throw new CloudRuntimeException("Unknown network device type:" + cmd.getType()); + } + + return res; + } + + @Override + public boolean deleteNetworkDevice(DeleteNetworkDeviceCmd cmd) { + // TODO Auto-generated method stub + return true; + } +} diff --git a/server/src/com/cloud/network/NetworkUsageManager.java b/server/src/com/cloud/network/NetworkUsageManager.java new file mode 100644 index 00000000000..701beded181 --- /dev/null +++ b/server/src/com/cloud/network/NetworkUsageManager.java @@ -0,0 +1,44 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.network; + +import java.util.List; + +import com.cloud.api.commands.AddTrafficMonitorCmd; +import com.cloud.api.commands.DeleteTrafficMonitorCmd; +import com.cloud.api.commands.ListTrafficMonitorsCmd; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.server.api.response.TrafficMonitorResponse; +import com.cloud.utils.component.Manager; + +public interface NetworkUsageManager extends Manager { + + Host addTrafficMonitor(AddTrafficMonitorCmd cmd); + + TrafficMonitorResponse getApiResponse(Host trafficMonitor); + + boolean deleteTrafficMonitor(DeleteTrafficMonitorCmd cmd); + + List listTrafficMonitors(ListTrafficMonitorsCmd cmd); + + List listAllocatedDirectIps(long zoneId); + +} diff --git a/server/src/com/cloud/network/NetworkUsageManagerImpl.java b/server/src/com/cloud/network/NetworkUsageManagerImpl.java new file mode 100644 index 00000000000..d2a98f4caf5 --- /dev/null +++ b/server/src/com/cloud/network/NetworkUsageManagerImpl.java @@ -0,0 +1,502 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.network; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.Listener; +import com.cloud.agent.api.AgentControlAnswer; +import com.cloud.agent.api.AgentControlCommand; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.DirectNetworkUsageAnswer; +import com.cloud.agent.api.DirectNetworkUsageCommand; +import com.cloud.agent.api.RecurringNetworkUsageCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupTrafficMonitorCommand; +import com.cloud.api.commands.AddTrafficMonitorCmd; +import com.cloud.api.commands.DeleteTrafficMonitorCmd; +import com.cloud.api.commands.ListTrafficMonitorsCmd; +import com.cloud.configuration.Config; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.event.EventTypes; +import com.cloud.event.UsageEventVO; +import com.cloud.event.dao.UsageEventDao; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.DetailVO; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.host.dao.HostDao; +import com.cloud.host.dao.HostDetailsDao; +import com.cloud.network.Network.GuestIpType; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.resource.TrafficSentinelResource; +import com.cloud.server.api.response.TrafficMonitorResponse; +import com.cloud.usage.UsageIPAddressVO; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserContext; +import com.cloud.user.UserStatisticsVO; +import com.cloud.user.dao.UserStatisticsDao; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GlobalLock; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.MacAddress; + +@Local(value = {NetworkUsageManager.class}) +public class NetworkUsageManagerImpl implements NetworkUsageManager { + public enum NetworkUsageResourceName { + TrafficSentinel; + } + + private static final org.apache.log4j.Logger s_logger = Logger.getLogger(NetworkUsageManagerImpl.class); + protected String _name; + @Inject HostDao _hostDao; + @Inject AgentManager _agentMgr; + @Inject IPAddressDao _ipAddressDao; + @Inject UserStatisticsDao _statsDao; + @Inject ConfigurationDao _configDao; + @Inject UsageEventDao _eventDao; + @Inject DataCenterDao _dcDao; + @Inject HostDetailsDao _detailsDao; + @Inject AccountManager _accountMgr; + @Inject NetworkDao _networksDao = null; + ScheduledExecutorService _executor; + int _networkStatsInterval; + protected SearchBuilder AllocatedIpSearch; + + @Override + public Host addTrafficMonitor(AddTrafficMonitorCmd cmd) { + + long zoneId = cmd.getZoneId(); + + DataCenterVO zone = _dcDao.findById(zoneId); + String zoneName; + if (zone == null) { + throw new InvalidParameterValueException("Could not find zone with ID: " + zoneId); + } else { + zoneName = zone.getName(); + } + + + List trafficMonitorsInZone = _hostDao.listByTypeDataCenter(Host.Type.TrafficMonitor, zoneId); + if (trafficMonitorsInZone.size() != 0) { + throw new InvalidParameterValueException("Already added an traffic monitor in zone: " + zoneName); + } + + URI uri; + try { + uri = new URI(cmd.getUrl()); + } catch (Exception e) { + s_logger.debug(e); + throw new InvalidParameterValueException(e.getMessage()); + } + + String ipAddress = uri.getHost(); + //String numRetries = params.get("numretries"); + //String timeout = params.get("timeout"); + + TrafficSentinelResource resource = new TrafficSentinelResource(); + String guid = getTrafficMonitorGuid(zoneId, NetworkUsageResourceName.TrafficSentinel, ipAddress); + + Map hostParams = new HashMap(); + hostParams.put("zone", String.valueOf(zoneId)); + hostParams.put("ipaddress", ipAddress); + hostParams.put("url", cmd.getUrl()); + //hostParams("numRetries", numRetries); + //hostParams("timeout", timeout); + hostParams.put("guid", guid); + hostParams.put("name", guid); + + try { + resource.configure(guid, hostParams); + } catch (ConfigurationException e) { + throw new CloudRuntimeException(e.getMessage()); + } + + Map hostDetails = new HashMap(); + hostDetails.put("url", cmd.getUrl()); + hostDetails.put("last_collection", ""+System.currentTimeMillis()); + + Host trafficMonitor = _agentMgr.addHost(zoneId, resource, Host.Type.TrafficMonitor, hostDetails); + return trafficMonitor; + } + + public String getTrafficMonitorGuid(long zoneId, NetworkUsageResourceName name, String ip) { + return zoneId + "-" + name + "-" + ip; + } + + @Override + public boolean deleteTrafficMonitor(DeleteTrafficMonitorCmd cmd) { + long hostId = cmd.getId(); + User caller = _accountMgr.getActiveUser(UserContext.current().getCallerUserId()); + HostVO trafficMonitor = _hostDao.findById(hostId); + if (trafficMonitor == null) { + throw new InvalidParameterValueException("Could not find an traffic monitor with ID: " + hostId); + } + + try { + if (_agentMgr.maintain(hostId) && _agentMgr.deleteHost(hostId, false, false, caller)) { + return true; + } else { + return false; + } + } catch (AgentUnavailableException e) { + s_logger.debug(e); + return false; + } + } + + @Override + public List listTrafficMonitors(ListTrafficMonitorsCmd cmd) { + long zoneId = cmd.getZoneId(); + return _hostDao.listByTypeDataCenter(Host.Type.TrafficMonitor, zoneId); + } + + @Override + public TrafficMonitorResponse getApiResponse(Host trafficMonitor) { + Map tmDetails = _detailsDao.findDetails(trafficMonitor.getId()); + TrafficMonitorResponse response = new TrafficMonitorResponse(); + response.setId(trafficMonitor.getId()); + response.setIpAddress(trafficMonitor.getPrivateIpAddress()); + response.setNumRetries(tmDetails.get("numRetries")); + response.setTimeout(tmDetails.get("timeout")); + return response; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + + AllocatedIpSearch = _ipAddressDao.createSearchBuilder(); + AllocatedIpSearch.and("allocated", AllocatedIpSearch.entity().getAllocatedTime(), Op.NNULL); + AllocatedIpSearch.and("dc", AllocatedIpSearch.entity().getDataCenterId(), Op.EQ); + SearchBuilder networkJoin = _networksDao.createSearchBuilder(); + networkJoin.and("guestType", networkJoin.entity().getGuestType(), Op.EQ); + AllocatedIpSearch.join("network", networkJoin, AllocatedIpSearch.entity().getSourceNetworkId(), networkJoin.entity().getId(), JoinBuilder.JoinType.INNER); + AllocatedIpSearch.done(); + + _networkStatsInterval = NumbersUtil.parseInt(_configDao.getValue(Config.DirectNetworkStatsInterval.key()), 86400); + _agentMgr.registerForHostEvents(new DirectNetworkStatsListener( _networkStatsInterval), true, false, false); + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public List listAllocatedDirectIps(long zoneId) { + SearchCriteria sc = AllocatedIpSearch.create(); + sc.setParameters("dc", zoneId); + sc.setJoinParameters("network", "guestType", GuestIpType.Direct); + return _ipAddressDao.search(sc, null); + } + + protected class DirectNetworkStatsListener implements Listener { + + private int _interval; + + private long mgmtSrvrId = MacAddress.getMacAddress().toLong(); + + protected DirectNetworkStatsListener(int interval) { + _interval = interval; + } + + @Override + public boolean isRecurring() { + return true; + } + + @Override @DB + public boolean processAnswers(long agentId, long seq, Answer[] answers) { + /* + * Do not collect Direct Network usage stats if the Traffic Monitor is not owned by this mgmt server + */ + HostVO host = _hostDao.findById(agentId); + if(host != null) { + if((host.getManagementServerId() == null) || (mgmtSrvrId != host.getManagementServerId())){ + s_logger.warn("Not the owner. Not collecting Direct Network usage from TrafficMonitor : "+agentId); + return false; + } + } else { + s_logger.warn("Agent not found. Not collecting Direct Network usage from TrafficMonitor : "+agentId); + return false; + } + + GlobalLock scanLock = GlobalLock.getInternLock("direct.network.usage.collect"+host.getDataCenterId()); + try { + if (scanLock.lock(10)) { + try { + return collectDirectNetworkUsage(host); + } finally { + scanLock.unlock(); + } + } + } finally { + scanLock.releaseRef(); + } + return false; + } + + private boolean collectDirectNetworkUsage(HostVO host){ + s_logger.debug("Direct Network Usage stats collector is running..."); + + long zoneId = host.getDataCenterId(); + DetailVO lastCollectDetail = _detailsDao.findDetail(host.getId(),"last_collection"); + if(lastCollectDetail == null){ + s_logger.warn("Last collection time not available. Skipping direct usage collection for Traffic Monitor: "+host.getId()); + return false; + } + Date lastCollection = new Date(new Long(lastCollectDetail.getValue())); + + //Get list of IPs currently allocated + List allocatedIps = listAllocatedDirectIps(zoneId); + Calendar rightNow = Calendar.getInstance(); + + // Allow 2 hours for traffic sentinel to populate historical traffic + // This coule be made configurable + + rightNow.add(Calendar.HOUR_OF_DAY, -2); + Date now = rightNow.getTime(); + + if(lastCollection.after(now)){ + s_logger.debug("Current time is less than 2 hours after last collection time : " + lastCollection.toString() + ". Skipping direct network usage collection"); + return false; + } + + //Get IP Assign/Release events from lastCollection time till now + List IpEvents = _eventDao.listDirectIpEvents(lastCollection, now, zoneId); + + Map ipAssigment = new HashMap(); + List IpPartialUsage = new ArrayList(); //Ips which were allocated only for the part of collection duration + List fullDurationIpUsage = new ArrayList(); //Ips which were allocated only for the entire collection duration + + // Use UsageEvents to track the IP assignment + // Add them to IpUsage list with account_id , ip_address, alloc_date, release_date + + for (UsageEventVO IpEvent : IpEvents){ + String address = IpEvent.getResourceName(); + if(EventTypes.EVENT_NET_IP_ASSIGN.equals(IpEvent.getType())){ + ipAssigment.put(address, IpEvent.getCreateDate()); + } else if(EventTypes.EVENT_NET_IP_RELEASE.equals(IpEvent.getType())) { + if(ipAssigment.containsKey(address)){ + Date assigned = ipAssigment.get(address); + ipAssigment.remove(address); + IpPartialUsage.add(new UsageIPAddressVO(IpEvent.getAccountId(), address, assigned, IpEvent.getCreateDate())); + } else{ + // Ip was assigned prior to lastCollection Date + IpPartialUsage.add(new UsageIPAddressVO(IpEvent.getAccountId(), address, lastCollection, IpEvent.getCreateDate())); + } + } + } + + List IpList = new ArrayList() ; + for(IPAddressVO ip : allocatedIps){ + if(ip.getAccountId() == AccountVO.ACCOUNT_ID_SYSTEM){ + //Ignore usage for system account + continue; + } + String address = (ip.getAddress()).toString(); + if(ipAssigment.containsKey(address)){ + // Ip was assigned during the current period but not release till Date now + IpPartialUsage.add(new UsageIPAddressVO(ip.getAccountId(), address, ipAssigment.get(address), now)); + } else { + // Ip was not assigned or released during current period. Consider entire duration for usage calculation (lastCollection to now) + fullDurationIpUsage.add(new UsageIPAddressVO(ip.getAccountId(), address, lastCollection, now)); + //Store just the Ips to send the list as part of DirectNetworkUsageCommand + IpList.add(address); + } + + } + + List collectedStats = new ArrayList(); + + //Get usage for Ips for which were assigned for the entire duration + if(fullDurationIpUsage.size() > 0){ + DirectNetworkUsageCommand cmd = new DirectNetworkUsageCommand(IpList, lastCollection, now); + DirectNetworkUsageAnswer answer = (DirectNetworkUsageAnswer) _agentMgr.easySend(host.getId(), cmd); + if (answer == null || !answer.getResult()) { + String details = (answer != null) ? answer.getDetails() : "details unavailable"; + String msg = "Unable to get network usage stats from " + host.getId() + " due to: " + details + "."; + s_logger.error(msg); + } else { + for(UsageIPAddressVO usageIp : fullDurationIpUsage){ + String publicIp = usageIp.getAddress(); + long[] bytesSentRcvd = answer.get(publicIp); + Long bytesSent = bytesSentRcvd[0]; + Long bytesRcvd = bytesSentRcvd[1]; + if(bytesSent == null || bytesRcvd == null){ + s_logger.debug("Incorrect bytes for IP: "+publicIp); + continue; + } + if(bytesSent == 0L && bytesRcvd == 0L){ + s_logger.trace("Ignore zero bytes for IP: "+publicIp); + continue; + } + UserStatisticsVO stats = new UserStatisticsVO(usageIp.getAccountId(), zoneId, null, null, null, null); + stats.setCurrentBytesSent(bytesSent); + stats.setCurrentBytesReceived(bytesRcvd); + collectedStats.add(stats); + } + } + } + + //Get usage for Ips for which were assigned for part of the duration period + for(UsageIPAddressVO usageIp : IpPartialUsage){ + IpList = new ArrayList() ; + IpList.add(usageIp.getAddress()); + DirectNetworkUsageCommand cmd = new DirectNetworkUsageCommand(IpList, usageIp.getAssigned(), usageIp.getReleased()); + DirectNetworkUsageAnswer answer = (DirectNetworkUsageAnswer) _agentMgr.easySend(host.getId(), cmd); + if (answer == null || !answer.getResult()) { + String details = (answer != null) ? answer.getDetails() : "details unavailable"; + String msg = "Unable to get network usage stats from " + host.getId() + " due to: " + details + "."; + s_logger.error(msg); + } else { + String publicIp = usageIp.getAddress(); + long[] bytesSentRcvd = answer.get(publicIp); + Long bytesSent = bytesSentRcvd[0]; + Long bytesRcvd = bytesSentRcvd[1]; + if(bytesSent == null || bytesRcvd == null){ + s_logger.debug("Incorrect bytes for IP: "+publicIp); + continue; + } + if(bytesSent == 0L && bytesRcvd == 0L){ + s_logger.trace("Ignore zero bytes for IP: "+publicIp); + continue; + } + UserStatisticsVO stats = new UserStatisticsVO(usageIp.getAccountId(), zoneId, null, null, null, null); + stats.setCurrentBytesSent(bytesSent); + stats.setCurrentBytesReceived(bytesRcvd); + collectedStats.add(stats); + + } + } + + //Persist all the stats and last_collection time in a single transaction + Transaction txn = Transaction.open(Transaction.CLOUD_DB); + try { + txn.start(); + for(UserStatisticsVO stat : collectedStats){ + UserStatisticsVO stats = _statsDao.lock(stat.getAccountId(), stat.getDataCenterId(), 0L, null, host.getId(), "DirectNetwork"); + if (stats == null) { + stats = new UserStatisticsVO(stat.getAccountId(), zoneId, null, host.getId(), "DirectNetwork", 0L); + stats.setCurrentBytesSent(stat.getCurrentBytesSent()); + stats.setCurrentBytesReceived(stat.getCurrentBytesReceived()); + _statsDao.persist(stats); + } else { + stats.setCurrentBytesSent(stats.getCurrentBytesSent() + stat.getCurrentBytesSent()); + stats.setCurrentBytesReceived(stats.getCurrentBytesReceived() + stat.getCurrentBytesReceived()); + _statsDao.update(stats.getId(), stats); + } + } + lastCollectDetail.setValue(""+now.getTime()); + _detailsDao.update(lastCollectDetail.getId(), lastCollectDetail); + txn.commit(); + } finally { + txn.close(); + } + + return true; + } + + @Override + public boolean processCommands(long agentId, long seq, Command[] commands) { + return false; + } + + @Override + public AgentControlAnswer processControlCommand(long agentId, AgentControlCommand cmd) { + return null; + } + + @Override + public boolean processDisconnect(long agentId, Status state) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Disconnected called on " + agentId + " with status " + state.toString()); + } + return true; + } + + @Override + public void processConnect(HostVO agent, StartupCommand cmd, boolean forRebalance) { + if (cmd instanceof StartupTrafficMonitorCommand) { + long agentId = agent.getId(); + s_logger.debug("Sending RecurringNetworkUsageCommand to " + agentId); + RecurringNetworkUsageCommand watch = new RecurringNetworkUsageCommand(_interval); + _agentMgr.gatherStats(agentId, watch, this); + } + return; + } + + @Override + public boolean processTimeout(long agentId, long seq) { + return true; + } + + @Override + public int getTimeout() { + return -1; + } + + protected DirectNetworkStatsListener() { + } + + + } + +} diff --git a/server/src/com/cloud/network/element/BareMetalElement.java b/server/src/com/cloud/network/element/BareMetalElement.java new file mode 100644 index 00000000000..52a2ad7b719 --- /dev/null +++ b/server/src/com/cloud/network/element/BareMetalElement.java @@ -0,0 +1,135 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.network.element; + +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.baremetal.ExternalDhcpManager; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.PublicIpAddress; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.StaticNat; +import com.cloud.offering.NetworkOffering; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Transaction; +import com.cloud.vm.NicProfile; +import com.cloud.vm.NicVO; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.NicDao; + +@Local(value=NetworkElement.class) +public class BareMetalElement extends AdapterBase implements NetworkElement { + private static final Logger s_logger = Logger.getLogger(BareMetalElement.class); + @Inject NicDao _nicDao; + @Inject ExternalDhcpManager _dhcpMgr; + + @Override + public Map> getCapabilities() { + return null; + } + + @Override + public Provider getProvider() { + return Provider.ExternalDhcpServer; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + return true; + } + + @Override @DB + public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, + ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + Host host = dest.getHost(); + if (host.getHypervisorType() != HypervisorType.BareMetal) { + return true; + } + + Transaction txn = Transaction.currentTxn(); + txn.start(); + nic.setMacAddress(host.getPrivateMacAddress()); + NicVO vo = _nicDao.findById(nic.getId()); + assert vo != null : "Where ths nic " + nic.getId() + " going???"; + vo.setMacAddress(nic.getMacAddress()); + _nicDao.update(vo.getId(), vo); + txn.commit(); + s_logger.debug("Bare Metal changes mac address of nic " + nic.getId() + " to " + nic.getMacAddress()); + + return _dhcpMgr.addVirtualMachineIntoNetwork(network, nic, vm, dest, context); + } + + @Override + public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean shutdown(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean restart(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, + InsufficientCapacityException { + return true; + } + + @Override + public boolean destroy(Network network) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean applyIps(Network network, List ipAddress) throws ResourceUnavailableException { + return true; + } + + @Override + public boolean applyRules(Network network, List rules) throws ResourceUnavailableException { + return true; + } + + @Override + public boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException { + return false; + } + +} diff --git a/server/src/com/cloud/network/element/CloudZonesNetworkElement.java b/server/src/com/cloud/network/element/CloudZonesNetworkElement.java new file mode 100644 index 00000000000..d58fdb71595 --- /dev/null +++ b/server/src/com/cloud/network/element/CloudZonesNetworkElement.java @@ -0,0 +1,262 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.network.element; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.AgentManager.OnError; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.StartAnswer; +import com.cloud.agent.api.routing.DhcpEntryCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.SavePasswordCommand; +import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.agent.manager.Commands; +import com.cloud.configuration.ConfigurationManager; +import com.cloud.configuration.ZoneConfig; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.NetworkVO; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.GuestIpType; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.NetworkManager; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.PublicIpAddress; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.router.VirtualNetworkApplianceManager; +import com.cloud.network.router.VirtualRouter; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.StaticNat; +import com.cloud.network.vpn.PasswordResetElement; +import com.cloud.offering.NetworkOffering; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.uservm.UserVm; +import com.cloud.utils.PasswordGenerator; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.component.Inject; +import com.cloud.vm.DomainRouterVO; +import com.cloud.vm.NicProfile; +import com.cloud.vm.NicVO; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmManager; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.Nic.ReservationStrategy; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.dao.UserVmDao; + + +@Local(value=NetworkElement.class) +public class CloudZonesNetworkElement extends AdapterBase implements NetworkElement, PasswordResetElement{ + private static final Logger s_logger = Logger.getLogger(CloudZonesNetworkElement.class); + + private static final Map> capabilities = setCapabilities(); + + @Inject NetworkDao _networkConfigDao; + @Inject NetworkManager _networkMgr; + @Inject VirtualNetworkApplianceManager _routerMgr; + @Inject UserVmManager _userVmMgr; + @Inject UserVmDao _userVmDao; + @Inject DomainRouterDao _routerDao; + @Inject ConfigurationManager _configMgr; + @Inject DataCenterDao _dcDao; + @Inject AgentManager _agentManager; + @Inject ServiceOfferingDao _serviceOfferingDao; + + private boolean canHandle(GuestIpType ipType, DeployDestination dest, TrafficType trafficType) { + DataCenterVO dc = (DataCenterVO)dest.getDataCenter(); + + if (dc.getDhcpProvider().equalsIgnoreCase(Provider.ExternalDhcpServer.getName())){ + _dcDao.loadDetails(dc); + String dhcpStrategy = dc.getDetail(ZoneConfig.DhcpStrategy.key()); + if ("external".equalsIgnoreCase(dhcpStrategy)) { + return true; + } + } + + return false; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException, ConcurrentOperationException, InsufficientCapacityException { + if (!canHandle(network.getGuestType(), dest, offering.getTrafficType())) { + return false; + } + + return true; + } + + @Override + public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vmProfile, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { + if (canHandle(network.getGuestType(), dest, network.getTrafficType())) { + + if (vmProfile.getType() != VirtualMachine.Type.User) { + return false; + } + @SuppressWarnings("unchecked") + VirtualMachineProfile uservm = (VirtualMachineProfile)vmProfile; + _userVmDao.loadDetails((UserVmVO) uservm.getVirtualMachine()); + String password = (String)uservm.getParameter(VirtualMachineProfile.Param.VmPassword); + String userData = uservm.getVirtualMachine().getUserData(); + String sshPublicKey = uservm.getVirtualMachine().getDetail("SSH.PublicKey"); + + Commands cmds = new Commands(OnError.Continue); + if (password != null && network.isDefault()) { + final String encodedPassword = PasswordGenerator.rot13(password); + SavePasswordCommand cmd = new SavePasswordCommand(encodedPassword, nic.getIp4Address(), uservm.getVirtualMachine().getHostName()); + cmds.addCommand("password", cmd); + } + String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(uservm.getServiceOfferingId()).getDisplayText(); + String zoneName = _dcDao.findById(network.getDataCenterId()).getName(); + + cmds.addCommand( + "vmdata", + generateVmDataCommand(nic.getIp4Address(), userData, serviceOffering, zoneName, nic.getIp4Address(), uservm.getVirtualMachine().getHostName(), uservm.getVirtualMachine().getInstanceName(), uservm.getId(), sshPublicKey)); + try { + _agentManager.send(dest.getHost().getId(), cmds); + } catch (OperationTimedoutException e) { + s_logger.debug("Unable to send vm data command to host " + dest.getHost()); + return false; + } + Answer dataAnswer = cmds.getAnswer("vmdata"); + if (dataAnswer != null && dataAnswer.getResult()) { + s_logger.info("Sent vm data successfully to vm " + uservm.getVirtualMachine().getInstanceName()); + return true; + } + s_logger.info("Failed to send vm data to vm " + uservm.getVirtualMachine().getInstanceName()); + return false; + } else { + return false; + } + } + + @Override + public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) { + return true; + } + + @Override + public boolean shutdown(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + return false; //assume that the agent will remove userdata etc + } + + @Override + public boolean destroy(Network config) throws ConcurrentOperationException, ResourceUnavailableException{ + return false; //assume that the agent will remove userdata etc + } + + @Override + public boolean applyRules(Network network, List rules) throws ResourceUnavailableException { + return false; + } + + @Override + public boolean applyIps(Network network, List ipAddress) throws ResourceUnavailableException { + return false; + } + + @Override + public boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException { + return false; + } + + + @Override + public Provider getProvider() { + return Provider.ExternalDhcpServer; + } + + @Override + public Map> getCapabilities() { + return capabilities; + } + + private static Map> setCapabilities() { + Map> capabilities = new HashMap>(); + + capabilities.put(Service.UserData, null); + + return capabilities; + } + + @Override + public boolean restart(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException{ + + s_logger.trace("Cloudzones element doesn't handle network restart for the network " + network); + return true; + + } + + @Override + public boolean savePassword(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException{ + s_logger.trace("Cloudzones element doesn't handle saving passwords for " + network); + return true; + } + + private VmDataCommand generateVmDataCommand( String vmPrivateIpAddress, + String userData, String serviceOffering, String zoneName, String guestIpAddress, String vmName, String vmInstanceName, long vmId, String publicKey) { + VmDataCommand cmd = new VmDataCommand(vmPrivateIpAddress, vmName); + + cmd.addVmData("userdata", "user-data", userData); + cmd.addVmData("metadata", "service-offering", serviceOffering); + cmd.addVmData("metadata", "availability-zone", zoneName); + cmd.addVmData("metadata", "local-ipv4", guestIpAddress); + cmd.addVmData("metadata", "local-hostname", vmName); + cmd.addVmData("metadata", "public-ipv4", guestIpAddress); + cmd.addVmData("metadata", "public-hostname", guestIpAddress); + cmd.addVmData("metadata", "instance-id", vmInstanceName); + cmd.addVmData("metadata", "vm-id", String.valueOf(vmId)); + cmd.addVmData("metadata", "public-keys", publicKey); + + return cmd; + } +} diff --git a/server/src/com/cloud/network/element/ExternalDhcpElement.java b/server/src/com/cloud/network/element/ExternalDhcpElement.java new file mode 100644 index 00000000000..c1f1970da9a --- /dev/null +++ b/server/src/com/cloud/network/element/ExternalDhcpElement.java @@ -0,0 +1,148 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.network.element; + +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.baremetal.ExternalDhcpManager; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.Pod; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.network.PublicIpAddress; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.GuestIpType; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.StaticNat; +import com.cloud.network.vpn.PasswordResetElement; +import com.cloud.offering.NetworkOffering; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.component.Inject; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value=NetworkElement.class) +public class ExternalDhcpElement extends AdapterBase implements NetworkElement, PasswordResetElement { + private static final Logger s_logger = Logger.getLogger(ExternalDhcpElement.class); + @Inject ExternalDhcpManager _dhcpMgr; + private boolean canHandle(GuestIpType ipType, DeployDestination dest, TrafficType trafficType) { + DataCenter dc = dest.getDataCenter(); + Pod pod = dest.getPod(); + + if (pod.getExternalDhcp() && dc.getNetworkType() == NetworkType.Basic && trafficType == TrafficType.Guest + && ipType == GuestIpType.Direct) { + s_logger.debug("External DHCP can handle"); + return true; + } + + return false; + } + + @Override + public boolean savePassword(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException { + return true; + } + + @Override + public Map> getCapabilities() { + return null; + } + + @Override + public Provider getProvider() { + return Provider.ExternalDhcpServer; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + if (!canHandle(network.getGuestType(), dest, offering.getTrafficType())) { + return false; + } + return true; + } + + @Override + public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, + ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + Host host = dest.getHost(); + if (host.getHypervisorType() == HypervisorType.BareMetal || !canHandle(network.getGuestType(), dest, network.getTrafficType())) { + //BareMetalElement or DhcpElement handle this + return false; + } + + return _dhcpMgr.addVirtualMachineIntoNetwork(network, nic, vm, dest, context); + } + + @Override + public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean shutdown(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean restart(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, + InsufficientCapacityException { + return true; + } + + @Override + public boolean destroy(Network network) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean applyIps(Network network, List ipAddress) throws ResourceUnavailableException { + return true; + } + + @Override + public boolean applyRules(Network network, List rules) throws ResourceUnavailableException { + return true; + } + + @Override + public boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException { + return false; + } + +} diff --git a/server/src/com/cloud/network/element/ExternalFirewallElement.java b/server/src/com/cloud/network/element/ExternalFirewallElement.java new file mode 100644 index 00000000000..5a79e58f914 --- /dev/null +++ b/server/src/com/cloud/network/element/ExternalFirewallElement.java @@ -0,0 +1,247 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.network.element; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.configuration.ConfigurationManager; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InsufficientNetworkCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.dao.HostDao; +import com.cloud.network.ExternalNetworkManager; +import com.cloud.network.Network; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.NetworkManager; +import com.cloud.network.PublicIpAddress; +import com.cloud.network.RemoteAccessVpn; +import com.cloud.network.VpnUser; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.StaticNat; +import com.cloud.network.vpn.RemoteAccessVpnElement; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.component.Inject; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value=NetworkElement.class) +public class ExternalFirewallElement extends AdapterBase implements NetworkElement, RemoteAccessVpnElement { + + private static final Logger s_logger = Logger.getLogger(ExternalFirewallElement.class); + + private static final Map> capabilities = setCapabilities(); + + @Inject NetworkManager _networkManager; + @Inject ExternalNetworkManager _externalNetworkManager; + @Inject HostDao _hostDao; + @Inject ConfigurationManager _configMgr; + @Inject NetworkOfferingDao _networkOfferingDao; + @Inject NetworkDao _networksDao; + + private boolean canHandle(Network config) { + DataCenter zone = _configMgr.getZone(config.getDataCenterId()); + if ((zone.getNetworkType() == NetworkType.Advanced && config.getGuestType() != Network.GuestIpType.Virtual) || (zone.getNetworkType() == NetworkType.Basic && config.getGuestType() != Network.GuestIpType.Direct)) { + s_logger.trace("Not handling guest ip type = " + config.getGuestType()); + return false; + } + + return _networkManager.zoneIsConfiguredForExternalNetworking(zone.getId()); + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException, ConcurrentOperationException, InsufficientNetworkCapacityException { + DataCenter zone = _configMgr.getZone(network.getDataCenterId()); + + //don't have to implement network is Basic zone + if (zone.getNetworkType() == NetworkType.Basic) { + s_logger.debug("Not handling network implement in zone of type " + NetworkType.Basic); + return false; + } + + if (!canHandle(network)) { + return false; + } + + return _externalNetworkManager.manageGuestNetworkWithExternalFirewall(true, network, offering); + } + + @Override + public boolean prepare(Network config, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientNetworkCapacityException, ResourceUnavailableException { + return true; + } + + @Override + public boolean release(Network config, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) { + return true; + } + + @Override + public boolean shutdown(Network network, ReservationContext context) throws ResourceUnavailableException, ConcurrentOperationException { + DataCenter zone = _configMgr.getZone(network.getDataCenterId()); + NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId()); + + //don't have to implement network is Basic zone + if (zone.getNetworkType() == NetworkType.Basic) { + s_logger.debug("Not handling network shutdown in zone of type " + NetworkType.Basic); + return false; + } + + if (!canHandle(network)) { + return false; + } + + return _externalNetworkManager.manageGuestNetworkWithExternalFirewall(false, network, offering); + } + + @Override + public boolean destroy(Network config) { + return true; + } + + @Override + public boolean applyIps(Network network, List ipAddresses) throws ResourceUnavailableException { + if (!canHandle(network)) { + return false; + } + + return _externalNetworkManager.applyIps(network, ipAddresses); + } + + + @Override + public boolean applyRules(Network config, List rules) throws ResourceUnavailableException { + if (!canHandle(config)) { + return false; + } + + return _externalNetworkManager.applyFirewallRules(config, rules); + } + + @Override + public boolean startVpn(Network config, RemoteAccessVpn vpn) throws ResourceUnavailableException { + if (!canHandle(config)) { + return false; + } + + return _externalNetworkManager.manageRemoteAccessVpn(true, config, vpn); + + } + + @Override + public boolean stopVpn(Network config, RemoteAccessVpn vpn) throws ResourceUnavailableException { + if (!canHandle(config)) { + return false; + } + + return _externalNetworkManager.manageRemoteAccessVpn(false, config, vpn); + } + + @Override + public String[] applyVpnUsers(RemoteAccessVpn vpn, List users) throws ResourceUnavailableException{ + Network config = _networksDao.findById(vpn.getNetworkId()); + + if (!canHandle(config)) { + return null; + } + + boolean result = _externalNetworkManager.manageRemoteAccessVpnUsers(config, vpn, users); + String[] results = new String[users.size()]; + for (int i = 0; i < results.length; i++) { + results[i] = String.valueOf(result); + } + + return results; + } + + @Override + public Provider getProvider() { + return Provider.JuniperSRX; + } + + @Override + public Map> getCapabilities() { + return capabilities; + } + + private static Map> setCapabilities() { + Map> capabilities = new HashMap>(); + + // Set capabilities for Firewall service + Map firewallCapabilities = new HashMap(); + + // Specifies that static NAT rules are supported by this element + firewallCapabilities.put(Capability.StaticNat, "true"); + + // Specifies that NAT rules can be made for either TCP or UDP traffic + firewallCapabilities.put(Capability.SupportedProtocols, "tcp,udp"); + + firewallCapabilities.put(Capability.MultipleIps, "true"); + + // Specifies that this element supports either one source NAT rule per account, or no source NAT rules at all; + // in the latter case a shared interface NAT rule will be used + firewallCapabilities.put(Capability.SupportedSourceNatTypes, "per account, per zone"); + + // Specifies that this element can measure network usage on a per public IP basis + firewallCapabilities.put(Capability.TrafficStatistics, "per public ip"); + + // Specifies that port forwarding rules are supported by this element + firewallCapabilities.put(Capability.PortForwarding, "true"); + + // Specifies supported VPN types + Map vpnCapabilities = new HashMap(); + vpnCapabilities.put(Capability.SupportedVpnTypes, "ipsec"); + capabilities.put(Service.Vpn, vpnCapabilities); + + capabilities.put(Service.Firewall, firewallCapabilities); + capabilities.put(Service.Gateway, null); + + return capabilities; + } + + @Override + public boolean restart(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException{ + return true; + } + + @Override + public boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException { + return false; + } +} + + diff --git a/server/src/com/cloud/network/element/ExternalLoadBalancerElement.java b/server/src/com/cloud/network/element/ExternalLoadBalancerElement.java new file mode 100644 index 00000000000..2e1bd0ae683 --- /dev/null +++ b/server/src/com/cloud/network/element/ExternalLoadBalancerElement.java @@ -0,0 +1,162 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.network.element; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.configuration.ConfigurationManager; +import com.cloud.dc.DataCenter; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InsufficientNetworkCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.ExternalNetworkManager; +import com.cloud.network.Network; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.NetworkManager; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.PublicIpAddress; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.StaticNat; +import com.cloud.offering.NetworkOffering; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.component.Inject; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value=NetworkElement.class) +public class ExternalLoadBalancerElement extends AdapterBase implements NetworkElement { + + private static final Logger s_logger = Logger.getLogger(ExternalLoadBalancerElement.class); + + @Inject NetworkManager _networkManager; + @Inject ExternalNetworkManager _externalNetworkManager; + @Inject ConfigurationManager _configMgr; + + private boolean canHandle(Network config) { + DataCenter zone = _configMgr.getZone(config.getDataCenterId()); + if (config.getGuestType() != Network.GuestIpType.Virtual || config.getTrafficType() != TrafficType.Guest) { + s_logger.trace("Not handling network with guest Type " + config.getGuestType() + " and traffic type " + config.getTrafficType()); + return false; + } + + return (_networkManager.zoneIsConfiguredForExternalNetworking(zone.getId()) && + zone.getLoadBalancerProvider() != null && zone.getLoadBalancerProvider().equals(Network.Provider.F5BigIp.getName())); + } + + @Override + public boolean implement(Network guestConfig, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException, ConcurrentOperationException, InsufficientNetworkCapacityException { + + if (!canHandle(guestConfig)) { + return false; + } + + return _externalNetworkManager.manageGuestNetworkWithExternalLoadBalancer(true, guestConfig); + } + + @Override + public boolean prepare(Network config, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientNetworkCapacityException, ResourceUnavailableException { + return true; + } + + @Override + public boolean release(Network config, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) { + return true; + } + + @Override + public boolean shutdown(Network guestConfig, ReservationContext context) throws ResourceUnavailableException, ConcurrentOperationException { + if (!canHandle(guestConfig)) { + return false; + } + + return _externalNetworkManager.manageGuestNetworkWithExternalLoadBalancer(false, guestConfig); + } + + @Override + public boolean destroy(Network config) { + return true; + } + + @Override + public boolean applyIps(Network network, List ipAddress) throws ResourceUnavailableException { + return true; + } + + @Override + public boolean applyRules(Network config, List rules) throws ResourceUnavailableException { + if (!canHandle(config)) { + return false; + } + + return _externalNetworkManager.applyLoadBalancerRules(config, rules); + } + + @Override + public Map> getCapabilities() { + Map> capabilities = new HashMap>(); + + // Set capabilities for LB service + Map lbCapabilities = new HashMap(); + + // Specifies that the RoundRobin and Leastconn algorithms are supported for load balancing rules + lbCapabilities.put(Capability.SupportedLBAlgorithms, "roundrobin,leastconn"); + + // Specifies that load balancing rules can be made for either TCP or UDP traffic + lbCapabilities.put(Capability.SupportedProtocols, "tcp,udp"); + + // Specifies that this element can measure network usage on a per public IP basis + lbCapabilities.put(Capability.TrafficStatistics, "per public ip"); + + // Specifies that load balancing rules can only be made with public IPs that aren't source NAT IPs + lbCapabilities.put(Capability.LoadBalancingSupportedIps, "additional"); + + capabilities.put(Service.Lb, lbCapabilities); + + return capabilities; + } + + @Override + public Provider getProvider() { + return Provider.F5BigIp; + } + + @Override + public boolean restart(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException{ + return true; + } + + @Override + public boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException { + return false; + } + +} diff --git a/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java b/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java new file mode 100644 index 00000000000..9e16c53eada --- /dev/null +++ b/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java @@ -0,0 +1,270 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.network.guru; + +import java.util.List; + +import javax.ejb.Local; + +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.DataCenter; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.event.EventTypes; +import com.cloud.event.EventUtils; +import com.cloud.event.EventVO; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapcityException; +import com.cloud.network.ExternalNetworkManager; +import com.cloud.network.Network; +import com.cloud.network.Network.State; +import com.cloud.network.NetworkManager; +import com.cloud.network.NetworkVO; +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.ovs.OvsNetworkManager; +import com.cloud.network.ovs.OvsTunnelManager; +import com.cloud.network.rules.PortForwardingRuleVO; +import com.cloud.network.rules.dao.PortForwardingRulesDao; +import com.cloud.offering.NetworkOffering; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.utils.component.Inject; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.Ip; +import com.cloud.utils.net.NetUtils; +import com.cloud.vm.Nic.ReservationStrategy; +import com.cloud.vm.NicProfile; +import com.cloud.vm.NicVO; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value = NetworkGuru.class) +public class ExternalGuestNetworkGuru extends GuestNetworkGuru { + + @Inject + NetworkManager _networkMgr; + @Inject + ExternalNetworkManager _externalNetworkMgr; + @Inject + NetworkDao _networkDao; + @Inject + DataCenterDao _zoneDao; + @Inject + ConfigurationDao _configDao; + @Inject + PortForwardingRulesDao _pfRulesDao; + @Inject + OvsNetworkManager _ovsNetworkMgr; + @Inject + OvsTunnelManager _tunnelMgr; + + @Override + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + + if (_ovsNetworkMgr.isOvsNetworkEnabled() || _tunnelMgr.isOvsTunnelEnabled()) { + return null; + } + + NetworkVO config = (NetworkVO) super.design(offering, plan, userSpecified, owner); + if (config == null) { + return null; + } else if (_networkMgr.zoneIsConfiguredForExternalNetworking(plan.getDataCenterId())) { + config.setState(State.Allocated); + } + + return config; + } + + @Override + public Network implement(Network config, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws InsufficientVirtualNetworkCapcityException { + assert (config.getState() == State.Implementing) : "Why are we implementing " + config; + + if (_ovsNetworkMgr.isOvsNetworkEnabled() || _tunnelMgr.isOvsTunnelEnabled()) { + return null; + } + + if (!_networkMgr.zoneIsConfiguredForExternalNetworking(config.getDataCenterId())) { + return super.implement(config, offering, dest, context); + } + + DataCenter zone = dest.getDataCenter(); + NetworkVO implemented = new NetworkVO(config.getTrafficType(), config.getGuestType(), config.getMode(), config.getBroadcastDomainType(), config.getNetworkOfferingId(), + config.getDataCenterId(), State.Allocated); + + // Get a vlan tag + int vlanTag; + if (config.getBroadcastUri() == null) { + String vnet = _dcDao.allocateVnet(zone.getId(), config.getAccountId(), context.getReservationId()); + + try { + vlanTag = Integer.parseInt(vnet); + } catch (NumberFormatException e) { + throw new CloudRuntimeException("Obtained an invalid guest vlan tag. Exception: " + e.getMessage()); + } + + implemented.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vlanTag)); + EventUtils.saveEvent(UserContext.current().getCallerUserId(), config.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VLAN_ASSIGN, "Assignbed Zone Vlan: "+vnet+ " Network Id: "+config.getId(), 0); + } else { + vlanTag = Integer.parseInt(config.getBroadcastUri().getHost()); + implemented.setBroadcastUri(config.getBroadcastUri()); + } + + // Determine the offset from the lowest vlan tag + int offset = _externalNetworkMgr.getVlanOffset(zone, vlanTag); + + // Determine the new gateway and CIDR + String[] oldCidr = config.getCidr().split("/"); + String oldCidrAddress = oldCidr[0]; + int cidrSize = _externalNetworkMgr.getGloballyConfiguredCidrSize(); + + // If the offset has more bits than there is room for, return null + long bitsInOffset = 32 - Integer.numberOfLeadingZeros(offset); + if (bitsInOffset > (cidrSize - 8)) { + throw new CloudRuntimeException("The offset " + offset + " needs " + bitsInOffset + " bits, but only have " + (cidrSize - 8) + " bits to work with."); + } + + long newCidrAddress = (NetUtils.ip2Long(oldCidrAddress) & 0xff000000) | (offset << (32 - cidrSize)); + implemented.setGateway(NetUtils.long2Ip(newCidrAddress + 1)); + implemented.setCidr(NetUtils.long2Ip(newCidrAddress) + "/" + cidrSize); + implemented.setState(State.Implemented); + + // Mask the Ipv4 address of all nics that use this network with the new guest VLAN offset + List nicsInNetwork = _nicDao.listByNetworkId(config.getId()); + for (NicVO nic : nicsInNetwork) { + if (nic.getIp4Address() != null) { + long ipMask = getIpMask(nic.getIp4Address(), cidrSize); + nic.setIp4Address(NetUtils.long2Ip(newCidrAddress | ipMask)); + _nicDao.persist(nic); + } + } + + // Mask the destination address of all port forwarding rules in this network with the new guest VLAN offset + List pfRulesInNetwork = _pfRulesDao.listByNetwork(config.getId()); + for (PortForwardingRuleVO pfRule : pfRulesInNetwork) { + if (pfRule.getDestinationIpAddress() != null) { + long ipMask = getIpMask(pfRule.getDestinationIpAddress().addr(), cidrSize); + String maskedDestinationIpAddress = NetUtils.long2Ip(newCidrAddress | ipMask); + pfRule.setDestinationIpAddress(new Ip(maskedDestinationIpAddress)); + _pfRulesDao.update(pfRule.getId(), pfRule); + } + } + + return implemented; + } + + @Override + public NicProfile allocate(Network config, NicProfile nic, VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapcityException, + InsufficientAddressCapacityException { + + if (_networkMgr.zoneIsConfiguredForExternalNetworking(config.getDataCenterId()) && nic != null && nic.getRequestedIp() != null) { + throw new CloudRuntimeException("Does not support custom ip allocation at this time: " + nic); + } + + NicProfile profile = super.allocate(config, nic, vm); + + if (_ovsNetworkMgr.isOvsNetworkEnabled() || _tunnelMgr.isOvsTunnelEnabled()) { + return null; + } + + if (_networkMgr.zoneIsConfiguredForExternalNetworking(config.getDataCenterId())) { + profile.setStrategy(ReservationStrategy.Start); + profile.setIp4Address(null); + profile.setGateway(null); + profile.setNetmask(null); + } + + return profile; + } + + @Override + public void deallocate(Network config, NicProfile nic, VirtualMachineProfile vm) { + super.deallocate(config, nic, vm); + + if (_ovsNetworkMgr.isOvsNetworkEnabled() || _tunnelMgr.isOvsTunnelEnabled()) { + return; + } + + if (_networkMgr.zoneIsConfiguredForExternalNetworking(config.getDataCenterId())) { + nic.setIp4Address(null); + nic.setGateway(null); + nic.setNetmask(null); + nic.setBroadcastUri(null); + nic.setIsolationUri(null); + } + } + + @Override + public void reserve(NicProfile nic, Network config, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) + throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException { + assert (nic.getReservationStrategy() == ReservationStrategy.Start) : "What can I do for nics that are not allocated at start? "; + if (_ovsNetworkMgr.isOvsNetworkEnabled()) { + return; + } + DataCenter dc = _dcDao.findById(config.getDataCenterId()); + if (_networkMgr.zoneIsConfiguredForExternalNetworking(config.getDataCenterId())) { + nic.setBroadcastUri(config.getBroadcastUri()); + nic.setIsolationUri(config.getBroadcastUri()); + nic.setDns1(dc.getDns1()); + nic.setDns2(dc.getDns2()); + nic.setNetmask(NetUtils.cidr2Netmask(config.getCidr())); + long cidrAddress = NetUtils.ip2Long(config.getCidr().split("/")[0]); + int cidrSize = _externalNetworkMgr.getGloballyConfiguredCidrSize(); + nic.setGateway(config.getGateway()); + + if (nic.getIp4Address() == null) { + + String guestIp = _networkMgr.acquireGuestIpAddress(config, null); + if (guestIp == null) { + throw new InsufficientVirtualNetworkCapcityException("Unable to acquire guest IP address for network " + config, DataCenter.class, dc.getId()); + } + + nic.setIp4Address(guestIp); + } else { + long ipMask = NetUtils.ip2Long(nic.getIp4Address()) & ~(0xffffffffffffffffl << (32 - cidrSize)); + nic.setIp4Address(NetUtils.long2Ip(cidrAddress | ipMask)); + } + } else { + super.reserve(nic, config, vm, dest, context); + } + + } + + @Override + public boolean release(NicProfile nic, VirtualMachineProfile vm, String reservationId) { + if (_ovsNetworkMgr.isOvsNetworkEnabled() || _tunnelMgr.isOvsTunnelEnabled()) { + return true; + } + + NetworkVO network = _networkDao.findById(nic.getNetworkId()); + if (network != null && _networkMgr.zoneIsConfiguredForExternalNetworking(network.getDataCenterId())) { + return true; + } else { + return super.release(nic, vm, reservationId); + } + } + + private long getIpMask(String ipAddress, long cidrSize) { + return NetUtils.ip2Long(ipAddress) & ~(0xffffffffffffffffl << (32 - cidrSize)); + } + +} diff --git a/server/src/com/cloud/server/ManagementServerExt.java b/server/src/com/cloud/server/ManagementServerExt.java new file mode 100644 index 00000000000..ec2804bd700 --- /dev/null +++ b/server/src/com/cloud/server/ManagementServerExt.java @@ -0,0 +1,66 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.server; + +import java.util.List; +import java.util.TimeZone; + +import com.cloud.api.commands.GenerateUsageRecordsCmd; +import com.cloud.api.commands.GetUsageRecordsCmd; +import com.cloud.server.api.response.UsageTypeResponse; +import com.cloud.usage.UsageVO; +public interface ManagementServerExt extends ManagementServer { + /** + * Generate Billing Records from the last time it was generated to the + * time specified. + * + * @param cmd the command wrapping the generate parameters + * - userId unique id of the user, pass in -1 to generate billing records + * for all users + * - startDate + * - endDate inclusive. If date specified is greater than the current time, the + * system will use the current time. + */ + boolean generateUsageRecords(GenerateUsageRecordsCmd cmd); + + /** + * Retrieves all Usage Records generated between the start and end date specified + * + * @param userId unique id of the user, pass in -1 to retrieve billing records + * for all users + * @param startDate inclusive. + * @param endDate inclusive. If date specified is greater than the current time, the + * system will use the current time. + * @param page The page of usage records to see (500 results are returned at a time, if + * more than 500 records exist then additional results can be retrieved by + * the appropriate page number) + * @return a list of usage records + */ + List getUsageRecords(GetUsageRecordsCmd cmd); + + /** + * Retrieves the timezone used for usage aggregation. One day is represented as midnight to 11:59:59pm + * in the given time zone + * @return the timezone specified by the config value usage.aggregation.timezone, or GMT if null + */ + TimeZone getUsageTimezone(); + + List listUsageTypes(); +} diff --git a/server/src/com/cloud/server/ManagementServerExtImpl.java b/server/src/com/cloud/server/ManagementServerExtImpl.java new file mode 100644 index 00000000000..613211a7577 --- /dev/null +++ b/server/src/com/cloud/server/ManagementServerExtImpl.java @@ -0,0 +1,242 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.server; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; + +import com.cloud.api.commands.GenerateUsageRecordsCmd; +import com.cloud.api.commands.GetUsageRecordsCmd; +import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.server.api.response.UsageTypeResponse; +import com.cloud.usage.UsageJobVO; +import com.cloud.usage.UsageTypes; +import com.cloud.usage.UsageVO; +import com.cloud.usage.dao.UsageDao; +import com.cloud.usage.dao.UsageJobDao; +import com.cloud.user.Account; +import com.cloud.user.AccountVO; +import com.cloud.user.UserContext; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; + +public class ManagementServerExtImpl extends ManagementServerImpl implements ManagementServerExt { + private final AccountDao _accountDao; + private final DomainDao _domainDao; + private final UsageDao _usageDao; + private final UsageJobDao _usageJobDao; + private final TimeZone _usageTimezone; + + protected ManagementServerExtImpl() { + super(); + + ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); + _accountDao = locator.getDao(AccountDao.class); + _domainDao = locator.getDao(DomainDao.class); + _usageDao = locator.getDao(UsageDao.class); + _usageJobDao = locator.getDao(UsageJobDao.class); + + Map configs = getConfigs(); + String timeZoneStr = configs.get("usage.aggregation.timezone"); + if (timeZoneStr == null) { + timeZoneStr = "GMT"; + } + _usageTimezone = TimeZone.getTimeZone(timeZoneStr); + } + + @Override + public boolean generateUsageRecords(GenerateUsageRecordsCmd cmd) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + try { + UsageJobVO immediateJob = _usageJobDao.getNextImmediateJob(); + if (immediateJob == null) { + UsageJobVO job = _usageJobDao.getLastJob(); + + String host = null; + int pid = 0; + if (job != null) { + host = job.getHost(); + pid = ((job.getPid() == null) ? 0 : job.getPid().intValue()); + } + _usageJobDao.createNewJob(host, pid, UsageJobVO.JOB_TYPE_SINGLE); + } + } finally { + txn.close(); + + // switch back to VMOPS_DB + Transaction swap = Transaction.open(Transaction.CLOUD_DB); + swap.close(); + } + return true; + } + + @Override + public List getUsageRecords(GetUsageRecordsCmd cmd) { + Long accountId = cmd.getAccountId(); + Long domainId = cmd.getDomainId(); + String accountName = cmd.getAccountName(); + Account userAccount = null; + Account account = (Account)UserContext.current().getCaller(); + Long usageType = cmd.getUsageType(); + + //if accountId is not specified, use accountName and domainId + if ((accountId == null) && (accountName != null) && (domainId != null)) { + if (_domainDao.isChildDomain(account.getDomainId(), domainId)) { + Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null); + List accounts = _accountDao.listAccounts(accountName, domainId, filter); + if(accounts.size() > 0){ + userAccount = accounts.get(0); + } + if (userAccount != null) { + accountId = userAccount.getId(); + } else { + throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId); + } + } else { + throw new PermissionDeniedException("Invalid Domain Id or Account"); + } + } + + boolean isAdmin = false; + + //If accountId couldn't be found using accountName and domainId, get it from userContext + if(accountId == null){ + accountId = account.getId(); + //List records for all the accounts if the caller account is of type admin. + //If account_id or account_name is explicitly mentioned, list records for the specified account only even if the caller is of type admin + if(account.getType() == Account.ACCOUNT_TYPE_ADMIN){ + isAdmin = true; + } + s_logger.debug("Account details not available. Using userContext accountId: "+accountId); + } + + Date startDate = cmd.getStartDate(); + Date endDate = cmd.getEndDate(); + if(startDate.after(endDate)){ + throw new InvalidParameterValueException("Incorrect Date Range. Start date: "+startDate+" is after end date:"+endDate); + } + TimeZone usageTZ = getUsageTimezone(); + Date adjustedStartDate = computeAdjustedTime(startDate, usageTZ, true); + Date adjustedEndDate = computeAdjustedTime(endDate, usageTZ, false); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("getting usage records for account: " + accountId + ", domainId: " + domainId + ", between " + startDate + " and " + endDate + ", using pageSize: " + cmd.getPageSizeVal() + " and startIndex: " + cmd.getStartIndex()); + } + + Filter usageFilter = new Filter(UsageVO.class, "startDate", false, cmd.getStartIndex(), cmd.getPageSizeVal()); + + SearchCriteria sc = _usageDao.createSearchCriteria(); + + if (accountId != -1 && accountId != Account.ACCOUNT_ID_SYSTEM && !isAdmin) { + sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); + } + + if (domainId != null) { + sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); + } + + if (usageType != null) { + sc.addAnd("usageType", SearchCriteria.Op.EQ, usageType); + } + + if ((adjustedStartDate != null) && (adjustedEndDate != null) && adjustedStartDate.before(adjustedEndDate)) { + sc.addAnd("startDate", SearchCriteria.Op.BETWEEN, adjustedStartDate, adjustedEndDate); + sc.addAnd("endDate", SearchCriteria.Op.BETWEEN, adjustedStartDate, adjustedEndDate); + } else { + return new ArrayList(); // return an empty list if we fail to validate the dates + } + + List usageRecords = null; + Transaction txn = Transaction.open(Transaction.USAGE_DB); + try { + usageRecords = _usageDao.searchAllRecords(sc, usageFilter); + } finally { + txn.close(); + + // switch back to VMOPS_DB + Transaction swap = Transaction.open(Transaction.CLOUD_DB); + swap.close(); + } + + // now that we are done with the records, update the command with the correct timezone so it can write the proper response + cmd.setUsageTimezone(getUsageTimezone()); + + return usageRecords; + } + + @Override + public TimeZone getUsageTimezone() { + return _usageTimezone; + } + + @Override + public String[] getApiConfig() { + return new String[] { "commands.properties", "commands-ext.properties" }; + } + + private Date computeAdjustedTime(Date initialDate, TimeZone targetTZ, boolean adjustToDayStart) { + Calendar cal = Calendar.getInstance(); + cal.setTime(initialDate); + TimeZone localTZ = cal.getTimeZone(); + int timezoneOffset = cal.get(Calendar.ZONE_OFFSET); + if (localTZ.inDaylightTime(initialDate)) { + timezoneOffset += (60 * 60 * 1000); + } + cal.add(Calendar.MILLISECOND, timezoneOffset); + + Date newTime = cal.getTime(); + + Calendar calTS = Calendar.getInstance(targetTZ); + calTS.setTime(newTime); + timezoneOffset = calTS.get(Calendar.ZONE_OFFSET); + if (targetTZ.inDaylightTime(initialDate)) { + timezoneOffset += (60 * 60 * 1000); + } + + calTS.add(Calendar.MILLISECOND, -1*timezoneOffset); + if (adjustToDayStart) { + calTS.set(Calendar.HOUR_OF_DAY, 0); + calTS.set(Calendar.MINUTE, 0); + calTS.set(Calendar.SECOND, 0); + calTS.set(Calendar.MILLISECOND, 0); + } else { + calTS.set(Calendar.HOUR_OF_DAY, 23); + calTS.set(Calendar.MINUTE, 59); + calTS.set(Calendar.SECOND, 59); + calTS.set(Calendar.MILLISECOND, 999); + } + + return calTS.getTime(); + } + + @Override + public List listUsageTypes() { + return UsageTypes.listUsageTypes(); + } +} diff --git a/server/src/com/cloud/server/api/response/BaremetalTemplateResponse.java b/server/src/com/cloud/server/api/response/BaremetalTemplateResponse.java new file mode 100644 index 00000000000..1729ab030b9 --- /dev/null +++ b/server/src/com/cloud/server/api/response/BaremetalTemplateResponse.java @@ -0,0 +1,18 @@ +package com.cloud.server.api.response; + +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class BaremetalTemplateResponse extends BaseResponse { + @SerializedName("id") @Param(description="the template ID") + private long id; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } +} diff --git a/server/src/com/cloud/server/api/response/ExternalFirewallResponse.java b/server/src/com/cloud/server/api/response/ExternalFirewallResponse.java new file mode 100644 index 00000000000..b5dad30080c --- /dev/null +++ b/server/src/com/cloud/server/api/response/ExternalFirewallResponse.java @@ -0,0 +1,149 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.server.api.response; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class ExternalFirewallResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) @Param(description="the ID of the external firewall") + private Long id; + + @SerializedName(ApiConstants.ZONE_ID) @Param(description="the zone ID of the external firewall") + private Long zoneId; + + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="the management IP address of the external firewall") + private String ipAddress; + + @SerializedName(ApiConstants.USERNAME) @Param(description="the username that's used to log in to the external firewall") + private String username; + + @SerializedName(ApiConstants.PUBLIC_INTERFACE) @Param(description="the public interface of the external firewall") + private String publicInterface; + + @SerializedName(ApiConstants.USAGE_INTERFACE) @Param(description="the usage interface of the external firewall") + private String usageInterface; + + @SerializedName(ApiConstants.PRIVATE_INTERFACE) @Param(description="the private interface of the external firewall") + private String privateInterface; + + @SerializedName(ApiConstants.PUBLIC_ZONE) @Param(description="the public security zone of the external firewall") + private String publicZone; + + @SerializedName(ApiConstants.PRIVATE_ZONE) @Param(description="the private security zone of the external firewall") + private String privateZone; + + @SerializedName(ApiConstants.NUM_RETRIES) @Param(description="the number of times to retry requests to the external firewall") + private String numRetries; + + @SerializedName(ApiConstants.TIMEOUT) @Param(description="the timeout (in seconds) for requests to the external firewall") + private String timeout; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getZoneId() { + return zoneId; + } + + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPublicInterface() { + return publicInterface; + } + + public void setPublicInterface(String publicInterface) { + this.publicInterface = publicInterface; + } + + public String getUsageInterface() { + return usageInterface; + } + + public void setUsageInterface(String usageInterface) { + this.usageInterface = usageInterface; + } + + public String getPrivateInterface() { + return privateInterface; + } + + public void setPrivateInterface(String privateInterface) { + this.privateInterface = privateInterface; + } + + public String getPublicZone() { + return publicZone; + } + + public void setPublicZone(String publicZone) { + this.publicZone = publicZone; + } + + public String getPrivateZone() { + return privateZone; + } + + public void setPrivateZone(String privateZone) { + this.privateZone = privateZone; + } + + public String getNumRetries() { + return numRetries; + } + + public void setNumRetries(String numRetries) { + this.numRetries = numRetries; + } + + public String getTimeout() { + return timeout; + } + + public void setTimeout(String timeout) { + this.timeout = timeout; + } +} diff --git a/server/src/com/cloud/server/api/response/ExternalLoadBalancerResponse.java b/server/src/com/cloud/server/api/response/ExternalLoadBalancerResponse.java new file mode 100644 index 00000000000..5d5baa68b0c --- /dev/null +++ b/server/src/com/cloud/server/api/response/ExternalLoadBalancerResponse.java @@ -0,0 +1,106 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.server.api.response; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class ExternalLoadBalancerResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) @Param(description="the ID of the external load balancer") + private Long id; + + @SerializedName(ApiConstants.ZONE_ID) @Param(description="the zone ID of the external load balancer") + private Long zoneId; + + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="the management IP address of the external load balancer") + private String ipAddress; + + @SerializedName(ApiConstants.USERNAME) @Param(description="the username that's used to log in to the external load balancer") + private String username; + + @SerializedName(ApiConstants.PUBLIC_INTERFACE) @Param(description="the public interface of the external load balancer") + private String publicInterface; + + @SerializedName(ApiConstants.PRIVATE_INTERFACE) @Param(description="the private interface of the external load balancer") + private String privateInterface; + + @SerializedName(ApiConstants.NUM_RETRIES) @Param(description="the number of times to retry requests to the external load balancer") + private String numRetries; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getZoneId() { + return zoneId; + } + + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPublicInterface() { + return publicInterface; + } + + public void setPublicInterface(String publicInterface) { + this.publicInterface = publicInterface; + } + + public String getPrivateInterface() { + return privateInterface; + } + + public void setPrivateInterface(String privateInterface) { + this.privateInterface = privateInterface; + } + + public String getNumRetries() { + return numRetries; + } + + public void setNumRetries(String numRetries) { + this.numRetries = numRetries; + } + +} diff --git a/server/src/com/cloud/server/api/response/NetworkDeviceResponse.java b/server/src/com/cloud/server/api/response/NetworkDeviceResponse.java new file mode 100644 index 00000000000..e650ce653d0 --- /dev/null +++ b/server/src/com/cloud/server/api/response/NetworkDeviceResponse.java @@ -0,0 +1,20 @@ +package com.cloud.server.api.response; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class NetworkDeviceResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the network device") + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/server/src/com/cloud/server/api/response/NwDeviceDhcpResponse.java b/server/src/com/cloud/server/api/response/NwDeviceDhcpResponse.java new file mode 100644 index 00000000000..e535e5dd9b0 --- /dev/null +++ b/server/src/com/cloud/server/api/response/NwDeviceDhcpResponse.java @@ -0,0 +1,47 @@ +package com.cloud.server.api.response; + +import com.cloud.api.ApiConstants; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class NwDeviceDhcpResponse extends NetworkDeviceResponse { + @SerializedName(ApiConstants.ZONE_ID) @Param(description="Zone where to add PXE server") + private Long zoneId; + + @SerializedName(ApiConstants.POD_ID) @Param(description="Pod where to add PXE server") + private Long podId; + + @SerializedName(ApiConstants.URL) @Param(description="Ip of PXE server") + private String url; + + @SerializedName(ApiConstants.TYPE) @Param(description="Type of add PXE server") + private String type; + + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + public Long getZoneId() { + return zoneId; + } + + public void setPodId(Long podId) { + this.podId = podId; + } + public Long getPodId() { + return podId; + } + + public void setUrl(String url) { + this.url = url; + } + public String getUrl() { + return url; + } + + public void setType(String type) { + this.type = type; + } + public String getType() { + return type; + } +} diff --git a/server/src/com/cloud/server/api/response/NwDevicePxeServerResponse.java b/server/src/com/cloud/server/api/response/NwDevicePxeServerResponse.java new file mode 100644 index 00000000000..24e02486f67 --- /dev/null +++ b/server/src/com/cloud/server/api/response/NwDevicePxeServerResponse.java @@ -0,0 +1,48 @@ +package com.cloud.server.api.response; + +import com.cloud.api.ApiConstants; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class NwDevicePxeServerResponse extends NetworkDeviceResponse { + + @SerializedName(ApiConstants.ZONE_ID) @Param(description="Zone where to add PXE server") + private Long zoneId; + + @SerializedName(ApiConstants.POD_ID) @Param(description="Pod where to add PXE server") + private Long podId; + + @SerializedName(ApiConstants.URL) @Param(description="Ip of PXE server") + private String url; + + @SerializedName(ApiConstants.TYPE) @Param(description="Type of add PXE server") + private String type; + + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + public Long getZoneId() { + return zoneId; + } + + public void setPodId(Long podId) { + this.podId = podId; + } + public Long getPodId() { + return podId; + } + + public void setUrl(String url) { + this.url = url; + } + public String getUrl() { + return url; + } + + public void setType(String type) { + this.type = type; + } + public String getType() { + return type; + } +} diff --git a/server/src/com/cloud/server/api/response/PxePingResponse.java b/server/src/com/cloud/server/api/response/PxePingResponse.java new file mode 100644 index 00000000000..adae898dc2a --- /dev/null +++ b/server/src/com/cloud/server/api/response/PxePingResponse.java @@ -0,0 +1,37 @@ +package com.cloud.server.api.response; + +import com.cloud.api.ApiConstants; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class PxePingResponse extends NwDevicePxeServerResponse { + @SerializedName(ApiConstants.PING_STORAGE_SERVER_IP) @Param(description="IP of PING storage server") + private String storageServerIp; + + @SerializedName(ApiConstants.PING_DIR) @Param(description="Direcotry on PING server where to get restore image") + private String pingDir; + + @SerializedName(ApiConstants.TFTP_DIR) @Param(description="Tftp root directory of PXE server") + private String tftpDir; + + public void setStorageServerIp(String ip) { + this.storageServerIp = ip; + } + public String getStorageServerIp() { + return this.storageServerIp; + } + + public void setPingDir(String dir) { + this.pingDir = dir; + } + public String getPingDir() { + return this.pingDir; + } + + public void setTftpDir(String dir) { + this.tftpDir = dir; + } + public String getTftpDir() { + return this.tftpDir; + } +} diff --git a/server/src/com/cloud/server/api/response/TrafficMonitorResponse.java b/server/src/com/cloud/server/api/response/TrafficMonitorResponse.java new file mode 100644 index 00000000000..f979a5385a1 --- /dev/null +++ b/server/src/com/cloud/server/api/response/TrafficMonitorResponse.java @@ -0,0 +1,83 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.server.api.response; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class TrafficMonitorResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) @Param(description="the ID of the external firewall") + private Long id; + + @SerializedName(ApiConstants.ZONE_ID) @Param(description="the zone ID of the external firewall") + private Long zoneId; + + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="the management IP address of the external firewall") + private String ipAddress; + + @SerializedName(ApiConstants.NUM_RETRIES) @Param(description="the number of times to retry requests to the external firewall") + private String numRetries; + + @SerializedName(ApiConstants.TIMEOUT) @Param(description="the timeout (in seconds) for requests to the external firewall") + private String timeout; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getZoneId() { + return zoneId; + } + + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public String getNumRetries() { + return numRetries; + } + + public void setNumRetries(String numRetries) { + this.numRetries = numRetries; + } + + public String getTimeout() { + return timeout; + } + + public void setTimeout(String timeout) { + this.timeout = timeout; + } +} diff --git a/server/src/com/cloud/server/api/response/UsageRecordResponse.java b/server/src/com/cloud/server/api/response/UsageRecordResponse.java new file mode 100644 index 00000000000..787a890a5c4 --- /dev/null +++ b/server/src/com/cloud/server/api/response/UsageRecordResponse.java @@ -0,0 +1,259 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.server.api.response; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class UsageRecordResponse extends BaseResponse { + @SerializedName(ApiConstants.ACCOUNT) @Param(description="the user account name") + private String accountName; + + @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description="the user account Id") + private Long accountId; + + @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain ID number") + private Long domainId; + + @SerializedName(ApiConstants.ZONE_ID) @Param(description="the zone ID number") + private Long zoneId; + + @SerializedName(ApiConstants.DESCRIPTION) @Param(description="description of account, including account name, service offering, and template") + private String description; + + @SerializedName("usage") @Param(description="usage in hours") + private String usage; + + @SerializedName("usagetype") @Param(description="usage type") + private Integer usageType; + + @SerializedName("rawusage") @Param(description="raw usage in hours") + private String rawUsage; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) @Param(description="virtual machine ID number") + private Long virtualMachineId; + + @SerializedName(ApiConstants.NAME) @Param(description="virtual machine name") + private String vmName; + + @SerializedName("offeringid") @Param(description="service offering ID number") + private Long serviceOfferingId; + + @SerializedName(ApiConstants.TEMPLATE_ID) @Param(description="template ID number") + private Long templateId; + + @SerializedName("usageid") @Param(description="id of the usage entity") + private Long usageId; + + @SerializedName(ApiConstants.TYPE) @Param(description="type") + private String type; + + @SerializedName(ApiConstants.SIZE) + private Long size; + + @SerializedName(ApiConstants.START_DATE) @Param(description="start date of account") + private String startDate; + + @SerializedName(ApiConstants.END_DATE) @Param(description="end date of account") + private String endDate; + + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="the IP address") + private String ipAddress; + + @SerializedName("assigneddate") @Param(description="the assign date of the account") + private String assignedDate; + + @SerializedName("releaseddate") @Param(description="the release date of the account") + private String releasedDate; + + @SerializedName("issourcenat") @Param(description="source Nat flag for IPAddress") + private Boolean isSourceNat; + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public Long getAccountId() { + return accountId; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + + public Long getDomainId() { + return domainId; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + public Long getZoneId() { + return zoneId; + } + + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getUsage() { + return usage; + } + + public void setUsage(String usage) { + this.usage = usage; + } + + public Integer getUsageType() { + return usageType; + } + + public void setUsageType(Integer usageType) { + this.usageType = usageType; + } + + public String getRawUsage() { + return rawUsage; + } + + public void setRawUsage(String rawUsage) { + this.rawUsage = rawUsage; + } + + public Long getVirtualMachineId() { + return virtualMachineId; + } + + public void setVirtualMachineId(Long virtualMachineId) { + this.virtualMachineId = virtualMachineId; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public Long getServiceOfferingId() { + return serviceOfferingId; + } + + public void setServiceOfferingId(Long serviceOfferingId) { + this.serviceOfferingId = serviceOfferingId; + } + + public Long getTemplateId() { + return templateId; + } + + public void setTemplateId(Long templateId) { + this.templateId = templateId; + } + + public Long getUsageId() { + return usageId; + } + + public void setUsageId(Long usageId) { + this.usageId = usageId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Long getSize() { + return size; + } + + public void setSize(Long size) { + this.size = size; + } + + public String getStartDate() { + return startDate; + } + + public void setStartDate(String startDate) { + this.startDate = startDate; + } + + public String getEndDate() { + return endDate; + } + + public void setEndDate(String endDate) { + this.endDate = endDate; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public String getAssignedDate() { + return assignedDate; + } + + public void setAssignedDate(String assignedDate) { + this.assignedDate = assignedDate; + } + + public String getReleasedDate() { + return releasedDate; + } + + public void setReleasedDate(String releasedDate) { + this.releasedDate = releasedDate; + } + + public void setSourceNat(Boolean isSourceNat) { + this.isSourceNat = isSourceNat; + } + + public Boolean isSourceNat() { + return isSourceNat; + } +} diff --git a/server/src/com/cloud/server/api/response/UsageTypeResponse.java b/server/src/com/cloud/server/api/response/UsageTypeResponse.java new file mode 100644 index 00000000000..fe7116834c4 --- /dev/null +++ b/server/src/com/cloud/server/api/response/UsageTypeResponse.java @@ -0,0 +1,58 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.server.api.response; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class UsageTypeResponse extends BaseResponse { + + @SerializedName("usagetypeid") @Param(description="usage type") + private Integer usageType; + + @SerializedName(ApiConstants.DESCRIPTION) @Param(description="description of usage type") + private String description; + + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getUsageType() { + return usageType; + } + + public void setUsageType(Integer usageType) { + this.usageType = usageType; + } + + public UsageTypeResponse(Integer usageType, String description){ + this.usageType = usageType; + this.description = description; + setObjectName("usagetype"); + } + +} diff --git a/server/src/com/cloud/server/api/response/netapp/AssociateLunCmdResponse.java b/server/src/com/cloud/server/api/response/netapp/AssociateLunCmdResponse.java new file mode 100644 index 00000000000..8586c58f0d9 --- /dev/null +++ b/server/src/com/cloud/server/api/response/netapp/AssociateLunCmdResponse.java @@ -0,0 +1,44 @@ +package com.cloud.server.api.response.netapp; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class AssociateLunCmdResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) @Param(description="the LUN id") + private String lun; + + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="the IP address of ") + private String ipAddress; + + @SerializedName(ApiConstants.TARGET_IQN) @Param(description="the target IQN") + private String targetIQN; + + public String getLun() { + return lun; + } + + public String getIpAddress() { + return ipAddress; + } + + public String getTargetIQN() { + return targetIQN; + } + + + public void setLun(String lun) { + this.lun = lun; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public void setTargetIQN(String targetIQN) { + this.targetIQN = targetIQN; + } + +} diff --git a/server/src/com/cloud/server/api/response/netapp/CreateLunCmdResponse.java b/server/src/com/cloud/server/api/response/netapp/CreateLunCmdResponse.java new file mode 100644 index 00000000000..a9c2f9f1e87 --- /dev/null +++ b/server/src/com/cloud/server/api/response/netapp/CreateLunCmdResponse.java @@ -0,0 +1,43 @@ +package com.cloud.server.api.response.netapp; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class CreateLunCmdResponse extends BaseResponse { + + @SerializedName(ApiConstants.PATH) @Param(description="pool path") + private String path; + + @SerializedName(ApiConstants.IQN) @Param(description="iqn") + private String iqn; + + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="ip address") + private String ipAddress; + + + public String getPath() { + return path; + } + + public String getIqn() { + return iqn; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setPath(String path) { + this.path = path; + } + + public void setIqn(String iqn) { + this.iqn = iqn; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } +} diff --git a/server/src/com/cloud/server/api/response/netapp/CreatePoolCmdResponse.java b/server/src/com/cloud/server/api/response/netapp/CreatePoolCmdResponse.java new file mode 100644 index 00000000000..657a6b64d5e --- /dev/null +++ b/server/src/com/cloud/server/api/response/netapp/CreatePoolCmdResponse.java @@ -0,0 +1,6 @@ +package com.cloud.server.api.response.netapp; + +import com.cloud.api.response.BaseResponse; + +public class CreatePoolCmdResponse extends BaseResponse{ +} diff --git a/server/src/com/cloud/server/api/response/netapp/CreateVolumeCmdResponse.java b/server/src/com/cloud/server/api/response/netapp/CreateVolumeCmdResponse.java new file mode 100644 index 00000000000..1dbdf20c188 --- /dev/null +++ b/server/src/com/cloud/server/api/response/netapp/CreateVolumeCmdResponse.java @@ -0,0 +1,6 @@ +package com.cloud.server.api.response.netapp; + +import com.cloud.api.response.BaseResponse; + +public class CreateVolumeCmdResponse extends BaseResponse { +} diff --git a/server/src/com/cloud/server/api/response/netapp/DeleteLUNCmdResponse.java b/server/src/com/cloud/server/api/response/netapp/DeleteLUNCmdResponse.java new file mode 100644 index 00000000000..a78b2f33811 --- /dev/null +++ b/server/src/com/cloud/server/api/response/netapp/DeleteLUNCmdResponse.java @@ -0,0 +1,9 @@ +package com.cloud.server.api.response.netapp; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class DeleteLUNCmdResponse extends BaseResponse{ +} diff --git a/server/src/com/cloud/server/api/response/netapp/DeletePoolCmdResponse.java b/server/src/com/cloud/server/api/response/netapp/DeletePoolCmdResponse.java new file mode 100644 index 00000000000..6eaa7fdb7f2 --- /dev/null +++ b/server/src/com/cloud/server/api/response/netapp/DeletePoolCmdResponse.java @@ -0,0 +1,6 @@ +package com.cloud.server.api.response.netapp; + +import com.cloud.api.response.BaseResponse; + +public class DeletePoolCmdResponse extends BaseResponse { +} diff --git a/server/src/com/cloud/server/api/response/netapp/DeleteVolumeCmdResponse.java b/server/src/com/cloud/server/api/response/netapp/DeleteVolumeCmdResponse.java new file mode 100644 index 00000000000..bc347b6b48c --- /dev/null +++ b/server/src/com/cloud/server/api/response/netapp/DeleteVolumeCmdResponse.java @@ -0,0 +1,9 @@ +package com.cloud.server.api.response.netapp; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class DeleteVolumeCmdResponse extends BaseResponse { +} diff --git a/server/src/com/cloud/server/api/response/netapp/DissociateLunCmdResponse.java b/server/src/com/cloud/server/api/response/netapp/DissociateLunCmdResponse.java new file mode 100644 index 00000000000..145523fe86c --- /dev/null +++ b/server/src/com/cloud/server/api/response/netapp/DissociateLunCmdResponse.java @@ -0,0 +1,9 @@ +package com.cloud.server.api.response.netapp; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class DissociateLunCmdResponse extends BaseResponse { +} diff --git a/server/src/com/cloud/server/api/response/netapp/ListLunsCmdResponse.java b/server/src/com/cloud/server/api/response/netapp/ListLunsCmdResponse.java new file mode 100644 index 00000000000..b7124d0b3cd --- /dev/null +++ b/server/src/com/cloud/server/api/response/netapp/ListLunsCmdResponse.java @@ -0,0 +1,53 @@ +package com.cloud.server.api.response.netapp; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class ListLunsCmdResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) @Param(description="lun id") + private Long id; + + @SerializedName(ApiConstants.IQN) @Param(description="lun iqn") + private String iqn; + + @SerializedName(ApiConstants.NAME) @Param(description="lun name") + private String name; + + @SerializedName(ApiConstants.VOLUME_ID) @Param(description="volume id") + private Long volumeId; + + + public Long getId() { + return id; + } + + public String getIqn() { + return iqn; + } + + public String getName() { + return name; + } + + public Long getVolumeId() { + return volumeId; + } + + public void setId(Long id) { + this.id = id; + } + + public void setIqn(String iqn) { + this.iqn = iqn; + } + + public void setName(String name) { + this.name = name; + } + + public void setVolumeId(Long id) { + this.volumeId = id; + } +} \ No newline at end of file diff --git a/server/src/com/cloud/server/api/response/netapp/ListPoolsCmdResponse.java b/server/src/com/cloud/server/api/response/netapp/ListPoolsCmdResponse.java new file mode 100644 index 00000000000..0b2d2d5c8cb --- /dev/null +++ b/server/src/com/cloud/server/api/response/netapp/ListPoolsCmdResponse.java @@ -0,0 +1,42 @@ +package com.cloud.server.api.response.netapp; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class ListPoolsCmdResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) @Param(description="pool id") + private Long id; + @SerializedName(ApiConstants.NAME) @Param(description="pool name") + private String name; + + @SerializedName(ApiConstants.ALGORITHM) @Param(description="pool algorithm") + private String algorithm; + + + public Long getId() { + return id; + } + + + public String getName() { + return name; + } + + public String getAlgorithm() { + return algorithm; + } + + public void setId(Long id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } +} diff --git a/server/src/com/cloud/server/api/response/netapp/ListVolumesCmdResponse.java b/server/src/com/cloud/server/api/response/netapp/ListVolumesCmdResponse.java new file mode 100644 index 00000000000..aaa90fcb63c --- /dev/null +++ b/server/src/com/cloud/server/api/response/netapp/ListVolumesCmdResponse.java @@ -0,0 +1,98 @@ +package com.cloud.server.api.response.netapp; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class ListVolumesCmdResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) @Param(description="volume id") + private Long id; + + @SerializedName(ApiConstants.POOL_NAME) @Param(description="pool name") + private String poolName; + + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="ip address") + private String ipAddress; + + @SerializedName(ApiConstants.AGGREGATE_NAME) @Param(description="Aggregate name") + private String aggrName; + + @SerializedName(ApiConstants.VOLUME_NAME) @Param(description="Volume name") + private String volumeName; + + @SerializedName(ApiConstants.SNAPSHOT_POLICY) @Param(description="snapshot policy") + private String snapshotPolicy; + + @SerializedName(ApiConstants.SNAPSHOT_RESERVATION) @Param(description="snapshot reservation") + private Integer snapshotReservation; + + @SerializedName(ApiConstants.SIZE) @Param(description="volume size") + private String volumeSize; + + public Long getId() { + return id; + } + + public String getPoolName() { + return poolName; + } + + public String getIpAddress() { + return ipAddress; + } + + public String getAggrName() { + return aggrName; + } + + public String getVolumeName() { + return volumeName; + } + + public String getSnapshotPolicy() { + return snapshotPolicy; + } + + public Integer getSnapshotReservation() { + return snapshotReservation; + } + + public String getVolumeSize() { + return volumeSize; + } + + public void setId(Long id) { + this.id = id; + } + + public void setPoolName(String poolName) { + this.poolName = poolName; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public void setAggrName(String aggrName) { + this.aggrName = aggrName; + } + + public void setVolumeName(String volumeName) { + this.volumeName = volumeName; + } + + public void setSnapshotPolicy(String snapshotPolicy) { + this.snapshotPolicy = snapshotPolicy; + } + + public void setSnapshotReservation(Integer snapshotReservation) { + this.snapshotReservation = snapshotReservation; + } + + public void setVolumeSize(String size) { + this.volumeSize = size; + } + +} + diff --git a/server/src/com/cloud/server/api/response/netapp/ModifyPoolCmdResponse.java b/server/src/com/cloud/server/api/response/netapp/ModifyPoolCmdResponse.java new file mode 100644 index 00000000000..ce2a2cc24b3 --- /dev/null +++ b/server/src/com/cloud/server/api/response/netapp/ModifyPoolCmdResponse.java @@ -0,0 +1,9 @@ +package com.cloud.server.api.response.netapp; + +import com.cloud.api.ApiConstants; +import com.cloud.api.response.BaseResponse; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class ModifyPoolCmdResponse extends BaseResponse { +} diff --git a/server/src/com/cloud/upgrade/PremiumDatabaseUpgradeChecker.java b/server/src/com/cloud/upgrade/PremiumDatabaseUpgradeChecker.java new file mode 100755 index 00000000000..062683f08d2 --- /dev/null +++ b/server/src/com/cloud/upgrade/PremiumDatabaseUpgradeChecker.java @@ -0,0 +1,60 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.upgrade; + +import javax.ejb.Local; + +import com.cloud.upgrade.dao.DbUpgrade; +import com.cloud.upgrade.dao.Upgrade217to218; +import com.cloud.upgrade.dao.Upgrade218to224DomainVlans; +import com.cloud.upgrade.dao.Upgrade218to22Premium; +import com.cloud.upgrade.dao.Upgrade221to222Premium; +import com.cloud.upgrade.dao.Upgrade222to224Premium; +import com.cloud.upgrade.dao.Upgrade224to225; +import com.cloud.upgrade.dao.Upgrade225to226; +import com.cloud.upgrade.dao.Upgrade227to228Premium; +import com.cloud.upgrade.dao.Upgrade228to229; +import com.cloud.upgrade.dao.Upgrade229to2210; +import com.cloud.upgrade.dao.UpgradeSnapshot217to224; +import com.cloud.upgrade.dao.UpgradeSnapshot223to224; +import com.cloud.upgrade.dao.VersionDaoImpl; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.component.SystemIntegrityChecker; + +@Local(value = {SystemIntegrityChecker.class}) +public class PremiumDatabaseUpgradeChecker extends DatabaseUpgradeChecker { + public PremiumDatabaseUpgradeChecker() { + _dao = ComponentLocator.inject(VersionDaoImpl.class); + _dao = ComponentLocator.inject(VersionDaoImpl.class); + _upgradeMap.put("2.1.7", new DbUpgrade[] { new Upgrade217to218(), new Upgrade218to22Premium(), new Upgrade221to222Premium(), new UpgradeSnapshot217to224(), new Upgrade222to224Premium(),new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228Premium(), new Upgrade228to229(), new Upgrade229to2210()}); + _upgradeMap.put("2.1.8", new DbUpgrade[] { new Upgrade218to22Premium(), new Upgrade221to222Premium(), new UpgradeSnapshot217to224(), new Upgrade222to224Premium(), + new Upgrade218to224DomainVlans(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228Premium(), new Upgrade228to229(), new Upgrade229to2210() }); + _upgradeMap.put("2.1.9", new DbUpgrade[] { new Upgrade218to22Premium(), new Upgrade221to222Premium(), new UpgradeSnapshot217to224(), new Upgrade222to224Premium(), + new Upgrade218to224DomainVlans(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228Premium(), new Upgrade228to229(), new Upgrade229to2210()}); + _upgradeMap.put("2.2.1", new DbUpgrade[] { new Upgrade221to222Premium(), new Upgrade222to224Premium(), new UpgradeSnapshot223to224(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228Premium(), new Upgrade228to229(), new Upgrade229to2210()}); + _upgradeMap.put("2.2.2", new DbUpgrade[] { new Upgrade222to224Premium(), new UpgradeSnapshot223to224(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228Premium(), new Upgrade228to229(), new Upgrade229to2210()}); + _upgradeMap.put("2.2.3", new DbUpgrade[] { new Upgrade222to224Premium(), new UpgradeSnapshot223to224(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228Premium(), new Upgrade228to229(), new Upgrade229to2210()}); + _upgradeMap.put("2.2.4", new DbUpgrade[] { new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228Premium(), new Upgrade228to229(), new Upgrade229to2210()}); + _upgradeMap.put("2.2.5", new DbUpgrade[] { new Upgrade225to226(), new Upgrade227to228Premium(), new Upgrade228to229(), new Upgrade229to2210()}); + _upgradeMap.put("2.2.6", new DbUpgrade[] { new Upgrade227to228Premium(), new Upgrade228to229(), new Upgrade229to2210()}); + _upgradeMap.put("2.2.7", new DbUpgrade[] { new Upgrade227to228Premium(), new Upgrade228to229(), new Upgrade229to2210()}); + _upgradeMap.put("2.2.8", new DbUpgrade[] { new Upgrade228to229(), new Upgrade229to2210()}); + _upgradeMap.put("2.2.9", new DbUpgrade[] { new Upgrade229to2210()}); + } +} diff --git a/server/src/com/cloud/upgrade/dao/Upgrade218to22Premium.java b/server/src/com/cloud/upgrade/dao/Upgrade218to22Premium.java new file mode 100644 index 00000000000..b83f7f3d5b9 --- /dev/null +++ b/server/src/com/cloud/upgrade/dao/Upgrade218to22Premium.java @@ -0,0 +1,102 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.upgrade.dao; + +import java.io.File; +import java.sql.Connection; +import java.sql.PreparedStatement; + +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; + +public class Upgrade218to22Premium extends Upgrade218to22 { + @Override + public File[] getPrepareScripts() { + File[] scripts = super.getPrepareScripts(); + File[] newScripts = new File[2]; + newScripts[0] = scripts[0]; + + String file = Script.findScript("","db/schema-21to22-premium.sql"); + if (file == null) { + throw new CloudRuntimeException("Unable to find the upgrade script, schema-21to22-premium.sql"); + } + + newScripts[1] = new File(file); + + return newScripts; + } + + @Override + public void performDataMigration(Connection conn) { + super.performDataMigration(conn); + updateUserStats(conn); + updateUsageIpAddress(conn); + } + + @Override + public File[] getCleanupScripts() { + File[] scripts = super.getCleanupScripts(); + File[] newScripts = new File[1]; + // Change the array to 2 when you add in the scripts for premium. + newScripts[0] = scripts[0]; + return newScripts; + } + + private void updateUserStats(Connection conn) { + try { + + // update device_id information + PreparedStatement pstmt = conn.prepareStatement("update cloud_usage.user_statistics uus set device_id = " + + "(select device_id from cloud.user_statistics us where uus.id = us.id)"); + pstmt.executeUpdate(); + pstmt.close(); + + s_logger.debug("Upgraded cloud_usage user_statistics with deviceId"); + + // update host_id information in usage_network + PreparedStatement pstmt1 = conn.prepareStatement("update cloud_usage.usage_network un set host_id = " + + "(select device_id from cloud_usage.user_statistics us where us.account_id = un.account_id and us.data_center_id = un.zone_id)"); + pstmt1.executeUpdate(); + pstmt1.close(); + + s_logger.debug("Upgraded cloud_usage usage_network with hostId"); + + + } catch (Exception e) { + throw new CloudRuntimeException("Failed to upgrade user stats: ", e); + } + } + + private void updateUsageIpAddress(Connection conn) { + try { + + // update id information + PreparedStatement pstmt = conn.prepareStatement("update cloud_usage.usage_ip_address uip set id = " + + "(select id from cloud.user_ip_address ip where uip.public_ip_address = ip.public_ip_address and ip.data_center_id = uip.zone_id)"); + pstmt.executeUpdate(); + pstmt.close(); + + s_logger.debug("Upgraded cloud_usage usage_ip_address with Id"); + + } catch (Exception e) { + throw new CloudRuntimeException("Failed to upgrade usage_ip_address: ", e); + } + } + +} diff --git a/server/src/com/cloud/upgrade/dao/Upgrade221to222Premium.java b/server/src/com/cloud/upgrade/dao/Upgrade221to222Premium.java new file mode 100644 index 00000000000..26795489e30 --- /dev/null +++ b/server/src/com/cloud/upgrade/dao/Upgrade221to222Premium.java @@ -0,0 +1,63 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.upgrade.dao; + +import java.io.File; +import java.sql.Connection; + +import org.apache.log4j.Logger; + +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; + +public class Upgrade221to222Premium extends Upgrade221to222 { + final static Logger s_logger = Logger.getLogger(Upgrade221to222Premium.class); + + @Override + public File[] getPrepareScripts() { + File[] scripts = super.getPrepareScripts(); + File[] newScripts = new File[2]; + newScripts[0] = scripts[0]; + + String file = Script.findScript("","db/schema-221to222-premium.sql"); + if (file == null) { + throw new CloudRuntimeException("Unable to find the upgrade script, schema-221to222-premium.sql"); + } + + newScripts[1] = new File(file); + + + return newScripts; + } + + @Override + public void performDataMigration(Connection conn) { + super.performDataMigration(conn); + // perform permium data migration here. + } + + @Override + public File[] getCleanupScripts() { + File[] scripts = super.getCleanupScripts(); + File[] newScripts = new File[1]; + // Change the array to 2 when you add in the scripts for premium. + newScripts[0] = scripts[0]; + return newScripts; + } +} diff --git a/server/src/com/cloud/upgrade/dao/Upgrade222to224Premium.java b/server/src/com/cloud/upgrade/dao/Upgrade222to224Premium.java new file mode 100644 index 00000000000..44f364879f0 --- /dev/null +++ b/server/src/com/cloud/upgrade/dao/Upgrade222to224Premium.java @@ -0,0 +1,89 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.upgrade.dao; + +import java.io.File; +import java.sql.Connection; +import java.sql.PreparedStatement; + +import org.apache.log4j.Logger; + +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; + +public class Upgrade222to224Premium extends Upgrade222to224 { + final static Logger s_logger = Logger.getLogger(Upgrade222to224Premium.class); + + @Override + public File[] getPrepareScripts() { + File[] scripts = super.getPrepareScripts(); + File[] newScripts = new File[2]; + newScripts[0] = scripts[0]; + + String file = Script.findScript("","db/schema-222to224-premium.sql"); + if (file == null) { + throw new CloudRuntimeException("Unable to find the upgrade script, schema-222to224-premium.sql"); + } + + newScripts[1] = new File(file); + + return newScripts; + } + + @Override + public void performDataMigration(Connection conn) { + super.performDataMigration(conn); + updateUserStats(conn); + } + + @Override + public File[] getCleanupScripts() { + File[] scripts = super.getCleanupScripts(); + File[] newScripts = new File[1]; + // Change the array to 2 when you add in the scripts for premium. + newScripts[0] = scripts[0]; + return newScripts; + } + + private void updateUserStats(Connection conn) { + try { + + // update network_id information + PreparedStatement pstmt = conn.prepareStatement("update cloud_usage.user_statistics uus, cloud.user_statistics us set uus.network_id = " + + "us.network_id where uus.id = us.id"); + pstmt.executeUpdate(); + pstmt.close(); + + s_logger.debug("Upgraded cloud_usage user_statistics with networkId"); + + + // update network_id information in usage_network + PreparedStatement pstmt1 = conn.prepareStatement("update cloud_usage.usage_network un, cloud_usage.user_statistics us set un.network_id = " + + "us.network_id where us.account_id = un.account_id and us.data_center_id = un.zone_id and us.device_id = un.host_id"); + pstmt1.executeUpdate(); + pstmt1.close(); + + s_logger.debug("Upgraded cloud_usage usage_network with networkId"); + + + } catch (Exception e) { + throw new CloudRuntimeException("Failed to upgrade user stats: ", e); + } + } +} diff --git a/server/src/com/cloud/upgrade/dao/Upgrade227to228Premium.java b/server/src/com/cloud/upgrade/dao/Upgrade227to228Premium.java new file mode 100644 index 00000000000..1e925d1ea17 --- /dev/null +++ b/server/src/com/cloud/upgrade/dao/Upgrade227to228Premium.java @@ -0,0 +1,138 @@ +/** +<<<<<<< HEAD + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * +======= + * Copyright (C) 2011 Cloud.com, Inc. All rights reserved. +>>>>>>> 2.2.y + */ +package com.cloud.upgrade.dao; + +import java.io.File; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.apache.log4j.Logger; + +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; + +public class Upgrade227to228Premium extends Upgrade227to228 { + final static Logger s_logger = Logger.getLogger(Upgrade227to228Premium.class); + + @Override + public File[] getPrepareScripts() { + File[] scripts = super.getPrepareScripts(); + File[] newScripts = new File[2]; + newScripts[0] = scripts[0]; + + String file = Script.findScript("","db/schema-227to228-premium.sql"); + if (file == null) { + throw new CloudRuntimeException("Unable to find the upgrade script, schema-227to228-premium.sql"); + } + + newScripts[1] = new File(file); + + return newScripts; + } + + @Override + public void performDataMigration(Connection conn) { + addSourceIdColumn(conn); + addNetworkIdsToUserStats(conn); + super.performDataMigration(conn); + } + + @Override + public File[] getCleanupScripts() { + return null; + } + + private void addSourceIdColumn(Connection conn) { + boolean insertField = false; + try { + PreparedStatement pstmt; + try { + pstmt = conn.prepareStatement("SELECT source_id FROM `cloud_usage`.`usage_storage`"); + ResultSet rs = pstmt.executeQuery(); + + if (rs.next()) { + s_logger.info("The source id field already exist, not adding it"); + } + + } catch (Exception e) { + // if there is an exception, it means that field doesn't exist, and we can create it + insertField = true; + } + + if (insertField) { + s_logger.debug("Adding source_id to usage_storage..."); + pstmt = conn.prepareStatement("ALTER TABLE `cloud_usage`.`usage_storage` ADD COLUMN `source_id` bigint unsigned"); + pstmt.executeUpdate(); + s_logger.debug("Column source_id was added successfully to usage_storage table"); + pstmt.close(); + } + + + } catch (SQLException e) { + s_logger.error("Failed to add source_id to usage_storage due to ", e); + throw new CloudRuntimeException("Failed to add source_id to usage_storage due to ", e); + } + } + + private void addNetworkIdsToUserStats(Connection conn) { + s_logger.debug("Adding network IDs to user stats..."); + try { + String stmt = "SELECT DISTINCT public_ip_address FROM `cloud`.`user_statistics` WHERE public_ip_address IS NOT null"; + PreparedStatement pstmt = conn.prepareStatement(stmt); + ResultSet rs = pstmt.executeQuery(); + + while (rs.next()) { + String publicIpAddress = rs.getString(1); + stmt = "SELECT network_id FROM `cloud`.`user_ip_address` WHERE public_ip_address = ?"; + pstmt = conn.prepareStatement(stmt); + pstmt.setString(1, publicIpAddress); + ResultSet rs2 = pstmt.executeQuery(); + + if (rs2.next()) { + Long networkId = rs2.getLong(1); + String[] dbs = {"cloud", "cloud_usage"}; + for (String db : dbs) { + stmt = "UPDATE `" + db + "`.`user_statistics` SET network_id = ? WHERE public_ip_address = ?"; + pstmt = conn.prepareStatement(stmt); + pstmt.setLong(1, networkId); + pstmt.setString(2, publicIpAddress); + pstmt.executeUpdate(); + } + } + + rs2.close(); + } + + rs.close(); + pstmt.close(); + s_logger.debug("Successfully added network IDs to user stats."); + } catch (SQLException e) { + String errorMsg = "Failed to add network IDs to user stats."; + s_logger.error(errorMsg, e); + throw new CloudRuntimeException(errorMsg, e); + } + } + +} diff --git a/server/src/com/cloud/usage/ExternalPublicIpStatisticsVO.java b/server/src/com/cloud/usage/ExternalPublicIpStatisticsVO.java new file mode 100644 index 00000000000..156d01e65e6 --- /dev/null +++ b/server/src/com/cloud/usage/ExternalPublicIpStatisticsVO.java @@ -0,0 +1,99 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage; + +import javax.persistence.Column; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.PrimaryKeyJoinColumn; +import javax.persistence.Table; + +@Entity +@Table(name="external_public_ip_statistics") +@PrimaryKeyJoinColumn(name="id") +public class ExternalPublicIpStatisticsVO { + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private Long id; + + @Column(name="data_center_id", updatable=false) + private long zoneId; + + @Column(name="account_id", updatable=false) + private long accountId; + + @Column(name="public_ip_address") + private String publicIpAddress; + + @Column(name="current_bytes_received") + private long currentBytesReceived; + + @Column(name="current_bytes_sent") + private long currentBytesSent; + + protected ExternalPublicIpStatisticsVO() { + } + + public ExternalPublicIpStatisticsVO(long zoneId, long accountId, String publicIpAddress) { + this.zoneId = zoneId; + this.accountId = accountId; + this.publicIpAddress = publicIpAddress; + this.currentBytesReceived = 0; + this.currentBytesSent = 0; + } + + public Long getId() { + return id; + } + + public long getZoneId() { + return zoneId; + } + + public long getAccountId() { + return accountId; + } + + public String getPublicIpAddress() { + return publicIpAddress; + } + + public long getCurrentBytesReceived() { + return currentBytesReceived; + } + + public void setCurrentBytesReceived(long currentBytesReceived) { + this.currentBytesReceived = currentBytesReceived; + } + + public long getCurrentBytesSent() { + return currentBytesSent; + } + + public void setCurrentBytesSent(long currentBytesSent) { + this.currentBytesSent = currentBytesSent; + } + +} diff --git a/server/src/com/cloud/usage/StorageTypes.java b/server/src/com/cloud/usage/StorageTypes.java new file mode 100644 index 00000000000..4c920c2f8d6 --- /dev/null +++ b/server/src/com/cloud/usage/StorageTypes.java @@ -0,0 +1,26 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage; + +public class StorageTypes { + public static final int TEMPLATE = 1; + public static final int ISO = 2; + public static final int SNAPSHOT = 3; +} diff --git a/server/src/com/cloud/usage/UsageIPAddressVO.java b/server/src/com/cloud/usage/UsageIPAddressVO.java new file mode 100644 index 00000000000..e0e6fbb8ff5 --- /dev/null +++ b/server/src/com/cloud/usage/UsageIPAddressVO.java @@ -0,0 +1,114 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +@Entity +@Table(name="usage_ip_address") +public class UsageIPAddressVO { + @Column(name="account_id") + private long accountId; + + @Column(name="domain_id") + private long domainId; + + @Column(name="zone_id") + private long zoneId; + + @Column(name="id") + private long id; + + @Column(name="public_ip_address") + private String address = null; + + @Column(name="is_source_nat") + private boolean isSourceNat = false; + + @Column(name="assigned") + @Temporal(value=TemporalType.TIMESTAMP) + private Date assigned = null; + + @Column(name="released") + @Temporal(value=TemporalType.TIMESTAMP) + private Date released = null; + + protected UsageIPAddressVO() { + } + + public UsageIPAddressVO(long id, long accountId, long domainId, long zoneId, String address, boolean isSourceNat, Date assigned, Date released) { + this.id = id; + this.accountId = accountId; + this.domainId = domainId; + this.zoneId = zoneId; + this.address = address; + this.isSourceNat = isSourceNat; + this.assigned = assigned; + this.released = released; + } + + public UsageIPAddressVO(long accountId, String address, Date assigned, Date released) { + this.accountId = accountId; + this.address = address; + this.assigned = assigned; + this.released = released; + } + + public long getAccountId() { + return accountId; + } + + public long getDomainId() { + return domainId; + } + + public long getZoneId() { + return zoneId; + } + + public long getId() { + return id; + } + + public String getAddress() { + return address; + } + + public boolean isSourceNat() { + return isSourceNat; + } + + public Date getAssigned() { + return assigned; + } + + public Date getReleased() { + return released; + } + public void setReleased(Date released) { + this.released = released; + } +} diff --git a/server/src/com/cloud/usage/UsageJobVO.java b/server/src/com/cloud/usage/UsageJobVO.java new file mode 100644 index 00000000000..cc3f93c94f7 --- /dev/null +++ b/server/src/com/cloud/usage/UsageJobVO.java @@ -0,0 +1,181 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +@Entity +@Table(name="usage_job") +public class UsageJobVO { + + public static final int JOB_TYPE_RECURRING = 0; + public static final int JOB_TYPE_SINGLE = 1; + + public static final int JOB_NOT_SCHEDULED = 0; + public static final int JOB_SCHEDULED = 1; + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private Long id; + + @Column(name="host") + private String host; + + @Column(name="pid") + private Integer pid; + + @Column(name="job_type") + private int jobType; + + @Column(name="scheduled") + private int scheduled; + + @Column(name="start_millis") + private long startMillis; + + @Column(name="end_millis") + private long endMillis; + + @Column(name="exec_time") + private long execTime; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name="start_date") + private Date startDate; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name="end_date") + private Date endDate; + + @Column(name="success") + private Boolean success; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name="heartbeat") + private Date heartbeat; + + public UsageJobVO() {} + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getPid() { + return pid; + } + + public void setPid(Integer pid) { + this.pid = pid; + } + + public int getJobType() { + return jobType; + } + + public void setJobType(int jobType) { + this.jobType = jobType; + } + + public int getScheduled() { + return scheduled; + } + + public void setScheduled(int scheduled) { + this.scheduled = scheduled; + } + + public long getStartMillis() { + return startMillis; + } + + public void setStartMillis(long startMillis) { + this.startMillis = startMillis; + } + + public long getEndMillis() { + return endMillis; + } + + public void setEndMillis(long endMillis) { + this.endMillis = endMillis; + } + + public long getExecTime() { + return execTime; + } + + public void setExecTime(long execTime) { + this.execTime = execTime; + } + + public Date getStartDate() { + return startDate; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public Date getEndDate() { + return endDate; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + + public Boolean getSuccess() { + return success; + } + + public void setSuccess(Boolean success) { + this.success = success; + } + + public Date getHeartbeat() { + return heartbeat; + } + + public void setHeartbeat(Date heartbeat) { + this.heartbeat = heartbeat; + } +} diff --git a/server/src/com/cloud/usage/UsageLoadBalancerPolicyVO.java b/server/src/com/cloud/usage/UsageLoadBalancerPolicyVO.java new file mode 100644 index 00000000000..c167e3dbdb3 --- /dev/null +++ b/server/src/com/cloud/usage/UsageLoadBalancerPolicyVO.java @@ -0,0 +1,92 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +@Entity +@Table(name="usage_load_balancer_policy") +public class UsageLoadBalancerPolicyVO { + + @Column(name="zone_id") + private long zoneId; + + @Column(name="account_id") + private long accountId; + + @Column(name="domain_id") + private long domainId; + + @Column(name="id") + private long id; + + @Column(name="created") + @Temporal(value=TemporalType.TIMESTAMP) + private Date created = null; + + @Column(name="deleted") + @Temporal(value=TemporalType.TIMESTAMP) + private Date deleted = null; + + protected UsageLoadBalancerPolicyVO() { + } + + public UsageLoadBalancerPolicyVO(long id, long zoneId, long accountId, long domainId, Date created, Date deleted) { + this.zoneId = zoneId; + this.accountId = accountId; + this.domainId = domainId; + this.id = id; + this.created = created; + this.deleted = deleted; + } + + public long getZoneId() { + return zoneId; + } + + public long getAccountId() { + return accountId; + } + + public long getDomainId() { + return domainId; + } + + public long getId() { + return id; + } + + public Date getCreated() { + return created; + } + + public Date getDeleted() { + return deleted; + } + public void setDeleted(Date deleted) { + this.deleted = deleted; + } +} diff --git a/server/src/com/cloud/usage/UsageNetworkOfferingVO.java b/server/src/com/cloud/usage/UsageNetworkOfferingVO.java new file mode 100644 index 00000000000..b889e69539d --- /dev/null +++ b/server/src/com/cloud/usage/UsageNetworkOfferingVO.java @@ -0,0 +1,108 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +@Entity +@Table(name="usage_network_offering") +public class UsageNetworkOfferingVO { + + @Column(name="zone_id") + private long zoneId; + + @Column(name="account_id") + private long accountId; + + @Column(name="domain_id") + private long domainId; + + @Column(name="vm_instance_id") + private long vmInstanceId; + + @Column(name="network_offering_id") + private Long networkOfferingId; + + @Column(name="is_default") + private boolean isDefault = false; + + @Column(name="created") + @Temporal(value=TemporalType.TIMESTAMP) + private Date created = null; + + @Column(name="deleted") + @Temporal(value=TemporalType.TIMESTAMP) + private Date deleted = null; + + protected UsageNetworkOfferingVO() { + } + + public UsageNetworkOfferingVO(long zoneId, long accountId, long domainId, long vmInstanceId, long networkOfferingId, boolean isDefault, Date created, Date deleted) { + this.zoneId = zoneId; + this.accountId = accountId; + this.domainId = domainId; + this.vmInstanceId = vmInstanceId; + this.networkOfferingId = networkOfferingId; + this.isDefault = isDefault; + this.created = created; + this.deleted = deleted; + } + + public long getZoneId() { + return zoneId; + } + + public long getAccountId() { + return accountId; + } + + public long getDomainId() { + return domainId; + } + + public long getVmInstanceId() { + return vmInstanceId; + } + + public Long getNetworkOfferingId() { + return networkOfferingId; + } + + public boolean isDefault() { + return isDefault; + } + + public Date getCreated() { + return created; + } + + public Date getDeleted() { + return deleted; + } + public void setDeleted(Date deleted) { + this.deleted = deleted; + } +} diff --git a/server/src/com/cloud/usage/UsageNetworkVO.java b/server/src/com/cloud/usage/UsageNetworkVO.java new file mode 100644 index 00000000000..91b5546c38c --- /dev/null +++ b/server/src/com/cloud/usage/UsageNetworkVO.java @@ -0,0 +1,156 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name="usage_network") +public class UsageNetworkVO { + @Id + @Column(name="account_id") + private long accountId; + + @Column(name="zone_id") + private long zoneId; + + @Column(name="host_id") + private long hostId; + + @Column(name="host_type") + private String hostType; + + @Column(name="network_id") + private Long networkId; + + + @Column(name="bytes_sent") + private long bytesSent; + + @Column(name="bytes_received") + private long bytesReceived; + + @Column(name="net_bytes_received") + private long netBytesReceived; + + @Column(name="net_bytes_sent") + private long netBytesSent; + + @Column(name="current_bytes_received") + private long currentBytesReceived; + + @Column(name="current_bytes_sent") + private long currentBytesSent; + + @Column(name="event_time_millis") + private long eventTimeMillis = 0; + + protected UsageNetworkVO() { + } + + public UsageNetworkVO(Long accountId, long zoneId, long hostId, String hostType, Long networkId, long bytesSent, long bytesReceived, long netBytesReceived, long netBytesSent, long currentBytesReceived, + long currentBytesSent, long eventTimeMillis) { + this.accountId = accountId; + this.zoneId = zoneId; + this.hostId = hostId; + this.hostType = hostType; + this.networkId = networkId; + this.bytesSent = bytesSent; + this.bytesReceived = bytesReceived; + this.netBytesReceived = netBytesReceived; + this.netBytesSent = netBytesSent; + this.currentBytesReceived = currentBytesReceived; + this.currentBytesSent = currentBytesSent; + this.eventTimeMillis = eventTimeMillis; + } + + public long getAccountId() { + return accountId; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + public long getZoneId() { + return zoneId; + } + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public Long getBytesSent() { + return bytesSent; + } + + public void setBytesSent(Long bytesSent) { + this.bytesSent = bytesSent; + } + + public Long getBytesReceived() { + return bytesReceived; + } + + public void setBytes(Long bytesReceived) { + this.bytesReceived = bytesReceived; + } + + public long getCurrentBytesReceived() { + return currentBytesReceived; + } + + public long getCurrentBytesSent() { + return currentBytesSent; + } + + public long getNetBytesReceived() { + return netBytesReceived; + } + + public long getNetBytesSent() { + return netBytesSent; + } + + public long getEventTimeMillis() { + return eventTimeMillis; + } + public void setEventTimeMillis(long eventTimeMillis) { + this.eventTimeMillis = eventTimeMillis; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + public long getHostId() { + return hostId; + } + + public String getHostType() { + return hostType; + } + + public Long getNetworkId() { + return networkId; + } +} diff --git a/server/src/com/cloud/usage/UsagePortForwardingRuleVO.java b/server/src/com/cloud/usage/UsagePortForwardingRuleVO.java new file mode 100644 index 00000000000..6f296d11aa0 --- /dev/null +++ b/server/src/com/cloud/usage/UsagePortForwardingRuleVO.java @@ -0,0 +1,92 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +@Entity +@Table(name="usage_port_forwarding") +public class UsagePortForwardingRuleVO { + + @Column(name="zone_id") + private long zoneId; + + @Column(name="account_id") + private long accountId; + + @Column(name="domain_id") + private long domainId; + + @Column(name="id") + private long id; + + @Column(name="created") + @Temporal(value=TemporalType.TIMESTAMP) + private Date created = null; + + @Column(name="deleted") + @Temporal(value=TemporalType.TIMESTAMP) + private Date deleted = null; + + protected UsagePortForwardingRuleVO() { + } + + public UsagePortForwardingRuleVO(long id, long zoneId, long accountId, long domainId, Date created, Date deleted) { + this.zoneId = zoneId; + this.accountId = accountId; + this.domainId = domainId; + this.id = id; + this.created = created; + this.deleted = deleted; + } + + public long getZoneId() { + return zoneId; + } + + public long getAccountId() { + return accountId; + } + + public long getDomainId() { + return domainId; + } + + public long getId() { + return id; + } + + public Date getCreated() { + return created; + } + + public Date getDeleted() { + return deleted; + } + public void setDeleted(Date deleted) { + this.deleted = deleted; + } +} diff --git a/server/src/com/cloud/usage/UsageStorageVO.java b/server/src/com/cloud/usage/UsageStorageVO.java new file mode 100644 index 00000000000..55ca5308490 --- /dev/null +++ b/server/src/com/cloud/usage/UsageStorageVO.java @@ -0,0 +1,116 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +@Entity +@Table(name="usage_storage") +public class UsageStorageVO { + + @Column(name="zone_id") + private long zoneId; + + @Column(name="account_id") + private long accountId; + + @Column(name="domain_id") + private long domainId; + + @Column(name="id") + private long id; + + @Column(name="storage_type") + private int storageType; + + @Column(name="source_id") + private Long sourceId; + + @Column(name="size") + private long size; + + @Column(name="created") + @Temporal(value=TemporalType.TIMESTAMP) + private Date created = null; + + @Column(name="deleted") + @Temporal(value=TemporalType.TIMESTAMP) + private Date deleted = null; + + protected UsageStorageVO() { + } + + public UsageStorageVO(long id, long zoneId, long accountId, long domainId, int storageType, Long sourceId, long size, Date created, Date deleted) { + this.zoneId = zoneId; + this.accountId = accountId; + this.domainId = domainId; + this.id = id; + this.storageType = storageType; + this.sourceId = sourceId; + this.size = size; + this.created = created; + this.deleted = deleted; + } + + public long getZoneId() { + return zoneId; + } + + public long getAccountId() { + return accountId; + } + + public long getDomainId() { + return domainId; + } + + public long getId() { + return id; + } + + public int getStorageType(){ + return storageType; + } + + public Long getSourceId(){ + return sourceId; + } + + public long getSize(){ + return size; + } + + public Date getCreated() { + return created; + } + + public Date getDeleted() { + return deleted; + } + public void setDeleted(Date deleted) { + this.deleted = deleted; + } +} diff --git a/server/src/com/cloud/usage/UsageTypes.java b/server/src/com/cloud/usage/UsageTypes.java new file mode 100644 index 00000000000..913f5491df0 --- /dev/null +++ b/server/src/com/cloud/usage/UsageTypes.java @@ -0,0 +1,59 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage; + +import java.util.ArrayList; +import java.util.List; + +import com.cloud.server.api.response.UsageTypeResponse; + +public class UsageTypes { + public static final int RUNNING_VM = 1; + public static final int ALLOCATED_VM = 2; // used for tracking how long storage has been allocated for a VM + public static final int IP_ADDRESS = 3; + public static final int NETWORK_BYTES_SENT = 4; + public static final int NETWORK_BYTES_RECEIVED = 5; + public static final int VOLUME = 6; + public static final int TEMPLATE = 7; + public static final int ISO = 8; + public static final int SNAPSHOT = 9; + public static final int SECURITY_GROUP = 10; + public static final int LOAD_BALANCER_POLICY = 11; + public static final int PORT_FORWARDING_RULE = 12; + public static final int NETWORK_OFFERING = 13; + + public static List listUsageTypes(){ + List responseList = new ArrayList(); + responseList.add(new UsageTypeResponse(RUNNING_VM, "Running Vm Usage")); + responseList.add(new UsageTypeResponse(ALLOCATED_VM, "Allocated Vm Usage")); + responseList.add(new UsageTypeResponse(IP_ADDRESS, "IP Address Usage")); + responseList.add(new UsageTypeResponse(NETWORK_BYTES_SENT, "Network Usage (Bytes Sent)")); + responseList.add(new UsageTypeResponse(NETWORK_BYTES_RECEIVED, "Network Usage (Bytes Received)")); + responseList.add(new UsageTypeResponse(VOLUME, "Volume Usage")); + responseList.add(new UsageTypeResponse(TEMPLATE, "Template Usage")); + responseList.add(new UsageTypeResponse(ISO, "ISO Usage")); + responseList.add(new UsageTypeResponse(SNAPSHOT, "Snapshot Usage")); + responseList.add(new UsageTypeResponse(SECURITY_GROUP, "Security Group Usage")); + responseList.add(new UsageTypeResponse(LOAD_BALANCER_POLICY, "Load Balancer Usage")); + responseList.add(new UsageTypeResponse(PORT_FORWARDING_RULE, "Port Forwarding Usage")); + responseList.add(new UsageTypeResponse(NETWORK_OFFERING, "Network Offering Usage")); + return responseList; + } +} diff --git a/server/src/com/cloud/usage/UsageVMInstanceVO.java b/server/src/com/cloud/usage/UsageVMInstanceVO.java new file mode 100644 index 00000000000..773bf59ea37 --- /dev/null +++ b/server/src/com/cloud/usage/UsageVMInstanceVO.java @@ -0,0 +1,124 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +@Entity +@Table(name="usage_vm_instance") +public class UsageVMInstanceVO { + @Column(name="usage_type") + private int usageType; + + @Column(name="zone_id") + private long zoneId; + + @Column(name="account_id") + private long accountId; + + @Column(name="vm_instance_id") + private long vmInstanceId; + + @Column(name="vm_name") + private String vmName = null; + + @Column(name="service_offering_id") + private long serviceOfferingId; + + @Column(name="template_id") + private long templateId; + + @Column(name="hypervisor_type") + private String hypervisorType; + + @Column(name="start_date") + @Temporal(value=TemporalType.TIMESTAMP) + private Date startDate = null; + + @Column(name="end_date") + @Temporal(value=TemporalType.TIMESTAMP) + private Date endDate = null; + + protected UsageVMInstanceVO() { + } + + public UsageVMInstanceVO(int usageType, long zoneId, long accountId, long vmInstanceId, String vmName, long serviceOfferingId, + long templateId, String hypervisorType, Date startDate, Date endDate) { + this.usageType = usageType; + this.zoneId = zoneId; + this.accountId = accountId; + this.vmInstanceId = vmInstanceId; + this.vmName = vmName; + this.serviceOfferingId = serviceOfferingId; + this.templateId = templateId; + this.hypervisorType = hypervisorType; + this.startDate = startDate; + this.endDate = endDate; + } + + public int getUsageType() { + return usageType; + } + + public long getZoneId() { + return zoneId; + } + + public long getAccountId() { + return accountId; + } + + public long getVmInstanceId() { + return vmInstanceId; + } + + public String getVmName() { + return vmName; + } + + public long getSerivceOfferingId() { + return serviceOfferingId; + } + + public long getTemplateId() { + return templateId; + } + + public String getHypervisorType() { + return hypervisorType; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + public void setEndDate(Date endDate) { + this.endDate = endDate; + } +} diff --git a/server/src/com/cloud/usage/UsageVO.java b/server/src/com/cloud/usage/UsageVO.java new file mode 100644 index 00000000000..4bca01bfb72 --- /dev/null +++ b/server/src/com/cloud/usage/UsageVO.java @@ -0,0 +1,225 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +@Entity +@Table(name="cloud_usage") +public class UsageVO { + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private Long id = null; + + @Column(name="zone_id") + private Long zoneId = null; + + @Column(name="account_id") + private Long accountId = null; + + @Column(name="domain_id") + private Long domainId = null; + + @Column(name="description") + private String description = null; + + @Column(name="usage_display") + private String usageDisplay = null; + + @Column(name="usage_type") + private int usageType; + + @Column(name="raw_usage") + private Double rawUsage = null; + + @Column(name="vm_instance_id") + private Long vmInstanceId; + + @Column(name="vm_name") + private String vmName = null; + + @Column(name="offering_id") + private Long offeringId = null; + + @Column(name="template_id") + private Long templateId = null; + + @Column(name="usage_id") + private Long usageId = null; + + @Column(name="type") + private String type = null; + + @Column(name="size") + private Long size = null; + + @Column(name="network_id") + private Long networkId = null; + + + @Column(name="start_date") + @Temporal(value=TemporalType.TIMESTAMP) + private Date startDate = null; + + @Column(name="end_date") + @Temporal(value=TemporalType.TIMESTAMP) + private Date endDate = null; + + public UsageVO() { + } + + public UsageVO(Long zoneId, Long accountId, Long domainId, String description, String usageDisplay, + int usageType, Double rawUsage, Long vmId, String vmName, Long offeringId, Long templateId, + Long usageId, Long size, Date startDate, Date endDate) { + this.zoneId = zoneId; + this.accountId = accountId; + this.domainId = domainId; + this.description = description; + this.usageDisplay = usageDisplay; + this.usageType = usageType; + this.rawUsage = rawUsage; + this.vmInstanceId = vmId; + this.vmName = vmName; + this.offeringId = offeringId; + this.templateId = templateId; + this.usageId = usageId; + this.size = size; + this.startDate = startDate; + this.endDate = endDate; + } + + public UsageVO(Long zoneId, Long accountId, Long domainId, String description, String usageDisplay, + int usageType, Double rawUsage, Long usageId, String type, Long networkId, Date startDate, Date endDate) { + this.zoneId = zoneId; + this.accountId = accountId; + this.domainId = domainId; + this.description = description; + this.usageDisplay = usageDisplay; + this.usageType = usageType; + this.rawUsage = rawUsage; + this.usageId = usageId; + this.type = type; + this.networkId = networkId; + this.startDate = startDate; + this.endDate = endDate; + } + + public UsageVO(Long zoneId, Long accountId, Long domainId, String description, String usageDisplay, + int usageType, Double rawUsage, Long vmId, String vmName, Long offeringId, Long templateId, + Long usageId, Date startDate, Date endDate, String type) { + this.zoneId = zoneId; + this.accountId = accountId; + this.domainId = domainId; + this.description = description; + this.usageDisplay = usageDisplay; + this.usageType = usageType; + this.rawUsage = rawUsage; + this.vmInstanceId = vmId; + this.vmName = vmName; + this.offeringId = offeringId; + this.templateId = templateId; + this.usageId = usageId; + this.type = type; + this.startDate = startDate; + this.endDate = endDate; + } + + public Long getId() { + return id; + } + + public Long getZoneId() { + return zoneId; + } + + public Long getAccountId() { + return accountId; + } + + public Long getDomainId() { + return domainId; + } + + public String getDescription() { + return description; + } + + public String getUsageDisplay() { + return usageDisplay; + } + + public int getUsageType() { + return usageType; + } + + public Double getRawUsage() { + return rawUsage; + } + + public Long getVmInstanceId() { + return vmInstanceId; + } + + public String getVmName() { + return vmName; + } + + public Long getOfferingId() { + return offeringId; + } + + public Long getTemplateId() { + return templateId; + } + + public Long getUsageId() { + return usageId; + } + + public String getType() { + return type; + } + + public Long getNetworkId() { + return networkId; + } + + public Long getSize() { + return size; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } +} diff --git a/server/src/com/cloud/usage/UsageVolumeVO.java b/server/src/com/cloud/usage/UsageVolumeVO.java new file mode 100644 index 00000000000..29fa9898b47 --- /dev/null +++ b/server/src/com/cloud/usage/UsageVolumeVO.java @@ -0,0 +1,116 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +@Entity +@Table(name="usage_volume") +public class UsageVolumeVO { + + @Column(name="zone_id") + private long zoneId; + + @Column(name="account_id") + private long accountId; + + @Column(name="domain_id") + private long domainId; + + @Column(name="id") + private long id; + + @Column(name="disk_offering_id") + private Long diskOfferingId; + + @Column(name="template_id") + private Long templateId; + + @Column(name="size") + private long size; + + @Column(name="created") + @Temporal(value=TemporalType.TIMESTAMP) + private Date created = null; + + @Column(name="deleted") + @Temporal(value=TemporalType.TIMESTAMP) + private Date deleted = null; + + protected UsageVolumeVO() { + } + + public UsageVolumeVO(long id, long zoneId, long accountId, long domainId, Long diskOfferingId, Long templateId, long size, Date created, Date deleted) { + this.id = id; + this.zoneId = zoneId; + this.accountId = accountId; + this.domainId = domainId; + this.diskOfferingId = diskOfferingId; + this.templateId = templateId; + this.size = size; + this.created = created; + this.deleted = deleted; + } + + public long getZoneId() { + return zoneId; + } + + public long getAccountId() { + return accountId; + } + + public long getDomainId() { + return domainId; + } + + public long getId() { + return id; + } + + public Long getDiskOfferingId() { + return diskOfferingId; + } + + public Long getTemplateId() { + return templateId; + } + + public long getSize() { + return size; + } + + public Date getCreated() { + return created; + } + + public Date getDeleted() { + return deleted; + } + public void setDeleted(Date deleted) { + this.deleted = deleted; + } +} diff --git a/server/src/com/cloud/usage/dao/ExternalPublicIpStatisticsDao.java b/server/src/com/cloud/usage/dao/ExternalPublicIpStatisticsDao.java new file mode 100644 index 00000000000..631acd3b58b --- /dev/null +++ b/server/src/com/cloud/usage/dao/ExternalPublicIpStatisticsDao.java @@ -0,0 +1,36 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.util.List; + +import com.cloud.usage.ExternalPublicIpStatisticsVO; +import com.cloud.user.UserStatisticsVO; +import com.cloud.utils.db.GenericDao; + +public interface ExternalPublicIpStatisticsDao extends GenericDao { + + ExternalPublicIpStatisticsVO lock(long accountId, long zoneId, String publicIpAddress); + + ExternalPublicIpStatisticsVO findBy(long accountId, long zoneId, String publicIpAddress); + + List listBy(long accountId, long zoneId); + +} diff --git a/server/src/com/cloud/usage/dao/ExternalPublicIpStatisticsDaoImpl.java b/server/src/com/cloud/usage/dao/ExternalPublicIpStatisticsDaoImpl.java new file mode 100644 index 00000000000..ab4b59b16b3 --- /dev/null +++ b/server/src/com/cloud/usage/dao/ExternalPublicIpStatisticsDaoImpl.java @@ -0,0 +1,77 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.util.List; + +import javax.ejb.Local; + +import com.cloud.usage.ExternalPublicIpStatisticsVO; +import com.cloud.user.UserStatisticsVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.vm.dao.DomainRouterDao; + +@Local(value = { ExternalPublicIpStatisticsDao.class }) +public class ExternalPublicIpStatisticsDaoImpl extends GenericDaoBase implements ExternalPublicIpStatisticsDao { + + private final SearchBuilder AccountZoneSearch; + private final SearchBuilder SingleRowSearch; + + public ExternalPublicIpStatisticsDaoImpl() { + AccountZoneSearch = createSearchBuilder(); + AccountZoneSearch.and("accountId", AccountZoneSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + AccountZoneSearch.and("zoneId", AccountZoneSearch.entity().getZoneId(), SearchCriteria.Op.EQ); + AccountZoneSearch.done(); + + SingleRowSearch = createSearchBuilder(); + SingleRowSearch.and("accountId", SingleRowSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + SingleRowSearch.and("zoneId", SingleRowSearch.entity().getZoneId(), SearchCriteria.Op.EQ); + SingleRowSearch.and("publicIp", SingleRowSearch.entity().getPublicIpAddress(), SearchCriteria.Op.EQ); + SingleRowSearch.done(); + } + + public ExternalPublicIpStatisticsVO lock(long accountId, long zoneId, String publicIpAddress) { + SearchCriteria sc = getSingleRowSc(accountId, zoneId, publicIpAddress); + return lockOneRandomRow(sc, true); + } + + public ExternalPublicIpStatisticsVO findBy(long accountId, long zoneId, String publicIpAddress) { + SearchCriteria sc = getSingleRowSc(accountId, zoneId, publicIpAddress); + return findOneBy(sc); + } + + private SearchCriteria getSingleRowSc(long accountId, long zoneId, String publicIpAddress) { + SearchCriteria sc = SingleRowSearch.create(); + sc.setParameters("accountId", accountId); + sc.setParameters("zoneId", zoneId); + sc.setParameters("publicIp", publicIpAddress); + return sc; + } + + public List listBy(long accountId, long zoneId) { + SearchCriteria sc = AccountZoneSearch.create(); + sc.setParameters("accountId", accountId); + sc.setParameters("zoneId", zoneId); + return search(sc, null); + } + +} diff --git a/server/src/com/cloud/usage/dao/UsageDao.java b/server/src/com/cloud/usage/dao/UsageDao.java new file mode 100644 index 00000000000..16ef6dbe24d --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageDao.java @@ -0,0 +1,44 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.util.Date; +import java.util.List; + +import com.cloud.event.UsageEventVO; +import com.cloud.exception.UsageServerException; +import com.cloud.usage.UsageVO; +import com.cloud.user.AccountVO; +import com.cloud.user.UserStatisticsVO; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDao; +import com.cloud.utils.db.SearchCriteria; + +public interface UsageDao extends GenericDao { + void deleteRecordsForAccount(Long accountId); + List searchAllRecords(SearchCriteria sc, Filter filter); + void saveAccounts(List accounts) throws UsageServerException; + void updateAccounts(List accounts) throws UsageServerException; + void saveUserStats(List userStats) throws UsageServerException; + void updateUserStats(List userStats) throws UsageServerException; + Long getLastAccountId() throws UsageServerException; + Long getLastUserStatsId() throws UsageServerException; + List listPublicTemplatesByAccount(long accountId); +} diff --git a/server/src/com/cloud/usage/dao/UsageDaoImpl.java b/server/src/com/cloud/usage/dao/UsageDaoImpl.java new file mode 100644 index 00000000000..c93ba600d83 --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageDaoImpl.java @@ -0,0 +1,268 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.exception.UsageServerException; +import com.cloud.usage.UsageVO; +import com.cloud.user.AccountVO; +import com.cloud.user.UserStatisticsVO; +import com.cloud.utils.DateUtil; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; + +@Local(value={UsageDao.class}) +public class UsageDaoImpl extends GenericDaoBase implements UsageDao { + public static final Logger s_logger = Logger.getLogger(UsageDaoImpl.class.getName()); + private static final String DELETE_ALL = "DELETE FROM cloud_usage"; + private static final String DELETE_ALL_BY_ACCOUNTID = "DELETE FROM cloud_usage WHERE account_id = ?"; + private static final String INSERT_ACCOUNT = "INSERT INTO cloud_usage.account (id, account_name, type, domain_id, removed, cleanup_needed) VALUES (?,?,?,?,?,?)"; + private static final String INSERT_USER_STATS = "INSERT INTO cloud_usage.user_statistics (id, data_center_id, account_id, public_ip_address, device_id, device_type, network_id, net_bytes_received, net_bytes_sent, current_bytes_received, current_bytes_sent) VALUES (?,?,?,?,?,?,?,?,?,?, ?)"; + + private static final String UPDATE_ACCOUNT = "UPDATE cloud_usage.account SET account_name=?, removed=? WHERE id=?"; + private static final String UPDATE_USER_STATS = "UPDATE cloud_usage.user_statistics SET net_bytes_received=?, net_bytes_sent=?, current_bytes_received=?, current_bytes_sent=? WHERE id=?"; + + private static final String GET_LAST_ACCOUNT = "SELECT id FROM cloud_usage.account ORDER BY id DESC LIMIT 1"; + private static final String GET_LAST_USER_STATS = "SELECT id FROM cloud_usage.user_statistics ORDER BY id DESC LIMIT 1"; + private static final String GET_PUBLIC_TEMPLATES_BY_ACCOUNTID = "SELECT id FROM cloud.vm_template WHERE account_id = ? AND public = '1' AND removed IS NULL"; + + protected final static TimeZone s_gmtTimeZone = TimeZone.getTimeZone("GMT"); + + public UsageDaoImpl () {} + + public void deleteRecordsForAccount(Long accountId) { + String sql = ((accountId == null) ? DELETE_ALL : DELETE_ALL_BY_ACCOUNTID); + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + try { + txn.start(); + pstmt = txn.prepareAutoCloseStatement(sql); + if (accountId != null) { + pstmt.setLong(1, accountId.longValue()); + } + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + s_logger.error("error retrieving usage vm instances for account id: " + accountId); + } finally { + txn.close(); + } + } + + @Override + public List searchAllRecords(SearchCriteria sc, Filter filter) { + return listIncludingRemovedBy(sc, filter); + } + + @Override + public void saveAccounts(List accounts) throws UsageServerException { + Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + String sql = INSERT_ACCOUNT; + PreparedStatement pstmt = null; + pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just want CLOUD_USAGE dataSource connection + for (AccountVO acct : accounts) { + pstmt.setLong(1, acct.getId()); + pstmt.setString(2, acct.getAccountName()); + pstmt.setShort(3, acct.getType()); + pstmt.setLong(4, acct.getDomainId()); + + Date removed = acct.getRemoved(); + if (removed == null) { + pstmt.setString(5, null); + } else { + pstmt.setString(5, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), acct.getRemoved())); + } + + pstmt.setBoolean(6, acct.getNeedsCleanup()); + + pstmt.addBatch(); + } + pstmt.executeBatch(); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + s_logger.error("error saving account to cloud_usage db", ex); + throw new UsageServerException(ex.getMessage()); + } + } + + @Override + public void updateAccounts(List accounts) throws UsageServerException { + Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + String sql = UPDATE_ACCOUNT; + PreparedStatement pstmt = null; + pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just want CLOUD_USAGE dataSource connection + for (AccountVO acct : accounts) { + pstmt.setString(1, acct.getAccountName()); + + Date removed = acct.getRemoved(); + if (removed == null) { + pstmt.setString(2, null); + } else { + pstmt.setString(2, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), acct.getRemoved())); + } + + pstmt.setLong(3, acct.getId()); + pstmt.addBatch(); + } + pstmt.executeBatch(); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + s_logger.error("error saving account to cloud_usage db", ex); + throw new UsageServerException(ex.getMessage()); + } + } + + @Override + public void saveUserStats(List userStats) throws UsageServerException { + Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + String sql = INSERT_USER_STATS; + PreparedStatement pstmt = null; + pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just want CLOUD_USAGE dataSource connection + for (UserStatisticsVO userStat : userStats) { + pstmt.setLong(1, userStat.getId()); + pstmt.setLong(2, userStat.getDataCenterId()); + pstmt.setLong(3, userStat.getAccountId()); + pstmt.setString(4, userStat.getPublicIpAddress()); + if(userStat.getDeviceId() != null){ + pstmt.setLong(5, userStat.getDeviceId()); + } else { + pstmt.setNull(5, Types.BIGINT); + } + pstmt.setString(6, userStat.getDeviceType()); + if(userStat.getNetworkId() != null){ + pstmt.setLong(7, userStat.getNetworkId()); + } else { + pstmt.setNull(7, Types.BIGINT); + } + pstmt.setLong(8, userStat.getNetBytesReceived()); + pstmt.setLong(9, userStat.getNetBytesSent()); + pstmt.setLong(10, userStat.getCurrentBytesReceived()); + pstmt.setLong(11, userStat.getCurrentBytesSent()); + pstmt.addBatch(); + } + pstmt.executeBatch(); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + s_logger.error("error saving user stats to cloud_usage db", ex); + throw new UsageServerException(ex.getMessage()); + } + } + + @Override + public void updateUserStats(List userStats) throws UsageServerException { + Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + String sql = UPDATE_USER_STATS; + PreparedStatement pstmt = null; + pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just want CLOUD_USAGE dataSource connection + for (UserStatisticsVO userStat : userStats) { + pstmt.setLong(1, userStat.getNetBytesReceived()); + pstmt.setLong(2, userStat.getNetBytesSent()); + pstmt.setLong(3, userStat.getCurrentBytesReceived()); + pstmt.setLong(4, userStat.getCurrentBytesSent()); + pstmt.setLong(5, userStat.getId()); + pstmt.addBatch(); + } + pstmt.executeBatch(); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + s_logger.error("error saving user stats to cloud_usage db", ex); + throw new UsageServerException(ex.getMessage()); + } + } + + @Override + public Long getLastAccountId() { + Transaction txn = Transaction.currentTxn(); + PreparedStatement pstmt = null; + String sql = GET_LAST_ACCOUNT; + try { + pstmt = txn.prepareAutoCloseStatement(sql); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + return Long.valueOf(rs.getLong(1)); + } + } catch (Exception ex) { + s_logger.error("error getting last account id", ex); + } + return null; + } + + @Override + public Long getLastUserStatsId() { + Transaction txn = Transaction.currentTxn(); + PreparedStatement pstmt = null; + String sql = GET_LAST_USER_STATS; + try { + pstmt = txn.prepareAutoCloseStatement(sql); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + return Long.valueOf(rs.getLong(1)); + } + } catch (Exception ex) { + s_logger.error("error getting last user stats id", ex); + } + return null; + } + + @Override + public List listPublicTemplatesByAccount(long accountId) { + Transaction txn = Transaction.currentTxn(); + PreparedStatement pstmt = null; + String sql = GET_PUBLIC_TEMPLATES_BY_ACCOUNTID; + List templateList = new ArrayList(); + try { + pstmt = txn.prepareAutoCloseStatement(sql); + pstmt.setLong(1, accountId); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + templateList.add(Long.valueOf(rs.getLong(1))); + } + } catch (Exception ex) { + s_logger.error("error listing public templates", ex); + } + return templateList; + } +} diff --git a/server/src/com/cloud/usage/dao/UsageIPAddressDao.java b/server/src/com/cloud/usage/dao/UsageIPAddressDao.java new file mode 100644 index 00000000000..224793b4772 --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageIPAddressDao.java @@ -0,0 +1,31 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.util.Date; +import java.util.List; + +import com.cloud.usage.UsageIPAddressVO; +import com.cloud.utils.db.GenericDao; + +public interface UsageIPAddressDao extends GenericDao { + public void update(UsageIPAddressVO usage); + public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate); +} diff --git a/server/src/com/cloud/usage/dao/UsageIPAddressDaoImpl.java b/server/src/com/cloud/usage/dao/UsageIPAddressDaoImpl.java new file mode 100644 index 00000000000..c5ea0bd83f9 --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageIPAddressDaoImpl.java @@ -0,0 +1,145 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.usage.UsageIPAddressVO; +import com.cloud.user.Account; +import com.cloud.utils.DateUtil; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.Transaction; + +@Local(value={UsageIPAddressDao.class}) +public class UsageIPAddressDaoImpl extends GenericDaoBase implements UsageIPAddressDao { + public static final Logger s_logger = Logger.getLogger(UsageIPAddressDaoImpl.class.getName()); + + protected static final String UPDATE_RELEASED = "UPDATE usage_ip_address SET released = ? WHERE account_id = ? AND public_ip_address = ? and released IS NULL"; + protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, account_id, domain_id, zone_id, public_ip_address, is_source_nat, assigned, released " + + "FROM usage_ip_address " + + "WHERE account_id = ? AND ((released IS NULL AND assigned <= ?) OR (assigned BETWEEN ? AND ?) OR " + + " (released BETWEEN ? AND ?) OR ((assigned <= ?) AND (released >= ?)))"; + protected static final String GET_USAGE_RECORDS_BY_DOMAIN = "SELECT id, account_id, domain_id, zone_id, public_ip_address, is_source_nat, assigned, released " + + "FROM usage_ip_address " + + "WHERE domain_id = ? AND ((released IS NULL AND assigned <= ?) OR (assigned BETWEEN ? AND ?) OR " + + " (released BETWEEN ? AND ?) OR ((assigned <= ?) AND (released >= ?)))"; + protected static final String GET_ALL_USAGE_RECORDS = "SELECT id, account_id, domain_id, zone_id, public_ip_address, is_source_nat, assigned, released " + + "FROM usage_ip_address " + + "WHERE (released IS NULL AND assigned <= ?) OR (assigned BETWEEN ? AND ?) OR " + + " (released BETWEEN ? AND ?) OR ((assigned <= ?) AND (released >= ?))"; + + public UsageIPAddressDaoImpl() {} + + public void update(UsageIPAddressVO usage) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + try { + txn.start(); + if (usage.getReleased() != null) { + pstmt = txn.prepareAutoCloseStatement(UPDATE_RELEASED); + pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), usage.getReleased())); + pstmt.setLong(2, usage.getAccountId()); + pstmt.setString(3, usage.getAddress()); + } + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error updating usageIPAddressVO", e); + } finally { + txn.close(); + } + } + + @Override + public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate) { + List usageRecords = new ArrayList(); + + Long param1 = null; + String sql = null; + if (accountId != null && accountId != Account.ACCOUNT_ID_SYSTEM) { + sql = GET_USAGE_RECORDS_BY_ACCOUNT; + param1 = accountId; + } else if (domainId != null) { + sql = GET_USAGE_RECORDS_BY_DOMAIN; + param1 = domainId; + } else { + sql = GET_ALL_USAGE_RECORDS; + } + + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + + try { + int i = 1; + pstmt = txn.prepareAutoCloseStatement(sql); + if (param1 != null) { + pstmt.setLong(i++, param1); + } + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + //account_id, domain_id, zone_id, address, assigned, released + Long id = Long.valueOf(rs.getLong(1)); + Long acctId = Long.valueOf(rs.getLong(2)); + Long dId = Long.valueOf(rs.getLong(3)); + Long zId = Long.valueOf(rs.getLong(4)); + String addr = rs.getString(5); + Boolean isSourceNat = Boolean.valueOf(rs.getBoolean(6)); + Date assignedDate = null; + Date releasedDate = null; + String assignedTS = rs.getString(7); + String releasedTS = rs.getString(8); + + if (assignedTS != null) { + assignedDate = DateUtil.parseDateString(s_gmtTimeZone, assignedTS); + } + if (releasedTS != null) { + releasedDate = DateUtil.parseDateString(s_gmtTimeZone, releasedTS); + } + + usageRecords.add(new UsageIPAddressVO(id, acctId, dId, zId, addr, isSourceNat, assignedDate, releasedDate)); + } + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error getting usage records", e); + } finally { + txn.close(); + } + + return usageRecords; + } +} diff --git a/server/src/com/cloud/usage/dao/UsageJobDao.java b/server/src/com/cloud/usage/dao/UsageJobDao.java new file mode 100644 index 00000000000..918192c06a7 --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageJobDao.java @@ -0,0 +1,37 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.util.Date; + +import com.cloud.exception.UsageServerException; +import com.cloud.usage.UsageJobVO; +import com.cloud.utils.db.GenericDao; + +public interface UsageJobDao extends GenericDao { + Long checkHeartbeat(String hostname, int pid, int aggregationDuration); + void createNewJob(String hostname, int pid, int jobType); + UsageJobVO getLastJob(); + UsageJobVO getNextImmediateJob(); + long getLastJobSuccessDateMillis(); + Date getLastHeartbeat(); + UsageJobVO isOwner(String hostname, int pid); + void updateJobSuccess(Long jobId, long startMillis, long endMillis, long execTime, boolean success) throws UsageServerException; +} diff --git a/server/src/com/cloud/usage/dao/UsageJobDaoImpl.java b/server/src/com/cloud/usage/dao/UsageJobDaoImpl.java new file mode 100644 index 00000000000..e1234e5663f --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageJobDaoImpl.java @@ -0,0 +1,201 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Date; +import java.util.List; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.exception.UsageServerException; +import com.cloud.usage.UsageJobVO; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; + +@Local(value={UsageJobDao.class}) +public class UsageJobDaoImpl extends GenericDaoBase implements UsageJobDao { + private static final Logger s_logger = Logger.getLogger(UsageJobDaoImpl.class.getName()); + + private static final String GET_LAST_JOB_SUCCESS_DATE_MILLIS = "SELECT end_millis FROM cloud_usage.usage_job WHERE end_millis > 0 ORDER BY end_millis DESC LIMIT 1"; + + @Override + public long getLastJobSuccessDateMillis() { + Transaction txn = Transaction.currentTxn(); + PreparedStatement pstmt = null; + String sql = GET_LAST_JOB_SUCCESS_DATE_MILLIS; + try { + pstmt = txn.prepareAutoCloseStatement(sql); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + return rs.getLong(1); + } + } catch (Exception ex) { + s_logger.error("error getting last usage job success date", ex); + } finally { + txn.close(); + } + return 0L; + } + + @Override + public void updateJobSuccess(Long jobId, long startMillis, long endMillis, long execTime, boolean success) throws UsageServerException { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + try { + txn.start(); + + UsageJobVO job = lockRow(jobId, Boolean.TRUE); + UsageJobVO jobForUpdate = createForUpdate(); + jobForUpdate.setStartMillis(startMillis); + jobForUpdate.setEndMillis(endMillis); + jobForUpdate.setExecTime(execTime); + jobForUpdate.setStartDate(new Date(startMillis)); + jobForUpdate.setEndDate(new Date(endMillis)); + jobForUpdate.setSuccess(success); + update(job.getId(), jobForUpdate); + + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + s_logger.error("error updating job success date", ex); + throw new UsageServerException(ex.getMessage()); + } finally { + txn.close(); + } + } + + @Override + public Long checkHeartbeat(String hostname, int pid, int aggregationDuration) { + UsageJobVO job = getNextRecurringJob(); + if (job == null) { + return null; + } + + if (job.getHost().equals(hostname) && (job.getPid() != null) && (job.getPid().intValue() == pid)) { + return job.getId(); + } + + Date lastHeartbeat = job.getHeartbeat(); + if (lastHeartbeat == null) { + return null; + } + + long sinceLastHeartbeat = System.currentTimeMillis() - lastHeartbeat.getTime(); + + // TODO: Make this check a little smarter..but in the mean time we want the mgmt + // server to monitor the usage server, we need to make sure other usage + // servers take over as the usage job owner more aggressively. For now + // this is hardcoded to 5 minutes. + if (sinceLastHeartbeat > (5 * 60 * 1000)) { + return job.getId(); + } + return null; + } + + @Override + public UsageJobVO isOwner(String hostname, int pid) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + try { + if ((hostname == null) || (pid <= 0)) { + return null; + } + + UsageJobVO job = getLastJob(); + if (job == null) { + return null; + } + + if (hostname.equals(job.getHost()) && (job.getPid() != null) && (pid == job.getPid().intValue())) { + return job; + } + } finally { + txn.close(); + } + return null; + } + + @Override + public void createNewJob(String hostname, int pid, int jobType) { + UsageJobVO newJob = new UsageJobVO(); + newJob.setHost(hostname); + newJob.setPid(pid); + newJob.setHeartbeat(new Date()); + newJob.setJobType(jobType); + persist(newJob); + } + + @Override + public UsageJobVO getLastJob() { + Filter filter = new Filter(UsageJobVO.class, "id", false, Long.valueOf(0), Long.valueOf(1)); + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0)); + List jobs = search(sc, filter); + + if ((jobs == null) || jobs.isEmpty()) { + return null; + } + return jobs.get(0); + } + + private UsageJobVO getNextRecurringJob() { + Filter filter = new Filter(UsageJobVO.class, "id", false, Long.valueOf(0), Long.valueOf(1)); + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0)); + sc.addAnd("jobType", SearchCriteria.Op.EQ, Integer.valueOf(UsageJobVO.JOB_TYPE_RECURRING)); + List jobs = search(sc, filter); + + if ((jobs == null) || jobs.isEmpty()) { + return null; + } + return jobs.get(0); + } + + @Override + public UsageJobVO getNextImmediateJob() { + Filter filter = new Filter(UsageJobVO.class, "id", false, Long.valueOf(0), Long.valueOf(1)); + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0)); + sc.addAnd("jobType", SearchCriteria.Op.EQ, Integer.valueOf(UsageJobVO.JOB_TYPE_SINGLE)); + sc.addAnd("scheduled", SearchCriteria.Op.EQ, Integer.valueOf(0)); + List jobs = search(sc, filter); + + if ((jobs == null) || jobs.isEmpty()) { + return null; + } + return jobs.get(0); + } + + @Override + public Date getLastHeartbeat() { + Filter filter = new Filter(UsageJobVO.class, "heartbeat", false, Long.valueOf(0), Long.valueOf(1)); + SearchCriteria sc = createSearchCriteria(); + List jobs = search(sc, filter); + + if ((jobs == null) || jobs.isEmpty()) { + return null; + } + return jobs.get(0).getHeartbeat(); + } +} diff --git a/server/src/com/cloud/usage/dao/UsageLoadBalancerPolicyDao.java b/server/src/com/cloud/usage/dao/UsageLoadBalancerPolicyDao.java new file mode 100644 index 00000000000..0fcf36dbfb2 --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageLoadBalancerPolicyDao.java @@ -0,0 +1,32 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.util.Date; +import java.util.List; + +import com.cloud.usage.UsageLoadBalancerPolicyVO; +import com.cloud.utils.db.GenericDao; + +public interface UsageLoadBalancerPolicyDao extends GenericDao { + public void removeBy(long userId, long id); + public void update(UsageLoadBalancerPolicyVO usage); + public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate, boolean limit, int page); +} diff --git a/server/src/com/cloud/usage/dao/UsageLoadBalancerPolicyDaoImpl.java b/server/src/com/cloud/usage/dao/UsageLoadBalancerPolicyDaoImpl.java new file mode 100644 index 00000000000..1f0b99fea87 --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageLoadBalancerPolicyDaoImpl.java @@ -0,0 +1,170 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.usage.UsageLoadBalancerPolicyVO; +import com.cloud.utils.DateUtil; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.Transaction; + +@Local(value={UsageLoadBalancerPolicyDao.class}) +public class UsageLoadBalancerPolicyDaoImpl extends GenericDaoBase implements UsageLoadBalancerPolicyDao { + public static final Logger s_logger = Logger.getLogger(UsageLoadBalancerPolicyDaoImpl.class.getName()); + + protected static final String REMOVE_BY_USERID_LBID = "DELETE FROM usage_load_balancer_policy WHERE account_id = ? AND id = ?"; + protected static final String UPDATE_DELETED = "UPDATE usage_load_balancer_policy SET deleted = ? WHERE account_id = ? AND id = ? and deleted IS NULL"; + protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, zone_id, account_id, domain_id, created, deleted " + + "FROM usage_load_balancer_policy " + + "WHERE account_id = ? AND ((deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?)))"; + protected static final String GET_USAGE_RECORDS_BY_DOMAIN = "SELECT id, zone_id, account_id, domain_id, created, deleted " + + "FROM usage_load_balancer_policy " + + "WHERE domain_id = ? AND ((deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?)))"; + protected static final String GET_ALL_USAGE_RECORDS = "SELECT id, zone_id, account_id, domain_id, created, deleted " + + "FROM usage_load_balancer_policy " + + "WHERE (deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?))"; + + public UsageLoadBalancerPolicyDaoImpl() {} + + public void removeBy(long accountId, long lbId) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + try { + txn.start(); + String sql = REMOVE_BY_USERID_LBID; + pstmt = txn.prepareAutoCloseStatement(sql); + pstmt.setLong(1, accountId); + pstmt.setLong(2, lbId); + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error removing UsageLoadBalancerPolicyVO", e); + } finally { + txn.close(); + } + } + + public void update(UsageLoadBalancerPolicyVO usage) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + try { + txn.start(); + if (usage.getDeleted() != null) { + pstmt = txn.prepareAutoCloseStatement(UPDATE_DELETED); + pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), usage.getDeleted())); + pstmt.setLong(2, usage.getAccountId()); + pstmt.setLong(3, usage.getId()); + } + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error updating UsageLoadBalancerPolicyVO", e); + } finally { + txn.close(); + } + } + + @Override + public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate, boolean limit, int page) { + List usageRecords = new ArrayList(); + + Long param1 = null; + String sql = null; + if (accountId != null) { + sql = GET_USAGE_RECORDS_BY_ACCOUNT; + param1 = accountId; + } else if (domainId != null) { + sql = GET_USAGE_RECORDS_BY_DOMAIN; + param1 = domainId; + } else { + sql = GET_ALL_USAGE_RECORDS; + } + + if (limit) { + int startIndex = 0; + if (page > 0) { + startIndex = 500 * (page-1); + } + sql += " LIMIT " + startIndex + ",500"; + } + + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + + try { + int i = 1; + pstmt = txn.prepareAutoCloseStatement(sql); + if (param1 != null) { + pstmt.setLong(i++, param1); + } + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + //id, zone_id, account_id, domain_id, created, deleted + Long lbId = Long.valueOf(rs.getLong(1)); + Long zoneId = Long.valueOf(rs.getLong(2)); + Long acctId = Long.valueOf(rs.getLong(3)); + Long dId = Long.valueOf(rs.getLong(4)); + Date createdDate = null; + Date deletedDate = null; + String createdTS = rs.getString(5); + String deletedTS = rs.getString(6); + + + if (createdTS != null) { + createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); + } + if (deletedTS != null) { + deletedDate = DateUtil.parseDateString(s_gmtTimeZone, deletedTS); + } + + usageRecords.add(new UsageLoadBalancerPolicyVO(lbId, zoneId, acctId, dId, createdDate, deletedDate)); + } + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error getting usage records", e); + } finally { + txn.close(); + } + + return usageRecords; + } +} diff --git a/server/src/com/cloud/usage/dao/UsageNetworkDao.java b/server/src/com/cloud/usage/dao/UsageNetworkDao.java new file mode 100644 index 00000000000..c14555f1373 --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageNetworkDao.java @@ -0,0 +1,30 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.util.Map; + +import com.cloud.usage.UsageNetworkVO; +import com.cloud.utils.db.GenericDao; + +public interface UsageNetworkDao extends GenericDao { + Map getRecentNetworkStats(); + void deleteOldStats(long maxEventTime); +} diff --git a/server/src/com/cloud/usage/dao/UsageNetworkDaoImpl.java b/server/src/com/cloud/usage/dao/UsageNetworkDaoImpl.java new file mode 100644 index 00000000000..7e17cb87200 --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageNetworkDaoImpl.java @@ -0,0 +1,104 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.HashMap; +import java.util.Map; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.usage.UsageNetworkVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.Transaction; + +@Local(value={UsageNetworkDao.class}) +public class UsageNetworkDaoImpl extends GenericDaoBase implements UsageNetworkDao { + private static final Logger s_logger = Logger.getLogger(UsageVMInstanceDaoImpl.class.getName()); + private static final String SELECT_LATEST_STATS = "SELECT u.account_id, u.zone_id, u.host_id, u.host_type, u.network_id, u.bytes_sent, u.bytes_received, u.net_bytes_received, u.net_bytes_sent, " + + "u.current_bytes_received, u.current_bytes_sent, u.event_time_millis " + + "FROM cloud_usage.usage_network u INNER JOIN (SELECT netusage.account_id as acct_id, netusage.zone_id as z_id, max(netusage.event_time_millis) as max_date " + + "FROM cloud_usage.usage_network netusage " + + "GROUP BY netusage.account_id, netusage.zone_id " + + ") joinnet on u.account_id = joinnet.acct_id and u.zone_id = joinnet.z_id and u.event_time_millis = joinnet.max_date"; + private static final String DELETE_OLD_STATS = "DELETE FROM cloud_usage.usage_network WHERE event_time_millis < ?"; + + public UsageNetworkDaoImpl() { + } + + @Override + public Map getRecentNetworkStats() { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + String sql = SELECT_LATEST_STATS; + PreparedStatement pstmt = null; + try { + pstmt = txn.prepareAutoCloseStatement(sql); + ResultSet rs = pstmt.executeQuery(); + Map returnMap = new HashMap(); + while (rs.next()) { + long accountId = rs.getLong(1); + long zoneId = rs.getLong(2); + Long hostId = rs.getLong(3); + String hostType = rs.getString(4); + Long networkId = rs.getLong(5); + long bytesSent = rs.getLong(6); + long bytesReceived = rs.getLong(7); + long netBytesReceived = rs.getLong(8); + long netBytesSent = rs.getLong(9); + long currentBytesReceived = rs.getLong(10); + long currentBytesSent = rs.getLong(11); + long eventTimeMillis = rs.getLong(12); + if(hostId != 0){ + returnMap.put(zoneId + "-" + accountId+ "-Host-" + hostId, new UsageNetworkVO(accountId, zoneId, hostId, hostType, networkId, bytesSent, bytesReceived, netBytesReceived, netBytesSent, + currentBytesReceived, currentBytesSent, eventTimeMillis)); + } else { + returnMap.put(zoneId + "-" + accountId, new UsageNetworkVO(accountId, zoneId, hostId, hostType, networkId, bytesSent, bytesReceived, netBytesReceived, netBytesSent, currentBytesReceived, + currentBytesSent, eventTimeMillis)); + } + } + return returnMap; + } catch (Exception ex) { + s_logger.error("error getting recent usage network stats", ex); + } finally { + txn.close(); + } + return null; + } + + @Override + public void deleteOldStats(long maxEventTime) { + Transaction txn = Transaction.currentTxn(); + String sql = DELETE_OLD_STATS; + PreparedStatement pstmt = null; + try { + txn.start(); + pstmt = txn.prepareAutoCloseStatement(sql); + pstmt.setLong(1, maxEventTime); + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + s_logger.error("error deleting old usage network stats", ex); + } + } +} diff --git a/server/src/com/cloud/usage/dao/UsageNetworkOfferingDao.java b/server/src/com/cloud/usage/dao/UsageNetworkOfferingDao.java new file mode 100644 index 00000000000..186e849416f --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageNetworkOfferingDao.java @@ -0,0 +1,31 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.util.Date; +import java.util.List; + +import com.cloud.usage.UsageNetworkOfferingVO; +import com.cloud.utils.db.GenericDao; + +public interface UsageNetworkOfferingDao extends GenericDao { + public void update(UsageNetworkOfferingVO usage); + public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate, boolean limit, int page); +} diff --git a/server/src/com/cloud/usage/dao/UsageNetworkOfferingDaoImpl.java b/server/src/com/cloud/usage/dao/UsageNetworkOfferingDaoImpl.java new file mode 100644 index 00000000000..941967c358d --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageNetworkOfferingDaoImpl.java @@ -0,0 +1,153 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.usage.UsageNetworkOfferingVO; +import com.cloud.utils.DateUtil; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.Transaction; + +@Local(value={UsageNetworkOfferingDao.class}) +public class UsageNetworkOfferingDaoImpl extends GenericDaoBase implements UsageNetworkOfferingDao { + public static final Logger s_logger = Logger.getLogger(UsageNetworkOfferingDaoImpl.class.getName()); + + protected static final String UPDATE_DELETED = "UPDATE usage_network_offering SET deleted = ? WHERE account_id = ? AND vm_instance_id = ? AND network_offering_id = ? and deleted IS NULL"; + protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT zone_id, account_id, domain_id, vm_instance_id, network_offering_id, is_default, created, deleted " + + "FROM usage_network_offering " + + "WHERE account_id = ? AND ((deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?)))"; + protected static final String GET_USAGE_RECORDS_BY_DOMAIN = "SELECT zone_id, account_id, domain_id, vm_instance_id, network_offering_id, is_default, created, deleted " + + "FROM usage_network_offering " + + "WHERE domain_id = ? AND ((deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?)))"; + protected static final String GET_ALL_USAGE_RECORDS = "SELECT zone_id, account_id, domain_id, vm_instance_id, network_offering_id, is_default, created, deleted " + + "FROM usage_network_offering " + + "WHERE (deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?))"; + + public UsageNetworkOfferingDaoImpl() {} + + public void update(UsageNetworkOfferingVO usage) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + try { + txn.start(); + if (usage.getDeleted() != null) { + pstmt = txn.prepareAutoCloseStatement(UPDATE_DELETED); + pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), usage.getDeleted())); + pstmt.setLong(2, usage.getAccountId()); + pstmt.setLong(3, usage.getVmInstanceId()); + pstmt.setLong(4, usage.getNetworkOfferingId()); + } + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error updating UsageNetworkOfferingVO", e); + } finally { + txn.close(); + } + } + + @Override + public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate, boolean limit, int page) { + List usageRecords = new ArrayList(); + + Long param1 = null; + String sql = null; + if (accountId != null) { + sql = GET_USAGE_RECORDS_BY_ACCOUNT; + param1 = accountId; + } else if (domainId != null) { + sql = GET_USAGE_RECORDS_BY_DOMAIN; + param1 = domainId; + } else { + sql = GET_ALL_USAGE_RECORDS; + } + + if (limit) { + int startIndex = 0; + if (page > 0) { + startIndex = 500 * (page-1); + } + sql += " LIMIT " + startIndex + ",500"; + } + + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + + try { + int i = 1; + pstmt = txn.prepareAutoCloseStatement(sql); + if (param1 != null) { + pstmt.setLong(i++, param1); + } + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + //zoneId, account_id, domain_id, vm_instance_id, network_offering_id, is_default, created, deleted + Long zoneId = Long.valueOf(rs.getLong(1)); + Long acctId = Long.valueOf(rs.getLong(2)); + Long dId = Long.valueOf(rs.getLong(3)); + long vmId = Long.valueOf(rs.getLong(4)); + long noId = Long.valueOf(rs.getLong(5)); + boolean isDefault = Boolean.valueOf(rs.getBoolean(6)); + Date createdDate = null; + Date deletedDate = null; + String createdTS = rs.getString(7); + String deletedTS = rs.getString(8); + + + if (createdTS != null) { + createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); + } + if (deletedTS != null) { + deletedDate = DateUtil.parseDateString(s_gmtTimeZone, deletedTS); + } + + usageRecords.add(new UsageNetworkOfferingVO(zoneId, acctId, dId, vmId, noId, isDefault, createdDate, deletedDate)); + } + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error getting usage records", e); + } finally { + txn.close(); + } + + return usageRecords; + } +} diff --git a/server/src/com/cloud/usage/dao/UsagePortForwardingRuleDao.java b/server/src/com/cloud/usage/dao/UsagePortForwardingRuleDao.java new file mode 100644 index 00000000000..62745626e67 --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsagePortForwardingRuleDao.java @@ -0,0 +1,32 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.util.Date; +import java.util.List; + +import com.cloud.usage.UsagePortForwardingRuleVO; +import com.cloud.utils.db.GenericDao; + +public interface UsagePortForwardingRuleDao extends GenericDao { + public void removeBy(long userId, long id); + public void update(UsagePortForwardingRuleVO usage); + public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate, boolean limit, int page); +} diff --git a/server/src/com/cloud/usage/dao/UsagePortForwardingRuleDaoImpl.java b/server/src/com/cloud/usage/dao/UsagePortForwardingRuleDaoImpl.java new file mode 100644 index 00000000000..b82ad440c91 --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsagePortForwardingRuleDaoImpl.java @@ -0,0 +1,170 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.usage.UsagePortForwardingRuleVO; +import com.cloud.utils.DateUtil; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.Transaction; + +@Local(value={UsagePortForwardingRuleDao.class}) +public class UsagePortForwardingRuleDaoImpl extends GenericDaoBase implements UsagePortForwardingRuleDao { + public static final Logger s_logger = Logger.getLogger(UsagePortForwardingRuleDaoImpl.class.getName()); + + protected static final String REMOVE_BY_USERID_PFID = "DELETE FROM usage_port_forwarding WHERE account_id = ? AND id = ?"; + protected static final String UPDATE_DELETED = "UPDATE usage_port_forwarding SET deleted = ? WHERE account_id = ? AND id = ? and deleted IS NULL"; + protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, zone_id, account_id, domain_id, created, deleted " + + "FROM usage_port_forwarding " + + "WHERE account_id = ? AND ((deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?)))"; + protected static final String GET_USAGE_RECORDS_BY_DOMAIN = "SELECT id, zone_id, account_id, domain_id, created, deleted " + + "FROM usage_port_forwarding " + + "WHERE domain_id = ? AND ((deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?)))"; + protected static final String GET_ALL_USAGE_RECORDS = "SELECT id, zone_id, account_id, domain_id, created, deleted " + + "FROM usage_port_forwarding " + + "WHERE (deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?))"; + + public UsagePortForwardingRuleDaoImpl() {} + + public void removeBy(long accountId, long pfId) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + try { + txn.start(); + String sql = REMOVE_BY_USERID_PFID; + pstmt = txn.prepareAutoCloseStatement(sql); + pstmt.setLong(1, accountId); + pstmt.setLong(2, pfId); + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error removing UsagePortForwardingRuleVO", e); + } finally { + txn.close(); + } + } + + public void update(UsagePortForwardingRuleVO usage) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + try { + txn.start(); + if (usage.getDeleted() != null) { + pstmt = txn.prepareAutoCloseStatement(UPDATE_DELETED); + pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), usage.getDeleted())); + pstmt.setLong(2, usage.getAccountId()); + pstmt.setLong(3, usage.getId()); + } + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error updating UsagePortForwardingRuleVO", e); + } finally { + txn.close(); + } + } + + @Override + public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate, boolean limit, int page) { + List usageRecords = new ArrayList(); + + Long param1 = null; + String sql = null; + if (accountId != null) { + sql = GET_USAGE_RECORDS_BY_ACCOUNT; + param1 = accountId; + } else if (domainId != null) { + sql = GET_USAGE_RECORDS_BY_DOMAIN; + param1 = domainId; + } else { + sql = GET_ALL_USAGE_RECORDS; + } + + if (limit) { + int startIndex = 0; + if (page > 0) { + startIndex = 500 * (page-1); + } + sql += " LIMIT " + startIndex + ",500"; + } + + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + + try { + int i = 1; + pstmt = txn.prepareAutoCloseStatement(sql); + if (param1 != null) { + pstmt.setLong(i++, param1); + } + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + //id, zone_id, account_id, domain_id, created, deleted + Long pfId = Long.valueOf(rs.getLong(1)); + Long zoneId = Long.valueOf(rs.getLong(2)); + Long acctId = Long.valueOf(rs.getLong(3)); + Long dId = Long.valueOf(rs.getLong(4)); + Date createdDate = null; + Date deletedDate = null; + String createdTS = rs.getString(5); + String deletedTS = rs.getString(6); + + + if (createdTS != null) { + createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); + } + if (deletedTS != null) { + deletedDate = DateUtil.parseDateString(s_gmtTimeZone, deletedTS); + } + + usageRecords.add(new UsagePortForwardingRuleVO(pfId, zoneId, acctId, dId, createdDate, deletedDate)); + } + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error getting usage records", e); + } finally { + txn.close(); + } + + return usageRecords; + } +} diff --git a/server/src/com/cloud/usage/dao/UsageStorageDao.java b/server/src/com/cloud/usage/dao/UsageStorageDao.java new file mode 100644 index 00000000000..a935f13c4ed --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageStorageDao.java @@ -0,0 +1,34 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.util.Date; +import java.util.List; + +import com.cloud.usage.UsageStorageVO; +import com.cloud.utils.db.GenericDao; + +public interface UsageStorageDao extends GenericDao { + public void removeBy(long userId, long id, int storage_type); + public void update(UsageStorageVO usage); + public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate, boolean limit, int page); + List listById(long accountId, long id, int type); + List listByIdAndZone(long accountId, long id, int type, long dcId); +} diff --git a/server/src/com/cloud/usage/dao/UsageStorageDaoImpl.java b/server/src/com/cloud/usage/dao/UsageStorageDaoImpl.java new file mode 100644 index 00000000000..ecb36682a2e --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageStorageDaoImpl.java @@ -0,0 +1,212 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.usage.UsageStorageVO; +import com.cloud.utils.DateUtil; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; + +@Local(value={UsageStorageDao.class}) +public class UsageStorageDaoImpl extends GenericDaoBase implements UsageStorageDao { + public static final Logger s_logger = Logger.getLogger(UsageStorageDaoImpl.class.getName()); + + protected static final String REMOVE_BY_USERID_STORAGEID = "DELETE FROM usage_storage WHERE account_id = ? AND id = ? AND storage_type = ?"; + protected static final String UPDATE_DELETED = "UPDATE usage_storage SET deleted = ? WHERE account_id = ? AND id = ? AND storage_type = ? and deleted IS NULL"; + protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, zone_id, account_id, domain_id, storage_type, source_id, size, created, deleted " + + "FROM usage_storage " + + "WHERE account_id = ? AND ((deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?)))"; + protected static final String GET_USAGE_RECORDS_BY_DOMAIN = "SELECT id, zone_id, account_id, domain_id, storage_type, source_id, size, created, deleted " + + "FROM usage_storage " + + "WHERE domain_id = ? AND ((deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?)))"; + protected static final String GET_ALL_USAGE_RECORDS = "SELECT id, zone_id, account_id, domain_id, storage_type, source_id, size, created, deleted " + + "FROM usage_storage " + + "WHERE (deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?))"; + + private final SearchBuilder IdSearch; + private final SearchBuilder IdZoneSearch; + + public UsageStorageDaoImpl() { + IdSearch = createSearchBuilder(); + IdSearch.and("accountId", IdSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + IdSearch.and("id", IdSearch.entity().getId(), SearchCriteria.Op.EQ); + IdSearch.and("type", IdSearch.entity().getStorageType(), SearchCriteria.Op.EQ); + IdSearch.done(); + + IdZoneSearch = createSearchBuilder(); + IdZoneSearch.and("accountId", IdZoneSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + IdZoneSearch.and("id", IdZoneSearch.entity().getId(), SearchCriteria.Op.EQ); + IdZoneSearch.and("type", IdZoneSearch.entity().getStorageType(), SearchCriteria.Op.EQ); + IdZoneSearch.and("dcId", IdZoneSearch.entity().getZoneId(), SearchCriteria.Op.EQ); + IdZoneSearch.done(); + } + + @Override + public List listById(long accountId, long id, int type) { + SearchCriteria sc = IdSearch.create(); + sc.setParameters("accountId", accountId); + sc.setParameters("id", id); + sc.setParameters("type", type); + return listBy(sc, null); + } + + @Override + public List listByIdAndZone(long accountId, long id, int type, long dcId) { + SearchCriteria sc = IdZoneSearch.create(); + sc.setParameters("accountId", accountId); + sc.setParameters("id", id); + sc.setParameters("type", type); + sc.setParameters("dcId", dcId); + return listBy(sc, null); + } + + public void removeBy(long accountId, long volId, int storage_type) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + try { + txn.start(); + String sql = REMOVE_BY_USERID_STORAGEID; + pstmt = txn.prepareAutoCloseStatement(sql); + pstmt.setLong(1, accountId); + pstmt.setLong(2, volId); + pstmt.setInt(3, storage_type); + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error removing usageStorageVO", e); + } finally { + txn.close(); + } + } + + public void update(UsageStorageVO usage) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + try { + txn.start(); + if (usage.getDeleted() != null) { + pstmt = txn.prepareAutoCloseStatement(UPDATE_DELETED); + pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), usage.getDeleted())); + pstmt.setLong(2, usage.getAccountId()); + pstmt.setLong(3, usage.getId()); + pstmt.setInt(4, usage.getStorageType()); + } + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error updating UsageStorageVO", e); + } finally { + txn.close(); + } + } + + @Override + public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate, boolean limit, int page) { + List usageRecords = new ArrayList(); + + Long param1 = null; + String sql = null; + if (accountId != null) { + sql = GET_USAGE_RECORDS_BY_ACCOUNT; + param1 = accountId; + } else if (domainId != null) { + sql = GET_USAGE_RECORDS_BY_DOMAIN; + param1 = domainId; + } else { + sql = GET_ALL_USAGE_RECORDS; + } + + if (limit) { + int startIndex = 0; + if (page > 0) { + startIndex = 500 * (page-1); + } + sql += " LIMIT " + startIndex + ",500"; + } + + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + + try { + int i = 1; + pstmt = txn.prepareAutoCloseStatement(sql); + if (param1 != null) { + pstmt.setLong(i++, param1); + } + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + //id, zone_id, account_id, domain_id, storage_type, size, created, deleted + Long id = Long.valueOf(rs.getLong(1)); + Long zoneId = Long.valueOf(rs.getLong(2)); + Long acctId = Long.valueOf(rs.getLong(3)); + Long dId = Long.valueOf(rs.getLong(4)); + Integer type = Integer.valueOf(rs.getInt(5)); + Long sourceId = Long.valueOf(rs.getLong(6)); + Long size = Long.valueOf(rs.getLong(7)); + Date createdDate = null; + Date deletedDate = null; + String createdTS = rs.getString(8); + String deletedTS = rs.getString(9); + + + if (createdTS != null) { + createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); + } + if (deletedTS != null) { + deletedDate = DateUtil.parseDateString(s_gmtTimeZone, deletedTS); + } + + usageRecords.add(new UsageStorageVO(id, zoneId, acctId, dId, type, sourceId, size, createdDate, deletedDate)); + } + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error getting usage records", e); + } finally { + txn.close(); + } + + return usageRecords; + } +} diff --git a/server/src/com/cloud/usage/dao/UsageVMInstanceDao.java b/server/src/com/cloud/usage/dao/UsageVMInstanceDao.java new file mode 100644 index 00000000000..1c4ace76a7e --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageVMInstanceDao.java @@ -0,0 +1,32 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.util.Date; +import java.util.List; + +import com.cloud.usage.UsageVMInstanceVO; +import com.cloud.utils.db.GenericDao; + +public interface UsageVMInstanceDao extends GenericDao { + public void update(UsageVMInstanceVO instance); + public void delete(UsageVMInstanceVO instance); + public List getUsageRecords(long userId, Date startDate, Date endDate); +} diff --git a/server/src/com/cloud/usage/dao/UsageVMInstanceDaoImpl.java b/server/src/com/cloud/usage/dao/UsageVMInstanceDaoImpl.java new file mode 100644 index 00000000000..1fb5b8d5a3b --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageVMInstanceDaoImpl.java @@ -0,0 +1,138 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.usage.UsageVMInstanceVO; +import com.cloud.utils.DateUtil; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.Transaction; + +@Local(value={UsageVMInstanceDao.class}) +public class UsageVMInstanceDaoImpl extends GenericDaoBase implements UsageVMInstanceDao { + public static final Logger s_logger = Logger.getLogger(UsageVMInstanceDaoImpl.class.getName()); + + protected static final String UPDATE_USAGE_INSTANCE_SQL = + "UPDATE usage_vm_instance SET end_date = ? " + + "WHERE account_id = ? and vm_instance_id = ? and usage_type = ? and end_date IS NULL"; + protected static final String DELETE_USAGE_INSTANCE_SQL = + "DELETE FROM usage_vm_instance WHERE account_id = ? and vm_instance_id = ? and usage_type = ?"; + protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT usage_type, zone_id, account_id, vm_instance_id, vm_name, service_offering_id, template_id, hypervisor_type, start_date, end_date " + + "FROM usage_vm_instance " + + "WHERE account_id = ? AND ((end_date IS NULL) OR (start_date BETWEEN ? AND ?) OR " + + " (end_date BETWEEN ? AND ?) OR ((start_date <= ?) AND (end_date >= ?)))"; + + public UsageVMInstanceDaoImpl() {} + + public void update(UsageVMInstanceVO instance) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + try { + txn.start(); + String sql = UPDATE_USAGE_INSTANCE_SQL; + pstmt = txn.prepareAutoCloseStatement(sql); + pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), instance.getEndDate())); + pstmt.setLong(2, instance.getAccountId()); + pstmt.setLong(3, instance.getVmInstanceId()); + pstmt.setInt(4, instance.getUsageType()); + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception e) { + s_logger.warn(e); + } finally { + txn.close(); + } + } + + public void delete(UsageVMInstanceVO instance) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + try { + txn.start(); + String sql = DELETE_USAGE_INSTANCE_SQL; + pstmt = txn.prepareAutoCloseStatement(sql); + pstmt.setLong(1, instance.getAccountId()); + pstmt.setLong(2, instance.getVmInstanceId()); + pstmt.setInt(3, instance.getUsageType()); + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + s_logger.error("error deleting usage vm instance with vmId: " + instance.getVmInstanceId() + ", for account with id: " + instance.getAccountId()); + } finally { + txn.close(); + } + } + + public List getUsageRecords(long accountId, Date startDate, Date endDate) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + List usageInstances = new ArrayList(); + try { + String sql = GET_USAGE_RECORDS_BY_ACCOUNT; + pstmt = txn.prepareAutoCloseStatement(sql); + pstmt.setLong(1, accountId); + pstmt.setString(2, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(3, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(4, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(5, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(6, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(7, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + int r_usageType = rs.getInt(1); + long r_zoneId = rs.getLong(2); + long r_accountId = rs.getLong(3); + long r_vmId = rs.getLong(4); + String r_vmName = rs.getString(5); + long r_soId = rs.getLong(6); + long r_tId = rs.getLong(7); + String hypervisorType = rs.getString(8); + String r_startDate = rs.getString(9); + String r_endDate = rs.getString(10); + Date instanceStartDate = null; + Date instanceEndDate = null; + if (r_startDate != null) { + instanceStartDate = DateUtil.parseDateString(s_gmtTimeZone, r_startDate); + } + if (r_endDate != null) { + instanceEndDate = DateUtil.parseDateString(s_gmtTimeZone, r_endDate); + } + UsageVMInstanceVO usageInstance = new UsageVMInstanceVO(r_usageType, r_zoneId, r_accountId, r_vmId, r_vmName, r_soId, r_tId, hypervisorType, instanceStartDate, instanceEndDate); + usageInstances.add(usageInstance); + } + } catch (Exception ex) { + s_logger.error("error retrieving usage vm instances for account id: " + accountId, ex); + } finally { + txn.close(); + } + return usageInstances; + } +} diff --git a/server/src/com/cloud/usage/dao/UsageVolumeDao.java b/server/src/com/cloud/usage/dao/UsageVolumeDao.java new file mode 100644 index 00000000000..de582add527 --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageVolumeDao.java @@ -0,0 +1,32 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.util.Date; +import java.util.List; + +import com.cloud.usage.UsageVolumeVO; +import com.cloud.utils.db.GenericDao; + +public interface UsageVolumeDao extends GenericDao { + public void removeBy(long userId, long id); + public void update(UsageVolumeVO usage); + public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate, boolean limit, int page); +} diff --git a/server/src/com/cloud/usage/dao/UsageVolumeDaoImpl.java b/server/src/com/cloud/usage/dao/UsageVolumeDaoImpl.java new file mode 100644 index 00000000000..c87753e0436 --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageVolumeDaoImpl.java @@ -0,0 +1,179 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.usage.UsageVolumeVO; +import com.cloud.utils.DateUtil; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.Transaction; + +@Local(value={UsageVolumeDao.class}) +public class UsageVolumeDaoImpl extends GenericDaoBase implements UsageVolumeDao { + public static final Logger s_logger = Logger.getLogger(UsageVolumeDaoImpl.class.getName()); + + protected static final String REMOVE_BY_USERID_VOLID = "DELETE FROM usage_volume WHERE account_id = ? AND id = ?"; + protected static final String UPDATE_DELETED = "UPDATE usage_volume SET deleted = ? WHERE account_id = ? AND id = ? and deleted IS NULL"; + protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, zone_id, account_id, domain_id, disk_offering_id, template_id, size, created, deleted " + + "FROM usage_volume " + + "WHERE account_id = ? AND ((deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?)))"; + protected static final String GET_USAGE_RECORDS_BY_DOMAIN = "SELECT id, zone_id, account_id, domain_id, disk_offering_id, template_id, size, created, deleted " + + "FROM usage_volume " + + "WHERE domain_id = ? AND ((deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?)))"; + protected static final String GET_ALL_USAGE_RECORDS = "SELECT id, zone_id, account_id, domain_id, disk_offering_id, template_id, size, created, deleted " + + "FROM usage_volume " + + "WHERE (deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?))"; + + public UsageVolumeDaoImpl() {} + + public void removeBy(long accountId, long volId) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + try { + txn.start(); + String sql = REMOVE_BY_USERID_VOLID; + pstmt = txn.prepareAutoCloseStatement(sql); + pstmt.setLong(1, accountId); + pstmt.setLong(2, volId); + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error removing usageVolumeVO", e); + } finally { + txn.close(); + } + } + + public void update(UsageVolumeVO usage) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + try { + txn.start(); + if (usage.getDeleted() != null) { + pstmt = txn.prepareAutoCloseStatement(UPDATE_DELETED); + pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), usage.getDeleted())); + pstmt.setLong(2, usage.getAccountId()); + pstmt.setLong(3, usage.getId()); + } + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error updating UsageVolumeVO", e); + } finally { + txn.close(); + } + } + + @Override + public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate, boolean limit, int page) { + List usageRecords = new ArrayList(); + + Long param1 = null; + String sql = null; + if (accountId != null) { + sql = GET_USAGE_RECORDS_BY_ACCOUNT; + param1 = accountId; + } else if (domainId != null) { + sql = GET_USAGE_RECORDS_BY_DOMAIN; + param1 = domainId; + } else { + sql = GET_ALL_USAGE_RECORDS; + } + + if (limit) { + int startIndex = 0; + if (page > 0) { + startIndex = 500 * (page-1); + } + sql += " LIMIT " + startIndex + ",500"; + } + + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + + try { + int i = 1; + pstmt = txn.prepareAutoCloseStatement(sql); + if (param1 != null) { + pstmt.setLong(i++, param1); + } + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + //id, zoneId, account_id, domain_id, disk_offering_id, template_id created, deleted + Long vId = Long.valueOf(rs.getLong(1)); + Long zoneId = Long.valueOf(rs.getLong(2)); + Long acctId = Long.valueOf(rs.getLong(3)); + Long dId = Long.valueOf(rs.getLong(4)); + Long doId = Long.valueOf(rs.getLong(5)); + if(doId == 0){ + doId = null; + } + Long tId = Long.valueOf(rs.getLong(6)); + if(tId == 0){ + tId = null; + } + long size = Long.valueOf(rs.getLong(7)); + Date createdDate = null; + Date deletedDate = null; + String createdTS = rs.getString(8); + String deletedTS = rs.getString(9); + + + if (createdTS != null) { + createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); + } + if (deletedTS != null) { + deletedDate = DateUtil.parseDateString(s_gmtTimeZone, deletedTS); + } + + usageRecords.add(new UsageVolumeVO(vId, zoneId, acctId, dId, doId, tId, size, createdDate, deletedDate)); + } + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error getting usage records", e); + } finally { + txn.close(); + } + + return usageRecords; + } +} diff --git a/server/test/com/cloud/upgrade/Usage217To224UpgradeTest.java b/server/test/com/cloud/upgrade/Usage217To224UpgradeTest.java new file mode 100644 index 00000000000..3886c67b9dc --- /dev/null +++ b/server/test/com/cloud/upgrade/Usage217To224UpgradeTest.java @@ -0,0 +1,93 @@ +/** + * * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.upgrade; + + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import junit.framework.TestCase; + +import org.apache.log4j.Logger; +import org.junit.After; +import org.junit.Before; + +import com.cloud.upgrade.dao.VersionDaoImpl; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.db.DbTestUtils; +import com.cloud.utils.db.Transaction; + +public class Usage217To224UpgradeTest extends TestCase { + private static final Logger s_logger = Logger.getLogger(Usage217To224UpgradeTest.class); + + @Override + @Before + public void setUp() throws Exception { + DbTestUtils.executeScript("PreviousDatabaseSchema/clean-db.sql", false, true); + DbTestUtils.executeUsageScript("PreviousDatabaseSchema/clean-usage-db.sql", false, true); + } + + @Override + @After + public void tearDown() throws Exception { + } + + public void test21to22Upgrade() throws SQLException { + s_logger.debug("Finding sample data from 2.1.7"); + DbTestUtils.executeScript("PreviousDatabaseSchema/2.1.7/2.1.tata.sql", false, true); + DbTestUtils.executeUsageScript("PreviousDatabaseSchema/2.1.7/2.1.usage.tata.sql", false, true); + + Connection conn; + PreparedStatement pstmt; + + VersionDaoImpl dao = ComponentLocator.inject(VersionDaoImpl.class); + PremiumDatabaseUpgradeChecker checker = ComponentLocator.inject(PremiumDatabaseUpgradeChecker.class); + + String version = dao.getCurrentVersion(); + assert version.equals("2.1.7") : "Version returned is not 2.1.7 but " + version; + + checker.upgrade("2.1.7", "2.2.4"); + + conn = Transaction.getStandaloneConnection(); + try { + pstmt = conn.prepareStatement("SELECT version FROM version ORDER BY id DESC LIMIT 1"); + ResultSet rs = pstmt.executeQuery(); + assert rs.next() : "No version selected"; + assert rs.getString(1).equals("2.2.4") : "VERSION stored is not 2.2.4: " + rs.getString(1); + rs.close(); + pstmt.close(); + + pstmt = conn.prepareStatement("SELECT COUNT(*) FROM usage_event"); + rs = pstmt.executeQuery(); + assert rs.next() : "Unable to get the count of usage events"; + assert (rs.getInt(1) == 182) : "Didn't find 182 usage events but found " + rs.getInt(1); + rs.close(); + pstmt.close(); + + } finally { + try { + conn.close(); + } catch (SQLException e) { + } + } + } + +} diff --git a/setup/db/create-database-premium.sql b/setup/db/create-database-premium.sql new file mode 100644 index 00000000000..5ce3413d96e --- /dev/null +++ b/setup/db/create-database-premium.sql @@ -0,0 +1,13 @@ +SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ANSI'; + +DROP DATABASE IF EXISTS `cloud_usage`; + +CREATE DATABASE `cloud_usage`; + +GRANT ALL ON cloud_usage.* to cloud@`localhost`; +GRANT ALL ON cloud_usage.* to cloud@`%`; + +GRANT process ON *.* TO cloud@`localhost`; +GRANT process ON *.* TO cloud@`%`; + +commit; diff --git a/setup/db/create-schema-premium.sql b/setup/db/create-schema-premium.sql new file mode 100644 index 00000000000..8bfbb5b32c8 --- /dev/null +++ b/setup/db/create-schema-premium.sql @@ -0,0 +1,254 @@ +SET foreign_key_checks = 0; +DROP TABLE IF EXISTS `cloud_usage`.`event`; +DROP TABLE IF EXISTS `cloud_usage`.`cloud_usage`; +DROP TABLE IF EXISTS `cloud_usage`.`usage_vm_instance`; +DROP TABLE IF EXISTS `cloud_usage`.`usage_ip_address`; +DROP TABLE IF EXISTS `cloud_usage`.`usage_network`; +DROP TABLE IF EXISTS `cloud_usage`.`usage_job`; +DROP TABLE IF EXISTS `cloud_usage`.`account`; +DROP TABLE IF EXISTS `cloud_usage`.`user_statistics`; +DROP TABLE IF EXISTS `cloud_usage`.`usage_volume`; +DROP TABLE IF EXISTS `cloud_usage`.`usage_storage`; +DROP TABLE IF EXISTS `cloud_usage`.`usage_security_group`; +DROP TABLE IF EXISTS `cloud_usage`.`usage_load_balancer_policy`; +DROP TABLE IF EXISTS `cloud_usage`.`usage_port_forwarding`; +DROP TABLE IF EXISTS `cloud_usage`.`usage_network_offering`; +DROP TABLE IF EXISTS `cloud_usage`.`usage_event`; + +CREATE TABLE `cloud_usage`.`event` ( + `id` bigint unsigned NOT NULL auto_increment, + `type` varchar(32) NOT NULL, + `description` varchar(1024) NOT NULL, + `user_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `created` datetime NOT NULL, + `level` varchar(16) NOT NULL, + `parameters` varchar(1024) NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`cloud_usage` ( + `id` bigint unsigned NOT NULL auto_increment, + `zone_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `domain_id` bigint unsigned NOT NULL, + `description` varchar(1024) NOT NULL, + `usage_display` varchar(255) NOT NULL, + `usage_type` int(1) unsigned, + `raw_usage` DOUBLE UNSIGNED NOT NULL, + `vm_instance_id` bigint unsigned, + `vm_name` varchar(255), + `offering_id` bigint unsigned, + `template_id` bigint unsigned, + `usage_id` bigint unsigned, + `type` varchar(32), + `size` bigint unsigned, + `network_id` bigint unsigned, + `start_date` DATETIME NOT NULL, + `end_date` DATETIME NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`usage_vm_instance` ( + `usage_type` int(1) unsigned, + `zone_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `vm_instance_id` bigint unsigned NOT NULL, + `vm_name` varchar(255) NOT NULL, + `service_offering_id` bigint unsigned NOT NULL, + `template_id` bigint unsigned NOT NULL, + `hypervisor_type` varchar(255), + `start_date` DATETIME NOT NULL, + `end_date` DATETIME NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`usage_network` ( + `account_id` bigint unsigned NOT NULL, + `zone_id` bigint unsigned NOT NULL, + `host_id` bigint unsigned NOT NULL, + `host_type` varchar(32), + `network_id` bigint unsigned, + `bytes_sent` bigint unsigned NOT NULL default '0', + `bytes_received` bigint unsigned NOT NULL default '0', + `net_bytes_received` bigint unsigned NOT NULL default '0', + `net_bytes_sent` bigint unsigned NOT NULL default '0', + `current_bytes_received` bigint unsigned NOT NULL default '0', + `current_bytes_sent` bigint unsigned NOT NULL default '0', + `event_time_millis` bigint unsigned NOT NULL default '0', + PRIMARY KEY (`account_id`, `zone_id`, `host_id`, `event_time_millis`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`usage_ip_address` ( + `id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `domain_id` bigint unsigned NOT NULL, + `zone_id` bigint unsigned NOT NULL, + `public_ip_address` varchar(15) NOT NULL, + `is_source_nat` smallint(1) NOT NULL, + `assigned` DATETIME NOT NULL, + `released` DATETIME NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`usage_job` ( + `id` bigint unsigned NOT NULL auto_increment, + `host` varchar(255), + `pid` int(5), + `job_type` int(1), + `scheduled` int(1), + `start_millis` bigint unsigned NOT NULL default '0' COMMENT 'start time in milliseconds of the aggregation range used by this job', + `end_millis` bigint unsigned NOT NULL default '0' COMMENT 'end time in milliseconds of the aggregation range used by this job', + `exec_time` bigint unsigned NOT NULL default '0' COMMENT 'how long in milliseconds it took for the job to execute', + `start_date` DATETIME COMMENT 'start date of the aggregation range used by this job', + `end_date` DATETIME COMMENT 'end date of the aggregation range used by this job', + `success` int(1), + `heartbeat` DATETIME NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`account` ( + `id` bigint unsigned NOT NULL, + `account_name` varchar(100) COMMENT 'an account name set by the creator of the account, defaults to username for single accounts', + `type` int(1) unsigned NOT NULL, + `domain_id` bigint unsigned, + `state` varchar(10) NOT NULL default 'enabled', + `removed` datetime COMMENT 'date removed', + `cleanup_needed` tinyint(1) NOT NULL default '0', + `network_domain` varchar(100) COMMENT 'Network domain name of the Vms of the account', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`user_statistics` ( + `id` bigint unsigned UNIQUE NOT NULL, + `data_center_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `public_ip_address` varchar(15), + `device_id` bigint unsigned NOT NULL, + `device_type` varchar(32) NOT NULL, + `network_id` bigint unsigned, + `net_bytes_received` bigint unsigned NOT NULL default '0', + `net_bytes_sent` bigint unsigned NOT NULL default '0', + `current_bytes_received` bigint unsigned NOT NULL default '0', + `current_bytes_sent` bigint unsigned NOT NULL default '0', + PRIMARY KEY (`id`), + UNIQUE KEY (`account_id`, `data_center_id`, `public_ip_address`, `device_id`, `device_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`usage_volume` ( + `id` bigint unsigned NOT NULL, + `zone_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `domain_id` bigint unsigned NOT NULL, + `disk_offering_id` bigint unsigned, + `template_id` bigint unsigned, + `size` bigint unsigned, + `created` DATETIME NOT NULL, + `deleted` DATETIME NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`usage_storage` ( + `id` bigint unsigned NOT NULL, + `zone_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `domain_id` bigint unsigned NOT NULL, + `storage_type` int(1) unsigned NOT NULL, + `source_id` bigint unsigned, + `size` bigint unsigned NOT NULL, + `created` DATETIME NOT NULL, + `deleted` DATETIME NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`usage_security_group` ( + `id` bigint unsigned NOT NULL, + `zone_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `domain_id` bigint unsigned NOT NULL, + `vm_id` bigint unsigned NOT NULL, + `num_rules` bigint unsigned NOT NULL, + `created` DATETIME NOT NULL, + `deleted` DATETIME NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`usage_load_balancer_policy` ( + `id` bigint unsigned NOT NULL, + `zone_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `domain_id` bigint unsigned NOT NULL, + `created` DATETIME NOT NULL, + `deleted` DATETIME NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`usage_event` ( + `id` bigint unsigned NOT NULL auto_increment, + `type` varchar(32) NOT NULL, + `account_id` bigint unsigned NOT NULL, + `created` datetime NOT NULL, + `zone_id` bigint unsigned NOT NULL, + `resource_id` bigint unsigned, + `resource_name` varchar(255), + `offering_id` bigint unsigned, + `template_id` bigint unsigned, + `size` bigint unsigned, + `resource_type` varchar(32), + `processed` tinyint NOT NULL default '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`usage_port_forwarding` ( + `id` bigint unsigned NOT NULL, + `zone_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `domain_id` bigint unsigned NOT NULL, + `created` DATETIME NOT NULL, + `deleted` DATETIME NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`usage_network_offering` ( + `zone_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `domain_id` bigint unsigned NOT NULL, + `vm_instance_id` bigint unsigned NOT NULL, + `network_offering_id` bigint unsigned NOT NULL, + `is_default` smallint(1) NOT NULL, + `created` DATETIME NOT NULL, + `deleted` DATETIME NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`netapp_volume` ( + `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT COMMENT 'id', + `ip_address` varchar(255) NOT NULL COMMENT 'ip address/fqdn of the volume', + `pool_id` bigint unsigned NOT NULL COMMENT 'id for the pool', + `pool_name` varchar(255) NOT NULL COMMENT 'name for the pool', + `aggregate_name` varchar(255) NOT NULL COMMENT 'name for the aggregate', + `volume_name` varchar(255) NOT NULL COMMENT 'name for the volume', + `volume_size` varchar(255) NOT NULL COMMENT 'volume size', + `snapshot_policy` varchar(255) NOT NULL COMMENT 'snapshot policy', + `snapshot_reservation` int NOT NULL COMMENT 'snapshot reservation', + `username` varchar(255) NOT NULL COMMENT 'username', + `password` varchar(200) COMMENT 'password', + `round_robin_marker` int COMMENT 'This marks the volume to be picked up for lun creation, RR fashion', + PRIMARY KEY (`id`), + CONSTRAINT `fk_netapp_volume__pool_id` FOREIGN KEY `fk_netapp_volume__pool_id` (`pool_id`) REFERENCES `netapp_pool` (`id`) ON DELETE CASCADE, + INDEX `i_netapp_volume__pool_id`(`pool_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`netapp_pool` ( + `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT COMMENT 'id', + `name` varchar(255) NOT NULL UNIQUE COMMENT 'name for the pool', + `algorithm` varchar(255) NOT NULL COMMENT 'algorithm', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`netapp_lun` ( + `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT COMMENT 'id', + `lun_name` varchar(255) NOT NULL COMMENT 'lun name', + `target_iqn` varchar(255) NOT NULL COMMENT 'target iqn', + `path` varchar(255) NOT NULL COMMENT 'lun path', + `size` bigint NOT NULL COMMENT 'lun size', + `volume_id` bigint unsigned NOT NULL COMMENT 'parent volume id', + PRIMARY KEY (`id`), + CONSTRAINT `fk_netapp_lun__volume_id` FOREIGN KEY `fk_netapp_lun__volume_id` (`volume_id`) REFERENCES `netapp_volume` (`id`) ON DELETE CASCADE, + INDEX `i_netapp_lun__volume_id`(`volume_id`), + INDEX `i_netapp_lun__lun_name`(`lun_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +SET foreign_key_checks = 1; diff --git a/setup/db/db/schema-21to22-premium.sql b/setup/db/db/schema-21to22-premium.sql new file mode 100755 index 00000000000..c098206d194 --- /dev/null +++ b/setup/db/db/schema-21to22-premium.sql @@ -0,0 +1,64 @@ +--; +-- Schema upgrade from 2.1 to 2.2; +--; +DROP TABLE IF EXISTS `cloud_usage`.`usage_port_forwarding`; +DROP TABLE IF EXISTS `cloud_usage`.`usage_network_offering`; +DROP TABLE IF EXISTS `cloud_usage`.`usage_event`; + +ALTER TABLE `cloud_usage`.`cloud_usage` modify `raw_usage` DOUBLE UNSIGNED NOT NULL; +ALTER TABLE `cloud_usage`.`cloud_usage` ADD COLUMN `type` varchar(32); + +ALTER TABLE `cloud_usage`.`usage_network` ADD COLUMN `host_id` bigint unsigned NOT NULL default 0; +ALTER TABLE `cloud_usage`.`usage_network` ADD COLUMN `host_type` varchar(32) default 'DomainRouter'; + +ALTER TABLE `cloud_usage`.`usage_network` drop PRIMARY KEY; +ALTER TABLE `cloud_usage`.`usage_network` add PRIMARY KEY (`account_id`, `zone_id`, `host_id`, `event_time_millis`); + +ALTER TABLE `cloud_usage`.`usage_ip_address` ADD COLUMN `id` bigint unsigned default 0; +ALTER TABLE `cloud_usage`.`usage_ip_address` ADD COLUMN `is_source_nat` smallint(1) NOT NULL default 0; + +ALTER TABLE `cloud_usage`.`account` ADD COLUMN `network_domain` varchar(100) COMMENT 'Network domain name of the Vms of the account'; + +ALTER TABLE `cloud_usage`.`user_statistics` ADD COLUMN `public_ip_address` varchar(15); +ALTER TABLE `cloud_usage`.`user_statistics` ADD COLUMN `device_id` bigint unsigned NOT NULL default 0; +ALTER TABLE `cloud_usage`.`user_statistics` ADD COLUMN `device_type` varchar(32) NOT NULL default 'DomainRouter'; + +CREATE TABLE `cloud_usage`.`usage_event` ( + `id` bigint unsigned NOT NULL auto_increment, + `type` varchar(32) NOT NULL, + `account_id` bigint unsigned NOT NULL, + `created` datetime NOT NULL, + `zone_id` bigint unsigned NOT NULL, + `resource_id` bigint unsigned, + `resource_name` varchar(255), + `offering_id` bigint unsigned, + `template_id` bigint unsigned, + `size` bigint unsigned, + `processed` tinyint NOT NULL default '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`usage_port_forwarding` ( + `id` bigint unsigned NOT NULL, + `zone_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `domain_id` bigint unsigned NOT NULL, + `created` DATETIME NOT NULL, + `deleted` DATETIME NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud_usage`.`usage_network_offering` ( + `zone_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `domain_id` bigint unsigned NOT NULL, + `vm_instance_id` bigint unsigned NOT NULL, + `network_offering_id` bigint unsigned NOT NULL, + `is_default` smallint(1) NOT NULL, + `created` DATETIME NOT NULL, + `deleted` DATETIME NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +update `cloud_usage`.`usage_volume` set size = (size * 1048576); + + + diff --git a/setup/db/db/schema-221to222-premium.sql b/setup/db/db/schema-221to222-premium.sql new file mode 100755 index 00000000000..d7a4fffdae8 --- /dev/null +++ b/setup/db/db/schema-221to222-premium.sql @@ -0,0 +1,9 @@ +--; +-- Schema upgrade from 2.2.1 to 2.2.2; +--; +ALTER TABLE `cloud_usage`.`cloud_usage` ADD COLUMN `network_id` bigint unsigned; + +ALTER TABLE `cloud_usage`.`usage_network` ADD COLUMN `network_id` bigint unsigned; + +ALTER TABLE `cloud_usage`.`user_statistics` ADD COLUMN `network_id` bigint unsigned; +ALTER TABLE `cloud_usage`.`user_statistics` ADD UNIQUE KEY (`account_id`, `data_center_id`, `public_ip_address`, `device_id`, `device_type`); diff --git a/setup/db/db/schema-222to224-premium.sql b/setup/db/db/schema-222to224-premium.sql new file mode 100755 index 00000000000..d8cb5a229d3 --- /dev/null +++ b/setup/db/db/schema-222to224-premium.sql @@ -0,0 +1,7 @@ +--; +-- Schema upgrade from 2.2.2 to 2.2.4; +--; +ALTER TABLE `cloud_usage`.`usage_vm_instance` ADD COLUMN `hypervisor_type` varchar(255); + +ALTER TABLE `cloud_usage`.`usage_event` ADD COLUMN `resource_type` varchar(32); + diff --git a/setup/db/db/schema-227to228-premium.sql b/setup/db/db/schema-227to228-premium.sql new file mode 100755 index 00000000000..131929a63dc --- /dev/null +++ b/setup/db/db/schema-227to228-premium.sql @@ -0,0 +1,59 @@ +--; +-- Schema upgrade from 2.2.7 to 2.2.8; +--; +CREATE TABLE IF NOT EXISTS `cloud`.`netapp_pool` ( + `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT COMMENT 'id', + `name` varchar(255) NOT NULL UNIQUE COMMENT 'name for the pool', + `algorithm` varchar(255) NOT NULL COMMENT 'algorithm', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +CREATE TABLE IF NOT EXISTS `cloud`.`netapp_volume` ( + `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT COMMENT 'id', + `ip_address` varchar(255) NOT NULL COMMENT 'ip address/fqdn of the volume', + `pool_id` bigint unsigned NOT NULL COMMENT 'id for the pool', + `pool_name` varchar(255) NOT NULL COMMENT 'name for the pool', + `aggregate_name` varchar(255) NOT NULL COMMENT 'name for the aggregate', + `volume_name` varchar(255) NOT NULL COMMENT 'name for the volume', + `volume_size` varchar(255) NOT NULL COMMENT 'volume size', + `snapshot_policy` varchar(255) NOT NULL COMMENT 'snapshot policy', + `snapshot_reservation` int NOT NULL COMMENT 'snapshot reservation', + `username` varchar(255) NOT NULL COMMENT 'username', + `password` varchar(200) COMMENT 'password', + `round_robin_marker` int COMMENT 'This marks the volume to be picked up for lun creation, RR fashion', + PRIMARY KEY (`ip_address`,`aggregate_name`,`volume_name`), + CONSTRAINT `fk_netapp_volume__pool_id` FOREIGN KEY `fk_netapp_volume__pool_id` (`pool_id`) REFERENCES `netapp_pool` (`id`) ON DELETE CASCADE, + INDEX `i_netapp_volume__pool_id`(`pool_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +CREATE TABLE IF NOT EXISTS `cloud`.`netapp_lun` ( + `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT COMMENT 'id', + `lun_name` varchar(255) NOT NULL COMMENT 'lun name', + `target_iqn` varchar(255) NOT NULL COMMENT 'target iqn', + `path` varchar(255) NOT NULL COMMENT 'lun path', + `size` bigint NOT NULL COMMENT 'lun size', + `volume_id` bigint unsigned NOT NULL COMMENT 'parent volume id', + PRIMARY KEY (`id`), + CONSTRAINT `fk_netapp_lun__volume_id` FOREIGN KEY `fk_netapp_lun__volume_id` (`volume_id`) REFERENCES `netapp_volume` (`id`) ON DELETE CASCADE, + INDEX `i_netapp_lun__volume_id`(`volume_id`), + INDEX `i_netapp_lun__lun_name`(`lun_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +--; +-- Cleanup usage records for bug # 10727; +--; + + +create table `cloud_usage`.`temp_usage` ( `vol_id` bigint unsigned, `created` DATETIME); + +insert into `cloud_usage`.`temp_usage` (vol_id, created) select id, max(created) from `cloud_usage`.`usage_volume` where deleted is null group by id having count(id) > 1; + +delete `cloud_usage`.`usage_volume` from `cloud_usage`.`usage_volume` inner join `cloud_usage`.`temp_usage` where `cloud_usage`.`usage_volume`.created = `cloud_usage`.`temp_usage`.created and `cloud_usage`.`usage_volume`.id = `cloud_usage`.`temp_usage`.vol_id and `cloud_usage`.`usage_volume`.deleted is null; + +drop table `cloud_usage`.`temp_usage`; + +update `cloud_usage`.`cloud_usage` set raw_usage = (raw_usage % 24) where usage_type =6 and raw_usage > 24 and (raw_usage % 24) <> 0; +update `cloud_usage`.`cloud_usage` set raw_usage = 24 where usage_type =6 and raw_usage > 24 and (raw_usage % 24) = 0; + diff --git a/wscript_build b/wscript_build index 566a930d021..c2eda77fa42 100644 --- a/wscript_build +++ b/wscript_build @@ -25,7 +25,8 @@ except ImportError: # Global variables setup sourcedir = bld.srcnode.abspath() builddir = bld.path.abspath(bld.env) -buildpremium = _exists(_join(sourcedir,"cloudstack-proprietary")) +#buildpremium = _exists(_join(sourcedir,"cloudstack-proprietary")) +buildpremium = False filelist = bld.path.ant_glob distdir = Utils.relpath(_join(sourcedir,"dist")) targetdir = Utils.relpath(_join(sourcedir,"target")) @@ -113,7 +114,7 @@ def build_dependences (): "cloud-commons-httpclient-3.1.jar", "cloud-commons-pool-1.4.jar", "cloud-servlet-api.jar", "cloud-commons-logging-1.1.1.jar", "cloud-ws-commons-util-1.0.2.jar", - "cloud-commons-collections-3.2.1.jar", "vmware*.jar"] + "cloud-commons-collections-3.2.1.jar"] start_path = bld.path.find_dir ("deps") bld.install_files('${JAVADIR}',start_path.ant_glob("*.jar", excl = excludes), cwd=start_path) @@ -220,9 +221,13 @@ def build_scripts (): bld.substitute('**',"${AGENTLIBDIR}/scripts",chmod=0755, cwd=start_path) def build_bin_exec_dirs (): - bld.install_files_filtered("${LIBEXECDIR}","*/libexec/* cloudstack-proprietary/*/libexec/*",chmod=0755) - bld.install_files_filtered("${BINDIR}","*/bindir/* cloudstack-proprietary/*/bindir/*",chmod=0755) - bld.install_files_filtered("${SBINDIR}","*/sbindir/* cloudstack-proprietary/*/sbindir/*",chmod=0755) + #bld.install_files_filtered("${LIBEXECDIR}","*/libexec/* cloudstack-proprietary/*/libexec/*",chmod=0755) + #bld.install_files_filtered("${BINDIR}","*/bindir/* cloudstack-proprietary/*/bindir/*",chmod=0755) + #bld.install_files_filtered("${SBINDIR}","*/sbindir/* cloudstack-proprietary/*/sbindir/*",chmod=0755) + + bld.install_files_filtered("${LIBEXECDIR}","*/libexec/*",chmod=0755) + bld.install_files_filtered("${BINDIR}","*/bindir/*",chmod=0755) + bld.install_files_filtered("${SBINDIR}","*/sbindir/*",chmod=0755) def build_server_client (): start_path = bld.path.find_dir("client/WEB-INF") @@ -261,7 +266,8 @@ def build_ui (): def build_conf_files (): # apply distro-specific config on top of the 'all' generic cloud-management config globspec = _join("*","distro",bld.env.DISTRO.lower(),"*") # matches premium/distro/centos/SYSCONFDIR - distrospecificdirs=_glob(globspec) + _glob(_join("cloudstack-proprietary",globspec)) + #distrospecificdirs=_glob(globspec) + _glob(_join("cloudstack-proprietary",globspec)) + distrospecificdirs=_glob(globspec) for dsdir in distrospecificdirs: start_path = bld.srcnode.find_dir(dsdir) subpath,varname = _split(dsdir) @@ -319,7 +325,7 @@ def build_xml_api_description (): sources.append (str) sources.append ("client/tomcatconf/commands.properties.in") if buildpremium: - sources.append("cloudstack-proprietary/premium/tomcatconf/commands-ext.properties.in") + sources.append("client/tomcatconf/commands-ext.properties.in") buildproducts = [] for i in sources: @@ -350,6 +356,10 @@ def build_xml_api_description (): bld.install_files("${PYTHONDIR}/cloudutils", 'python/lib/cloudutils/*') bld.install_as("${PYTHONDIR}/cloudapis.py", 'cloud-cli/cloudapis/cloud.py') +def build_ovm(): + start_path = bld.path.find_dir("ovm/scripts") + bld.substitute('**',"${AGENTLIBDIR}/scripts",chmod=0755, cwd=start_path) + # Get started to execute here @@ -357,7 +367,7 @@ build_utils_docs () build_jars () build_python_and_daemonize () build_premium () -build_thirdparty_dir() +#build_thirdparty_dir() build_dependences () build_console_proxy () build_patches () @@ -372,6 +382,7 @@ build_conf_files () build_db_files () build_plugins () build_xml_api_description () +build_ovm () # ====================== Magic! ========================================= bld.use_the_magic()