diff --git a/api/src/com/cloud/agent/api/to/FirewallRuleTO.java b/api/src/com/cloud/agent/api/to/FirewallRuleTO.java index 95ac57c3867..38de8d0b4eb 100644 --- a/api/src/com/cloud/agent/api/to/FirewallRuleTO.java +++ b/api/src/com/cloud/agent/api/to/FirewallRuleTO.java @@ -50,7 +50,7 @@ public class FirewallRuleTO implements InternalIdentity { FirewallRule.Purpose purpose; private Integer icmpType; private Integer icmpCode; - + private FirewallRule.TrafficType trafficType; protected FirewallRuleTO() { } @@ -85,6 +85,7 @@ public class FirewallRuleTO implements InternalIdentity { this.sourceCidrList = sourceCidr; this.icmpType = icmpType; this.icmpCode = icmpCode; + this.trafficType = null; } public FirewallRuleTO(FirewallRule rule, String srcVlanTag, String srcIp) { this(rule.getId(),srcVlanTag, srcIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getState()==State.Revoke, rule.getState()==State.Active, rule.getPurpose(),rule.getSourceCidrList(),rule.getIcmpType(),rule.getIcmpCode()); @@ -93,6 +94,23 @@ public class FirewallRuleTO implements InternalIdentity { public FirewallRuleTO(FirewallRule rule, String srcIp) { this(rule.getId(),null, srcIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getState()==State.Revoke, rule.getState()==State.Active, rule.getPurpose(),rule.getSourceCidrList(),rule.getIcmpType(),rule.getIcmpCode()); } + + public FirewallRuleTO(FirewallRule rule, String srcVlanTag, String srcIp, FirewallRule.Purpose purpose) { + this(rule.getId(),srcVlanTag, srcIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getState()==State.Revoke, rule.getState()==State.Active, purpose,rule.getSourceCidrList(),rule.getIcmpType(),rule.getIcmpCode()); + } + + public FirewallRuleTO(FirewallRule rule, String srcVlanTag, String srcIp, FirewallRule.Purpose purpose, FirewallRule.TrafficType trafficType) { + this(rule.getId(),srcVlanTag, srcIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getState()==State.Revoke, rule.getState()==State.Active, purpose,rule.getSourceCidrList(),rule.getIcmpType(),rule.getIcmpCode()); + this.trafficType = trafficType; + } + + public FirewallRuleTO(FirewallRule rule, String srcVlanTag, String srcIp, FirewallRule.Purpose purpose, boolean revokeState, boolean alreadyAdded) { + this(rule.getId(),srcVlanTag, srcIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(), revokeState, alreadyAdded, purpose,rule.getSourceCidrList(),rule.getIcmpType(),rule.getIcmpCode()); + } + + public FirewallRule.TrafficType getTrafficType(){ + return trafficType; + } public long getId() { return id; diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index f0ec2964fa6..0dd97cb438c 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -60,6 +60,7 @@ public class EventTypes { public static final String EVENT_VM_UPDATE = "VM.UPDATE"; public static final String EVENT_VM_UPGRADE = "VM.UPGRADE"; public static final String EVENT_VM_RESETPASSWORD = "VM.RESETPASSWORD"; + public static final String EVENT_VM_RESETSSHKEY = "VM.RESETSSHKEY"; public static final String EVENT_VM_MIGRATE = "VM.MIGRATE"; public static final String EVENT_VM_MOVE = "VM.MOVE"; public static final String EVENT_VM_RESTORE = "VM.RESTORE"; @@ -97,6 +98,11 @@ public class EventTypes { public static final String EVENT_FIREWALL_OPEN = "FIREWALL.OPEN"; public static final String EVENT_FIREWALL_CLOSE = "FIREWALL.CLOSE"; + //NIC Events + public static final String EVENT_NIC_CREATE = "NIC.CREATE"; + public static final String EVENT_NIC_DELETE = "NIC.DELETE"; + public static final String EVENT_NIC_UPDATE = "NIC.UPDATE"; + // Load Balancers public static final String EVENT_ASSIGN_TO_LOAD_BALANCER_RULE = "LB.ASSIGN.TO.RULE"; public static final String EVENT_REMOVE_FROM_LOAD_BALANCER_RULE = "LB.REMOVE.FROM.RULE"; @@ -349,6 +355,11 @@ public class EventTypes { public static final String EVENT_AUTOSCALEVMGROUP_DISABLE = "AUTOSCALEVMGROUP.DISABLE"; + public static final String EVENT_BAREMETAL_DHCP_SERVER_ADD = "PHYSICAL.DHCP.ADD"; + public static final String EVENT_BAREMETAL_DHCP_SERVER_DELETE = "PHYSICAL.DHCP.DELETE"; + public static final String EVENT_BAREMETAL_PXE_SERVER_ADD = "PHYSICAL.PXE.ADD"; + public static final String EVENT_BAREMETAL_PXE_SERVER_DELETE = "PHYSICAL.PXE.DELETE"; + static { // TODO: need a way to force author adding event types to declare the entity details as well, with out braking diff --git a/api/src/com/cloud/host/Host.java b/api/src/com/cloud/host/Host.java index bd26f81c8c2..7236680fd7b 100755 --- a/api/src/com/cloud/host/Host.java +++ b/api/src/com/cloud/host/Host.java @@ -39,6 +39,8 @@ public interface Host extends StateObject, Identity, InternalIdentity { ExternalLoadBalancer(false), ExternalVirtualSwitchSupervisor(false), PxeServer(false), + BaremetalPxe(false), + BaremetalDhcp(false), TrafficMonitor(false), ExternalDhcp(false), diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java index b3c8381f15a..1dbb327155e 100644 --- a/api/src/com/cloud/network/Network.java +++ b/api/src/com/cloud/network/Network.java @@ -46,8 +46,8 @@ public interface Network extends ControlledEntity, StateObject, I public static final Service Dhcp = new Service("Dhcp"); public static final Service Dns = new Service("Dns", Capability.AllowDnsSuffixModification); public static final Service Gateway = new Service("Gateway"); - public static final Service Firewall = new Service("Firewall", Capability.SupportedProtocols, - Capability.MultipleIps, Capability.TrafficStatistics); + public static final Service Firewall = new Service("Firewall", Capability.SupportedProtocols, + Capability.MultipleIps, Capability.TrafficStatistics, Capability.SupportedTrafficDirection, Capability.SupportedEgressProtocols); public static final Service Lb = new Service("Lb", Capability.SupportedLBAlgorithms, Capability.SupportedLBIsolation, Capability.SupportedProtocols, Capability.TrafficStatistics, Capability.LoadBalancingSupportedIps, Capability.SupportedStickinessMethods, Capability.ElasticLb); @@ -172,6 +172,8 @@ public interface Network extends ControlledEntity, StateObject, I public static final Capability ElasticLb = new Capability("ElasticLb"); public static final Capability AutoScaleCounters = new Capability("AutoScaleCounters"); public static final Capability InlineMode = new Capability("InlineMode"); + public static final Capability SupportedTrafficDirection = new Capability("SupportedTrafficDirection"); + public static final Capability SupportedEgressProtocols = new Capability("SupportedEgressProtocols"); private String name; @@ -265,6 +267,8 @@ public interface Network extends ControlledEntity, StateObject, I void setPhysicalNetworkId(Long physicalNetworkId); + public void setTrafficType(TrafficType type); + ACLType getAclType(); boolean isRestartRequired(); diff --git a/api/src/com/cloud/network/NetworkProfile.java b/api/src/com/cloud/network/NetworkProfile.java index b5c463de375..bb59b047ac0 100644 --- a/api/src/com/cloud/network/NetworkProfile.java +++ b/api/src/com/cloud/network/NetworkProfile.java @@ -226,4 +226,8 @@ public class NetworkProfile implements Network { return vpcId; } + @Override + public void setTrafficType(TrafficType type) { + this.trafficType = type; + } } diff --git a/api/src/com/cloud/network/element/UserDataServiceProvider.java b/api/src/com/cloud/network/element/UserDataServiceProvider.java index 321ccc7d456..d9ddf9329ff 100644 --- a/api/src/com/cloud/network/element/UserDataServiceProvider.java +++ b/api/src/com/cloud/network/element/UserDataServiceProvider.java @@ -30,4 +30,5 @@ public interface UserDataServiceProvider extends NetworkElement { public boolean addPasswordAndUserdata(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException; boolean savePassword(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException; boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException; + boolean saveSSHKey(Network network, NicProfile nic, VirtualMachineProfile vm, String SSHPublicKey) throws ResourceUnavailableException; } diff --git a/api/src/com/cloud/network/firewall/FirewallService.java b/api/src/com/cloud/network/firewall/FirewallService.java index 3858499b2bb..419cc6a1bfd 100644 --- a/api/src/com/cloud/network/firewall/FirewallService.java +++ b/api/src/com/cloud/network/firewall/FirewallService.java @@ -27,7 +27,8 @@ import com.cloud.user.Account; import com.cloud.utils.Pair; public interface FirewallService { - FirewallRule createFirewallRule(FirewallRule rule) throws NetworkRuleConflictException; + FirewallRule createIngressFirewallRule(FirewallRule rule) throws NetworkRuleConflictException; + FirewallRule createEgressFirewallRule(FirewallRule rule) throws NetworkRuleConflictException; Pair, Integer> listFirewallRules(ListFirewallRulesCmd cmd); @@ -40,7 +41,8 @@ public interface FirewallService { */ boolean revokeFirewallRule(long ruleId, boolean apply); - boolean applyFirewallRules(long ipId, Account caller) throws ResourceUnavailableException; + boolean applyEgressFirewallRules (FirewallRule rule, Account caller) throws ResourceUnavailableException; + boolean applyIngressFirewallRules(long Ipid , Account caller) throws ResourceUnavailableException; FirewallRule getFirewallRule(long ruleId); diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java index b1ebe10596d..768ea9502f3 100755 --- a/api/src/com/cloud/vm/UserVmService.java +++ b/api/src/com/cloud/vm/UserVmService.java @@ -33,6 +33,7 @@ import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd; import org.apache.cloudstack.api.command.user.vm.RebootVMCmd; import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd; import org.apache.cloudstack.api.command.user.vm.ResetVMPasswordCmd; +import org.apache.cloudstack.api.command.user.vm.ResetVMSSHKeyCmd; import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd; import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd; @@ -88,6 +89,15 @@ public interface UserVmService { */ UserVm resetVMPassword(ResetVMPasswordCmd cmd, String password) throws ResourceUnavailableException, InsufficientCapacityException; + /** + * Resets the SSH Key of a virtual machine. + * + * @param cmd + * - the command specifying vmId, Keypair name + * @return the VM if reset worked successfully, null otherwise + */ + UserVm resetVMSSHKey(ResetVMSSHKeyCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException; + /** * Attaches the specified volume to the specified VM * @@ -113,6 +123,27 @@ public interface UserVmService { UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException; + /** + * Adds a NIC on the given network to the virtual machine + * @param cmd the command object that defines the vm and the given network + * @return the vm object if successful, null otherwise + */ + UserVm addNicToVirtualMachine(AddNicToVMCmd cmd); + + /** + * Removes a NIC on the given network from the virtual machine + * @param cmd the command object that defines the vm and the given network + * @return the vm object if successful, null otherwise + */ + UserVm removeNicFromVirtualMachine(RemoveNicFromVMCmd cmd); + + /** + * Updates default Nic to the given network for given virtual machine + * @param cmd the command object that defines the vm and the given network + * @return the vm object if successful, null otherwise + */ + UserVm updateDefaultNicForVirtualMachine(UpdateDefaultNicForVMCmd cmd); + UserVm recoverVirtualMachine(RecoverVMCmd cmd) throws ResourceAllocationException; /** diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java old mode 100644 new mode 100755 index 58a78318fae..d242830631b --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -224,6 +224,7 @@ public class ApiConstants { public static final String NETWORK_OFFERING_ID = "networkofferingid"; public static final String NETWORK_IDS = "networkids"; public static final String NETWORK_ID = "networkid"; + public static final String NIC_ID = "nicid"; public static final String SPECIFY_VLAN = "specifyvlan"; public static final String IS_DEFAULT = "isdefault"; public static final String IS_SYSTEM = "issystem"; @@ -427,6 +428,7 @@ public class ApiConstants { public static final String CONDITION_IDS = "conditionids"; public static final String COUNTERPARAM_LIST = "counterparam"; public static final String AUTOSCALE_USER_ID = "autoscaleuserid"; + public static final String BAREMETAL_DISCOVER_NAME = "baremetaldiscovername"; public enum HostDetails { all, capacity, events, stats, min; diff --git a/api/src/org/apache/cloudstack/api/ApiErrorCode.java b/api/src/org/apache/cloudstack/api/ApiErrorCode.java index 51671cd7ec2..ee28fa05878 100644 --- a/api/src/org/apache/cloudstack/api/ApiErrorCode.java +++ b/api/src/org/apache/cloudstack/api/ApiErrorCode.java @@ -33,7 +33,7 @@ public enum ApiErrorCode { ACCOUNT_RESOURCE_LIMIT_ERROR(532), INSUFFICIENT_CAPACITY_ERROR(533), RESOURCE_UNAVAILABLE_ERROR(534), - RESOURCE_ALLOCATION_ERROR(534), + RESOURCE_ALLOCATION_ERROR(535), RESOURCE_IN_USE_ERROR(536), NETWORK_RULE_CONFLICT_ERROR(537); diff --git a/api/src/org/apache/cloudstack/api/BaseCmd.java b/api/src/org/apache/cloudstack/api/BaseCmd.java index ef72af9cae9..966e39cc1a5 100644 --- a/api/src/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/org/apache/cloudstack/api/BaseCmd.java @@ -289,14 +289,14 @@ public abstract class BaseCmd { StringBuffer sb = new StringBuffer(); if (RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) { // JSON response - sb.append("{ \"" + getCommandName() + "\" : { " + "\"@attributes\":{\"cloudstack-version\":\"" + _mgr.getVersion() + "\"},"); + sb.append("{ \"" + getCommandName() + "\" : { " + "\"@attributes\":{\"cloud-stack-version\":\"" + _mgr.getVersion() + "\"},"); sb.append("\"errorcode\" : \"" + apiException.getErrorCode() + "\", \"description\" : \"" + apiException.getDescription() + "\" } }"); } else { sb.append(""); sb.append("<" + getCommandName() + ">"); sb.append("" + apiException.getErrorCode() + ""); sb.append("" + escapeXml(apiException.getDescription()) + ""); - sb.append(""); + sb.append(""); } return sb.toString(); } @@ -307,10 +307,10 @@ public abstract class BaseCmd { // set up the return value with the name of the response if (RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) { - prefixSb.append("{ \"" + getCommandName() + "\" : { \"@attributes\":{\"cloudstack-version\":\"" + _mgr.getVersion() + "\"},"); + prefixSb.append("{ \"" + getCommandName() + "\" : { \"@attributes\":{\"cloud-stack-version\":\"" + _mgr.getVersion() + "\"},"); } else { prefixSb.append(""); - prefixSb.append("<" + getCommandName() + " cloudstack-version=\"" + _mgr.getVersion() + "\">"); + prefixSb.append("<" + getCommandName() + " cloud-stack-version=\"" + _mgr.getVersion() + "\">"); } int i = 0; diff --git a/api/src/org/apache/cloudstack/api/command/user/firewall/CreateEgressFirewallRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/firewall/CreateEgressFirewallRuleCmd.java new file mode 100644 index 00000000000..097b2c55f80 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/firewall/CreateEgressFirewallRuleCmd.java @@ -0,0 +1,341 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License.package org.apache.cloudstack.api.command.user.firewall; + +package org.apache.cloudstack.api.command.user.firewall; + +import java.util.ArrayList; +import java.util.List; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.NetworkResponse; + +import com.cloud.async.AsyncJob; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.utils.net.NetUtils; + +@APICommand(name = "createEgressFirewallRule", description = "Creates a egress firewall rule for a given network ", responseObject = FirewallResponse.class) +public class CreateEgressFirewallRuleCmd extends BaseAsyncCreateCmd implements FirewallRule { + public static final Logger s_logger = Logger.getLogger(CreateEgressFirewallRuleCmd.class.getName()); + + private static final String s_name = "createegressfirewallruleresponse"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter (name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the network id of the port forwarding rule") + private Long networkId; + + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, description = "the protocol for the firewall rule. Valid values are TCP/UDP/ICMP.") + private String protocol; + + @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "the starting port of firewall rule") + private Integer publicStartPort; + + @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "the ending port of firewall rule") + private Integer publicEndPort; + + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the cidr list to forward traffic from") + private List cidrlist; + + @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the icmp message being sent") + private Integer icmpType; + + @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "error code for this icmp message") + private Integer icmpCode; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "type of firewallrule: system/user") + private String type; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public Long getIpAddressId() { + return null; + } + + @Override + public String getProtocol() { + return protocol.trim(); + } + + @Override + public List getSourceCidrList() { + if (cidrlist != null) { + return cidrlist; + } else { + List oneCidrList = new ArrayList(); + oneCidrList.add(_networkService.getNetwork(networkId).getCidr()); + return oneCidrList; + } + } + + public Long getVpcId() { + Network network = _networkService.getNetwork(getNetworkId()); + if (network == null) { + throw new InvalidParameterValueException("Invalid networkId is given"); + } + + Long vpcId = network.getVpcId(); + return vpcId; + } + + + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + public void setSourceCidrList(List cidrs){ + cidrlist = cidrs; + } + + @Override + public void execute() throws ResourceUnavailableException { + UserContext callerContext = UserContext.current(); + boolean success = false; + FirewallRule rule = _entityMgr.findById(FirewallRule.class, getEntityId()); + try { + UserContext.current().setEventDetails("Rule Id: " + getEntityId()); + success = _firewallService.applyEgressFirewallRules (rule, callerContext.getCaller()); + // State is different after the rule is applied, so get new object here + rule = _entityMgr.findById(FirewallRule.class, getEntityId()); + FirewallResponse fwResponse = new FirewallResponse(); + if (rule != null) { + fwResponse = _responseGenerator.createFirewallResponse(rule); + setResponseObject(fwResponse); + } + fwResponse.setResponseName(getCommandName()); + } finally { + if (!success || rule == null) { + _firewallService.revokeFirewallRule(getEntityId(), true); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create firewall rule"); + } + } + } + + @Override + public long getId() { + throw new UnsupportedOperationException("database id can only provided by VO objects"); + } + + @Override + public String getXid() { + // FIXME: We should allow for end user to specify Xid. + return null; + } + + @Override + public Long getSourceIpAddressId() { + return null; + } + + @Override + public Integer getSourcePortStart() { + if (publicStartPort != null) { + return publicStartPort.intValue(); + } + return null; + } + + @Override + public Integer getSourcePortEnd() { + if (publicEndPort == null) { + if (publicStartPort != null) { + return publicStartPort.intValue(); + } + } else { + return publicEndPort.intValue(); + } + + return null; + } + + @Override + public Purpose getPurpose() { + return Purpose.Firewall; + } + + @Override + public State getState() { + throw new UnsupportedOperationException("Should never call me to find the state"); + } + + @Override + public long getNetworkId() { + return networkId; + } + + @Override + public long getEntityOwnerId() { + Account account = UserContext.current().getCaller(); + + if (account != null) { + return account.getId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + @Override + public long getDomainId() { + Network network =_networkService.getNetwork(networkId); + return network.getDomainId(); + } + + @Override + public void create() { + if (getSourceCidrList() != null) { + String guestCidr = _networkService.getNetwork(getNetworkId()).getCidr(); + + for (String cidr: getSourceCidrList()){ + if (!NetUtils.isValidCIDR(cidr)){ + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Source cidrs formatting error " + cidr); + } + if (cidr.equals(NetUtils.ALL_CIDRS)) { + continue; + } + if(!NetUtils.isNetworkAWithinNetworkB(cidr, guestCidr)) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, cidr + "is not within the guest cidr " + guestCidr); + } + } + } + if (getProtocol().equalsIgnoreCase(NetUtils.ALL_PROTO)) { + if (getSourcePortStart() != null && getSourcePortEnd() != null) { + throw new InvalidParameterValueException("Do not pass ports to protocol ALL, porotocol ALL do not require ports. Unable to create " + +"firewall rule for the network id=" + networkId); + } + } + + if (getVpcId() != null ){ + throw new InvalidParameterValueException("Unable to create firewall rule for the network id=" + networkId + + " as firewall egress rule can be created only for non vpc networks."); + } + + try { + FirewallRule result = _firewallService.createEgressFirewallRule(this); + setEntityId(result.getId()); + } catch (NetworkRuleConflictException ex) { + s_logger.info("Network rule conflict: " + ex.getMessage()); + s_logger.trace("Network Rule Conflict: ", ex); + throw new ServerApiException(ApiErrorCode.NETWORK_RULE_CONFLICT_ERROR, ex.getMessage()); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_FIREWALL_OPEN; + } + + @Override + public String getEventDescription() { + Network network = _networkService.getNetwork(networkId); + return ("Creating firewall rule for network: " + network + " for protocol:" + this.getProtocol()); + } + + + @Override + public long getAccountId() { + Network network = _networkService.getNetwork(networkId); + return network.getAccountId(); + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + return getNetworkId(); + } + + + @Override + public Integer getIcmpCode() { + if (icmpCode != null) { + return icmpCode; + } else if (protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO)) { + return -1; + } + return null; + } + + @Override + public Integer getIcmpType() { + if (icmpType != null) { + return icmpType; + } else if (protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO)) { + return -1; + + } + return null; + } + + @Override + public Long getRelated() { + return null; + } + + @Override + public FirewallRuleType getType() { + if (type != null && type.equalsIgnoreCase("system")) { + return FirewallRuleType.System; + } else { + return FirewallRuleType.User; + } + } + + @Override + public AsyncJob.Type getInstanceType() { + return AsyncJob.Type.FirewallRule; + } + + @Override + public TrafficType getTrafficType() { + return TrafficType.Egress; + } + + @Override + public String getUuid() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java index 0dcba5f4d5b..f5d7b1b02a7 100644 --- a/api/src/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java @@ -122,7 +122,7 @@ public class CreateFirewallRuleCmd extends BaseAsyncCreateCmd implements Firewal FirewallRule rule = _entityMgr.findById(FirewallRule.class, getEntityId()); try { UserContext.current().setEventDetails("Rule Id: " + getEntityId()); - success = _firewallService.applyFirewallRules(rule.getSourceIpAddressId(), callerContext.getCaller()); + success = _firewallService.applyIngressFirewallRules(rule.getSourceIpAddressId(), callerContext.getCaller()); // State is different after the rule is applied, so get new object here rule = _entityMgr.findById(FirewallRule.class, getEntityId()); @@ -238,7 +238,7 @@ public class CreateFirewallRuleCmd extends BaseAsyncCreateCmd implements Firewal } try { - FirewallRule result = _firewallService.createFirewallRule(this); + FirewallRule result = _firewallService.createIngressFirewallRule(this); setEntityId(result.getId()); setEntityUuid(result.getUuid()); } catch (NetworkRuleConflictException ex) { diff --git a/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java index 7d8dbb0bbdf..53348c9df44 100644 --- a/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java @@ -163,7 +163,7 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P UserContext.current().setEventDetails("Rule Id: " + getEntityId()); if (getOpenFirewall()) { - success = success && _firewallService.applyFirewallRules(ipAddressId, callerContext.getCaller()); + success = success && _firewallService.applyIngressFirewallRules(ipAddressId, callerContext.getCaller()); } success = success && _rulesService.applyPortForwardingRules(ipAddressId, callerContext.getCaller()); diff --git a/api/src/org/apache/cloudstack/api/command/user/firewall/DeleteEgressFirewallRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/firewall/DeleteEgressFirewallRuleCmd.java new file mode 100644 index 00000000000..4646cdbcf69 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/firewall/DeleteEgressFirewallRuleCmd.java @@ -0,0 +1,120 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License.package org.apache.cloudstack.api.command.user.firewall; + +package org.apache.cloudstack.api.command.user.firewall; + +import org.apache.cloudstack.api.APICommand; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import com.cloud.async.AsyncJob; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.rules.FirewallRule; +import com.cloud.user.UserContext; + +@APICommand(name = "deleteEgressFirewallRule", description="Deletes an ggress firewall rule", responseObject=SuccessResponse.class) +public class DeleteEgressFirewallRuleCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DeleteEgressFirewallRuleCmd.class.getName()); + private static final String s_name = "deleteegressfirewallruleresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @Parameter(name=ApiConstants.ID, type=CommandType.LONG, required=true, description="the ID of the firewall rule") + private Long id; + + // unexposed parameter needed for events logging + @Parameter(name=ApiConstants.ACCOUNT_ID, type=CommandType.LONG, expose=false) + private Long ownerId; + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public String getCommandName() { + return s_name; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_FIREWALL_CLOSE; + } + + @Override + public String getEventDescription() { + return ("Deleting egress firewall rule id=" + id); + } + + @Override + public long getEntityOwnerId() { + if (ownerId == null) { + FirewallRule rule = _entityMgr.findById(FirewallRule.class, id); + if (rule == null) { + throw new InvalidParameterValueException("Unable to find egress firewall rule by id"); + } else { + ownerId = _entityMgr.findById(FirewallRule.class, id).getAccountId(); + } + } + return ownerId; + } + + @Override + public void execute() throws ResourceUnavailableException { + UserContext.current().setEventDetails("Rule Id: " + id); + boolean result = _firewallService.revokeFirewallRule(id, true); + + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete egress firewall rule"); + } + } + + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + return _firewallService.getFirewallRule(id).getNetworkId(); + } + + @Override + public AsyncJob.Type getInstanceType() { + return AsyncJob.Type.FirewallRule; + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/firewall/ListEgressFirewallRulesCmd.java b/api/src/org/apache/cloudstack/api/command/user/firewall/ListEgressFirewallRulesCmd.java new file mode 100644 index 00000000000..6eeb53648f5 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/firewall/ListEgressFirewallRulesCmd.java @@ -0,0 +1,89 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License.package org.apache.cloudstack.api.command.user.firewall; + +package org.apache.cloudstack.api.command.user.firewall; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.api.APICommand; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListTaggedResourcesCmd; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.ListResponse; +import com.cloud.network.rules.FirewallRule; +import com.cloud.utils.Pair; + +@APICommand(name = "listEgressFirewallRules", description="Lists all egress firewall rules for network id.", responseObject=FirewallResponse.class) +public class ListEgressFirewallRulesCmd extends ListFirewallRulesCmd { + public static final Logger s_logger = Logger.getLogger(ListEgressFirewallRulesCmd.class.getName()); + private static final String s_name = "listegressfirewallrulesresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.ID, type=CommandType.LONG, description="Lists rule with the specified ID.") + private Long id; + + @Parameter(name=ApiConstants.NETWORK_ID, type=CommandType.LONG, description="the id network network for the egress firwall services") + private Long networkId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getNetworkId() { + return networkId; + } + + public FirewallRule.TrafficType getTrafficType () { + return FirewallRule.TrafficType.Egress; + } + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + Pair, Integer> result = _firewallService.listFirewallRules(this); + ListResponse response = new ListResponse(); + List fwResponses = new ArrayList(); + + for (FirewallRule fwRule : result.first()) { + FirewallResponse ruleData = _responseGenerator.createFirewallResponse(fwRule); + ruleData.setObjectName("firewallrule"); + fwResponses.add(ruleData); + } + response.setResponses(fwResponses, result.second()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/firewall/ListFirewallRulesCmd.java b/api/src/org/apache/cloudstack/api/command/user/firewall/ListFirewallRulesCmd.java index 80581fb7d3b..5062a4ea86f 100644 --- a/api/src/org/apache/cloudstack/api/command/user/firewall/ListFirewallRulesCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/firewall/ListFirewallRulesCmd.java @@ -56,6 +56,10 @@ public class ListFirewallRulesCmd extends BaseListTaggedResourcesCmd { return ipAddressId; } + public FirewallRule.TrafficType getTrafficType () { + return FirewallRule.TrafficType.Ingress; + } + public Long getId() { return id; } diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java index b42ff4cc5f1..28bde8f36b1 100644 --- a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java @@ -245,7 +245,7 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements UserContext.current().setEventDetails("Rule Id: " + getEntityId()); if (getOpenFirewall()) { - success = success && _firewallService.applyFirewallRules(getSourceIpAddressId(), callerContext.getCaller()); + success = success && _firewallService.applyIngressFirewallRules(getSourceIpAddressId(), callerContext.getCaller()); } // State might be different after the rule is applied, so get new object here diff --git a/api/src/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java index c3894c47f57..7867f8de926 100644 --- a/api/src/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java @@ -115,7 +115,7 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Sta UserContext.current().setEventDetails("Rule Id: "+ getEntityId()); if (getOpenFirewall()) { - result = result && _firewallService.applyFirewallRules(ipAddressId, UserContext.current().getCaller()); + result = result && _firewallService.applyIngressFirewallRules(ipAddressId, UserContext.current().getCaller()); } result = result && _rulesService.applyStaticNatRules(ipAddressId, UserContext.current().getCaller()); diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java new file mode 100644 index 00000000000..43340008e16 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java @@ -0,0 +1,121 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.vm; + +import java.util.ArrayList; +import java.util.EnumSet; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.*; +import org.apache.cloudstack.api.ApiConstants.VMDetails; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.uservm.UserVm; + +@APICommand(name = "addNicToVirtualMachine", description="Adds VM to specified network by creating a NIC", responseObject=UserVmResponse.class) + +public class AddNicToVMCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(AddNicToVMCmd.class); + private static final String s_name = "addnictovirtualmachineresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType=UserVmResponse.class, + required=true, description="Virtual Machine ID") + private Long vmId; + + @Parameter(name=ApiConstants.NETWORK_ID, type=CommandType.UUID, entityType=NetworkResponse.class, + required=true, description="Network ID") + private Long netId; + + @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, description="IP Address for the new network") + private String ipaddr; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getVmId() { + return vmId; + } + + public Long getNetworkId() { + return netId; + } + + public String getIpAddress() { + return ipaddr; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + public static String getResultObjectName() { + return "virtualmachine"; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NIC_CREATE; + } + + @Override + public String getEventDescription() { + return "Adding network " + getNetworkId() + " to user vm: " + getVmId(); + } + + @Override + public long getEntityOwnerId() { + UserVm vm = _responseGenerator.findUserVmById(getVmId()); + if (vm == null) { + return Account.ACCOUNT_ID_SYSTEM; // bad id given, parent this command to SYSTEM so ERROR events are tracked + } + return vm.getAccountId(); + } + + @Override + public void execute(){ + UserContext.current().setEventDetails("Vm Id: " + getVmId() + " Network Id: " + getNetworkId()); + UserVm result = _userVmService.addNicToVirtualMachine(this); + ArrayList dc = new ArrayList(); + dc.add(VMDetails.valueOf("nics")); + EnumSet details = EnumSet.copyOf(dc); + if (result != null){ + UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", details, result).get(0); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add NIC to vm. Refer to server logs for details."); + } + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index 9d39f692eaf..5300e3cefc8 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -146,8 +146,7 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { "Mutually exclusive with securitygroupids parameter") private List securityGroupNameList; - @ACL(checkKeyAccess=true) - @Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, entityType={Network.class, IpAddress.class}, + @Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, description = "ip to network mapping. Can't be specified with networkIds parameter." + " Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].networkid=uuid - requests to use ip 10.10.10.11 in network id=uuid") private Map ipToNetworkList; diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/RemoveNicFromVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/RemoveNicFromVMCmd.java new file mode 100644 index 00000000000..b1a870ec8f6 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vm/RemoveNicFromVMCmd.java @@ -0,0 +1,115 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.vm; + +import java.util.ArrayList; +import java.util.EnumSet; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.*; +import org.apache.cloudstack.api.ApiConstants.VMDetails; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.NicResponse; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.uservm.UserVm; + +@APICommand(name = "removeNicFromVirtualMachine", description="Removes VM from specified network by deleting a NIC", responseObject=UserVmResponse.class) + +public class RemoveNicFromVMCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(RemoveNicFromVMCmd.class); + private static final String s_name = "removenicfromvirtualmachineresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType=UserVmResponse.class, + required=true, description="Virtual Machine ID") + private Long vmId; + + @Parameter(name=ApiConstants.NIC_ID, type=CommandType.UUID, entityType=NicResponse.class, + required=true, description="NIC ID") + private Long nicId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getVmId() { + return vmId; + } + + public Long getNicId() { + return nicId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + public static String getResultObjectName() { + return "virtualmachine"; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NIC_DELETE; + } + + @Override + public String getEventDescription() { + return "Removing NIC " + getNicId() + " from user vm: " + getVmId(); + } + + + @Override + public long getEntityOwnerId() { + UserVm vm = _responseGenerator.findUserVmById(getVmId()); + if (vm == null) { + return Account.ACCOUNT_ID_SYSTEM; // bad id given, parent this command to SYSTEM so ERROR events are tracked + } + return vm.getAccountId(); + } + + @Override + public void execute(){ + UserContext.current().setEventDetails("Vm Id: "+getVmId() + " Nic Id: " + getNicId()); + UserVm result = _userVmService.removeNicFromVirtualMachine(this); + ArrayList dc = new ArrayList(); + dc.add(VMDetails.valueOf("nics")); + EnumSet details = EnumSet.copyOf(dc); + if (result != null){ + UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", details, result).get(0); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove NIC from vm, see error log for details"); + } + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/ResetVMSSHKeyCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/ResetVMSSHKeyCmd.java new file mode 100644 index 00000000000..3d1da155fcf --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vm/ResetVMSSHKeyCmd.java @@ -0,0 +1,151 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.user.vm; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import com.cloud.async.AsyncJob; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.uservm.UserVm; +import com.cloud.event.EventTypes; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; + +@APICommand(name = "resetSSHKeyForVirtualMachine", responseObject = UserVmResponse.class, description = "Resets the SSH Key for virtual machine. " + + "The virtual machine must be in a \"Stopped\" state. [async]") +public class ResetVMSSHKeyCmd extends BaseAsyncCmd { + + public static final Logger s_logger = Logger.getLogger(ResetVMSSHKeyCmd.class.getName()); + + private static final String s_name = "resetSSHKeyforvirtualmachineresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the virtual machine") + private Long id; + + @Parameter(name = ApiConstants.SSH_KEYPAIR, type = CommandType.STRING, required = true, description = "name of the ssh key pair used to login to the virtual machine") + private String name; + + + //Owner information + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the ssh key. Must be used with domainId.") + private String accountName; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used.") + private Long domainId; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the ssh key") + private Long projectId; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + + public Long getId() { + return id; + } + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getProjectId() { + return projectId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + + @Override + public String getEventType() { + return EventTypes.EVENT_VM_RESETSSHKEY; + } + + @Override + public String getEventDescription() { + return "resetting SSHKey for vm: " + getId(); + } + + public AsyncJob.Type getInstanceType() { + return AsyncJob.Type.VirtualMachine; + } + + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + UserVm vm = _responseGenerator.findUserVmById(getId()); + if (vm != null) { + return vm.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + public Long getInstanceId() { + return getId(); + } + + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException { + + UserContext.current().setEventDetails("Vm Id: " + getId()); + UserVm result = _userVmService.resetVMSSHKey(this); + + if (result != null) { + UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", result).get(0); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to reset vm SSHKey"); + } + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/UpdateDefaultNicForVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/UpdateDefaultNicForVMCmd.java new file mode 100644 index 00000000000..07518c90928 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vm/UpdateDefaultNicForVMCmd.java @@ -0,0 +1,115 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.vm; + +import java.util.ArrayList; +import java.util.EnumSet; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.*; +import org.apache.cloudstack.api.ApiConstants.VMDetails; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.NicResponse; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.uservm.UserVm; + +@APICommand(name = "updateDefaultNicForVirtualMachine", description="Changes the default NIC on a VM", responseObject=UserVmResponse.class) + +public class UpdateDefaultNicForVMCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(UpdateDefaultNicForVMCmd.class); + private static final String s_name = "updatedefaultnicforvirtualmachineresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType=UserVmResponse.class, + required=true, description="Virtual Machine ID") + private Long vmId; + + @Parameter(name=ApiConstants.NIC_ID, type=CommandType.UUID, entityType=NicResponse.class, + required=true, description="NIC ID") + private Long nicId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getVmId() { + return vmId; + } + + public Long getNicId() { + return nicId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + public static String getResultObjectName() { + return "virtualmachine"; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NIC_UPDATE; + } + + @Override + public String getEventDescription() { + return "Updating NIC " + getNicId() + " on user vm: " + getVmId(); + } + + + @Override + public long getEntityOwnerId() { + UserVm vm = _responseGenerator.findUserVmById(getVmId()); + if (vm == null) { + return Account.ACCOUNT_ID_SYSTEM; // bad id given, parent this command to SYSTEM so ERROR events are tracked + } + return vm.getAccountId(); + } + + @Override + public void execute(){ + UserContext.current().setEventDetails("Vm Id: "+getVmId() + " Nic Id: " + getNicId()); + UserVm result = _userVmService.updateDefaultNicForVirtualMachine(this); + ArrayList dc = new ArrayList(); + dc.add(VMDetails.valueOf("nics")); + EnumSet details = EnumSet.copyOf(dc); + if (result != null){ + UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", details, result).get(0); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to set default nic for VM. Refer to server logs for details."); + } + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java index 6c2e0f1eb54..0ab9a330ad1 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java @@ -21,7 +21,7 @@ import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; -import org.apache.cloudstack.api.response.DiskOfferingResponse; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.UserVmResponse; import com.cloud.exception.InvalidParameterValueException; import com.cloud.offering.ServiceOffering; @@ -44,7 +44,7 @@ public class UpgradeVMCmd extends BaseCmd { required=true, description="The ID of the virtual machine") private Long id; - @Parameter(name=ApiConstants.SERVICE_OFFERING_ID, type=CommandType.UUID, entityType=DiskOfferingResponse.class, + @Parameter(name=ApiConstants.SERVICE_OFFERING_ID, type=CommandType.UUID, entityType=ServiceOfferingResponse.class, required=true, description="the service offering ID to apply to the virtual machine") private Long serviceOfferingId; diff --git a/api/src/org/apache/cloudstack/api/response/FirewallResponse.java b/api/src/org/apache/cloudstack/api/response/FirewallResponse.java index 27992569e78..d399590f254 100644 --- a/api/src/org/apache/cloudstack/api/response/FirewallResponse.java +++ b/api/src/org/apache/cloudstack/api/response/FirewallResponse.java @@ -40,6 +40,9 @@ public class FirewallResponse extends BaseResponse { @SerializedName(ApiConstants.IP_ADDRESS_ID) @Param(description="the public ip address id for the firewall rule") private Long publicIpAddressId; + @SerializedName(ApiConstants.NETWORK_ID) @Param(description="the network id of the firewall rule") + private Long networkId; + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="the public ip address for the firewall rule") private String publicIpAddress; @@ -82,6 +85,10 @@ public class FirewallResponse extends BaseResponse { this.publicIpAddress = publicIpAddress; } + public void setNetworkId(Long networkId) { + this.networkId = networkId; + } + public void setState(String state) { this.state = state; } diff --git a/api/src/org/apache/cloudstack/api/response/NicResponse.java b/api/src/org/apache/cloudstack/api/response/NicResponse.java index a6ca5b8232d..7e200ae1c8b 100644 --- a/api/src/org/apache/cloudstack/api/response/NicResponse.java +++ b/api/src/org/apache/cloudstack/api/response/NicResponse.java @@ -17,11 +17,14 @@ package org.apache.cloudstack.api.response; import org.apache.cloudstack.api.ApiConstants; +import com.cloud.vm.Nic; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; @SuppressWarnings("unused") +@EntityReference(value=Nic.class) public class NicResponse extends BaseResponse { @SerializedName("id") @Param(description="the ID of the nic") diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties index 390738fd4cf..eda07782196 100644 --- a/client/WEB-INF/classes/resources/messages.properties +++ b/client/WEB-INF/classes/resources/messages.properties @@ -1215,6 +1215,7 @@ label.network.offering.id=Network Offering ID label.network.offering.name=Network Offering Name label.network.offering=Network Offering label.network.rate=Network Rate +label.network.rate.megabytes=Network Rate (Mb/s) label.network.read=Network Read label.network.type=Network Type label.network.write=Network Write diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 3740fb00633..755a5f27513 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -60,6 +60,7 @@ rebootVirtualMachine=15 startVirtualMachine=15 stopVirtualMachine=15 resetPasswordForVirtualMachine=15 +resetSSHKeyForVirtualMachine=15 updateVirtualMachine=15 listVirtualMachines=15 getVMPassword=15 @@ -320,6 +321,11 @@ listNetworks=15 restartNetwork=15 updateNetwork=15 +#### nic commands #### +addNicToVirtualMachine=15 +removeNicFromVirtualMachine=15 +updateDefaultNicForVirtualMachine=15 + #### SSH key pair commands registerSSHKeyPair=15 createSSHKeyPair=15 @@ -345,6 +351,11 @@ createFirewallRule=15 deleteFirewallRule=15 listFirewallRules=15 +#### +createEgressFirewallRule=15 +deleteEgressFirewallRule=15 +listEgressFirewallRules=15 + #### hypervisor capabilities commands updateHypervisorCapabilities=1 listHypervisorCapabilities=1 diff --git a/client/tomcatconf/server-ssl.xml.in b/client/tomcatconf/server-ssl.xml.in index 6e8bf525ec6..5d68f1d3d8b 100755 --- a/client/tomcatconf/server-ssl.xml.in +++ b/client/tomcatconf/server-ssl.xml.in @@ -197,7 +197,6 @@ >>>>>> bcfd64a... CS-15373: Awsapi port change to 7080. maxThreads="150" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreType="JKS" diff --git a/console-proxy/pom.xml b/console-proxy/pom.xml index 25265031258..0e12db0904f 100644 --- a/console-proxy/pom.xml +++ b/console-proxy/pom.xml @@ -25,6 +25,9 @@ cloudstack 4.1.0-SNAPSHOT + + mkisofs + log4j @@ -167,6 +170,19 @@ + + + genisoimage + + + /usr/bin/genisoimage + + + + genisoimage + + vmware @@ -209,7 +225,7 @@ - mkisofs + ${mkisofs} dist -quiet diff --git a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index 18a0426e2fa..05107e726a0 100755 --- a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -69,12 +69,14 @@ import com.cloud.agent.api.routing.SetStaticRouteCommand; import com.cloud.agent.api.routing.Site2SiteVpnCfgCommand; import com.cloud.agent.api.routing.VmDataCommand; import com.cloud.agent.api.routing.VpnUsersCfgCommand; +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.exception.InternalErrorException; import com.cloud.network.HAProxyConfigurator; import com.cloud.network.LoadBalancerConfigurator; +import com.cloud.network.rules.FirewallRule; import com.cloud.utils.NumbersUtil; import com.cloud.utils.component.Manager; import com.cloud.utils.net.NetUtils; @@ -214,11 +216,18 @@ public class VirtualRoutingResource implements Manager { return new SetFirewallRulesAnswer(cmd, false, results); } + FirewallRuleTO[] allrules = cmd.getRules(); + FirewallRule.TrafficType trafficType = allrules[0].getTrafficType(); + String[][] rules = cmd.generateFwRules(); final Script command = new Script(_firewallPath, _timeout, s_logger); command.add(routerIp); command.add("-F"); - + + if (trafficType == FirewallRule.TrafficType.Egress){ + command.add("-E"); + } + StringBuilder sb = new StringBuilder(); String[] fwRules = rules[0]; if (fwRules.length > 0) { diff --git a/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java b/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java index a4bea9df2b4..a634c68bcd2 100755 --- a/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java +++ b/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java @@ -398,7 +398,8 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements @Override public boolean accept(final File directory, final String fileName) { - return !fileName.startsWith("."); + File fileToUpload = new File(directory.getAbsolutePath() + "/" + fileName); + return !fileName.startsWith(".") && !fileToUpload.isDirectory(); } }, new ObjectNamingStrategy() { @Override diff --git a/docs/en-US/about-security-groups.xml b/docs/en-US/about-security-groups.xml index 85e8477cfb1..6a31b25ef48 100644 --- a/docs/en-US/about-security-groups.xml +++ b/docs/en-US/about-security-groups.xml @@ -24,7 +24,12 @@
About Security Groups - Security groups provide a way to isolate traffic to VMs. A security group is a group of VMs that filter their incoming and outgoing traffic according to a set of rules, called ingress and egress rules. These rules filter network traffic according to the IP address that is attempting to communicate with the VM. Security groups are particularly useful in zones that use basic networking, because there is a single guest network for all guest VMs. In &PRODUCT; 3.0.3 - 3.0.5, security groups are supported only in zones that use basic networking. + Security groups provide a way to isolate traffic to VMs. A security group is a group of + VMs that filter their incoming and outgoing traffic according to a set of rules, called + ingress and egress rules. These rules filter network traffic according to the IP address + that is attempting to communicate with the VM. Security groups are particularly useful in + zones that use basic networking, because there is a single guest network for all guest VMs. + In advanced zones, security groups are supported only on the KVM hypervisor. In a zone that uses advanced networking, you can instead define multiple guest networks to isolate traffic to VMs. diff --git a/docs/en-US/admin-alerts.xml b/docs/en-US/admin-alerts.xml index 4e47032c77a..5354c5e9b8e 100644 --- a/docs/en-US/admin-alerts.xml +++ b/docs/en-US/admin-alerts.xml @@ -31,6 +31,5 @@ The Management Server cluster runs low on CPU, memory, or storage resources The Management Server loses heartbeat from a Host for more than 3 minutes The Host cluster runs low on CPU, memory, or storage resources - - +
diff --git a/docs/en-US/hypervisor-host-install-libvirt.xml b/docs/en-US/hypervisor-host-install-libvirt.xml index 34e66783600..8ba2fe99a2a 100644 --- a/docs/en-US/hypervisor-host-install-libvirt.xml +++ b/docs/en-US/hypervisor-host-install-libvirt.xml @@ -31,7 +31,7 @@ Set the following paramaters: listen_tls = 0 listen_tcp = 1 - tcp_port = 16059 + tcp_port = "16059" auth_tcp = "none" mdns_adv = 0 diff --git a/docs/en-US/network-rate.xml b/docs/en-US/network-rate.xml new file mode 100644 index 00000000000..56fe25c04a5 --- /dev/null +++ b/docs/en-US/network-rate.xml @@ -0,0 +1,144 @@ + + +%BOOK_ENTITIES; +]> + +
+ Network Throttling + Network throttling is the process of controlling the network access and bandwidth usage + based on certain rules. &PRODUCT; controls this behaviour of the guest networks in the cloud by + using the network rate parameter. This parameter is defined as the default data transfer rate in + Mbps (Megabits Per Second) allowed in a guest network. It defines the upper limits for network + utilization. If the current utilization is below the allowed upper limits, access is granted, + else revoked. + You can throttle the network bandwidth either to control the usage above a certain limit for + some accounts, or to control network congestion in a large cloud environment. The network rate + for your cloud can be configured on the following: + + + Network Offering + + + Service Offering + + + Global parameter + + + If network rate is set to NULL in service offering, the value provided in the + vm.network.throttling.rate global parameter is applied. If the value is set to NULL for network + offering, the value provided in the network.throttling.rate global parameter is + considered. + For the default public, storage, and management networks, network rate is set to 0. This + implies that the public, storage, and management networks will have unlimited bandwidth by + default. For default guest networks, network rate is set to NULL. In this case, network rate is + defaulted to the global parameter value. + The following table gives you an overview of how network rate is applied on different types + of networks in &PRODUCT;. + + + + + + + Networks + Network Rate Is Taken from + + + + + Guest network of Virtual Router + Guest Network Offering + + + Public network of Virtual Router + Guest Network Offering + + + Storage network of Secondary Storage VM + System Network Offering + + + Management network of Secondary Storage VM + System Network Offering + + + Storage network of Console Proxy VM + System Network Offering + + + Management network of Console Proxy VM + System Network Offering + + + Storage network of Virtual Router + System Network Offering + + + Management network of Virtual Router + System Network Offering + + + Public network of Secondary Storage VM + System Network Offering + + + Public network of Console Proxy VM + System Network Offering + + + Default network of a guest VM + Compute Offering + + + Additional networks of a guest VM + Corresponding Network Offerings + + + + + A guest VM must have a default network, and can also have many additional networks. + Depending on various parameters, such as the host and virtual switch used, you can observe a + difference in the network rate in your cloud. For example, on a VMware host the actual network + rate varies based on where they are configured (compute offering, network offering, or both); + the network type (shared or isolated); and traffic direction (ingress or egress). + The network rate set for a network offering used by a particular network in &PRODUCT; is + used for the traffic shaping policy of a port group, for example: port group A, for that + network: a particular subnet or VLAN on the actual network. The virtual routers for that network + connects to the port group A, and by default instances in that network connects to this port + group. However, if an instance is deployed with a compute offering with the network rate set, + and if this rate is used for the traffic shaping policy of another port group for the network, + for example port group B, then instances using this compute offering are connected to the port + group B, instead of connecting to port group A. + The traffic shaping policy on standard port groups in VMware only applies to the egress + traffic, and the net effect depends on the type of network used in &PRODUCT;. In shared + networks, ingress traffic is unlimited for &PRODUCT;, and egress traffic is limited to the rate + that applies to the port group used by the instance if any. If the compute offering has a + network rate configured, this rate applies to the egress traffic, otherwise the network rate set + for the network offering applies. For isolated networks, the network rate set for the network + offering, if any, effectively applies to the ingress traffic. This is mainly because the network + rate set for the network offering applies to the egress traffic from the virtual router to the + instance. The egress traffic is limited by the rate that applies to the port group used by the + instance if any, similar to shared networks. + For example: + Network rate of network offering = 10 Mbps + Network rate of compute offering = 200 Mbps + In shared networks, ingress traffic will not be limited for &PRODUCT;, while egress traffic + will be limited to 200 Mbps. In an isolated network, ingress traffic will be limited to 10 Mbps + and egress to 200 Mbps. +
diff --git a/docs/en-US/offerings.xml b/docs/en-US/offerings.xml index ca6312d7bfe..c880a9c4810 100644 --- a/docs/en-US/offerings.xml +++ b/docs/en-US/offerings.xml @@ -25,5 +25,6 @@ are discussed in the section on setting up networking for users. + diff --git a/docs/en-US/security-groups-advanced-zones.xml b/docs/en-US/security-groups-advanced-zones.xml new file mode 100644 index 00000000000..bfae1883cc9 --- /dev/null +++ b/docs/en-US/security-groups-advanced-zones.xml @@ -0,0 +1,52 @@ + + +%BOOK_ENTITIES; +]> + + + +
+ Security Groups in Advanced Zones (KVM Only) + &PRODUCT; provides the ability to use security groups to provide isolation between + guests on a single shared, zone-wide network in an advanced zone where KVM is the + hypervisor. Using security groups in advanced zones rather than multiple VLANs allows a greater range + of options for setting up guest isolation in a cloud. + + Limitations + The following are not supported for this feature: + + + + Two IP ranges with the same VLAN and different gateway or netmask in security + group-enabled shared network. + + + Two IP ranges with the same VLAN and different gateway or netmask in + account-specific shared networks. + + + Multiple VLAN ranges in security group-enabled shared network. + + + Multiple VLAN ranges in account-specific shared networks. + + + Security groups must be enabled in the zone in order for this feature to be used. +
diff --git a/docs/en-US/security-groups.xml b/docs/en-US/security-groups.xml index 3c08965c8d3..00dbf5ed2e8 100644 --- a/docs/en-US/security-groups.xml +++ b/docs/en-US/security-groups.xml @@ -25,7 +25,8 @@
Security Groups - + +
diff --git a/docs/en-US/snapshot-throttling.xml b/docs/en-US/snapshot-throttling.xml new file mode 100644 index 00000000000..6bda437e503 --- /dev/null +++ b/docs/en-US/snapshot-throttling.xml @@ -0,0 +1,45 @@ + + +%BOOK_ENTITIES; +]> + + + +
+ Snapshot Job Throttling + When a snapshot of a virtual machine is requested, the snapshot job runs on the same + host where the VM is running or, in the case of a stopped VM, the host where it ran last. If + many snapshots are requested for VMs on a single host, this can lead to problems with too + many snapshot jobs overwhelming the resources of the host. + To address this situation, the cloud's root administrator can throttle how many snapshot + jobs are executed simultaneously on the hosts in the cloud by using the global configuration + setting concurrent.snapshots.threshold.perhost. By using this setting, the administrator can + better ensure that snapshot jobs do not time out and hypervisor hosts do not experience + performance issues due to hosts being overloaded with too many snapshot requests. + Set concurrent.snapshots.threshold.perhost to a value that represents a best guess about + how many snapshot jobs the hypervisor hosts can execute at one time, given the current + resources of the hosts and the number of VMs running on the hosts. If a given host has more + snapshot requests, the additional requests are placed in a waiting queue. No new snapshot + jobs will start until the number of currently executing snapshot jobs falls below the + configured limit. + The admin can also set job.expire.minutes to place a maximum on how long a snapshot + request will wait in the queue. If this limit is reached, the snapshot request fails and + returns an error message. +
diff --git a/docs/en-US/using-sshkeys.xml b/docs/en-US/using-sshkeys.xml index 1e98eb699ad..cd10d68470a 100644 --- a/docs/en-US/using-sshkeys.xml +++ b/docs/en-US/using-sshkeys.xml @@ -102,4 +102,11 @@ KfEEuzcCUIxtJYTahJ1pvlFkQ8anpuxjSEDp8x/18bq3 ssh -i ~/.ssh/keypair-doc <ip address> The -i parameter tells the ssh client to use a ssh key found at ~/.ssh/keypair-doc. +
+ Resetting SSH Keys + With the API command resetSSHKeyForVirtualMachine, a user can set or reset the SSH keypair + assigned to a virtual machine. A lost or compromised SSH keypair + can be changed, and the user can access the VM by using the new keypair. Just create or register a + new keypair, then call resetSSHKeyForVirtualMachine. +
diff --git a/docs/en-US/vmware-install.xml b/docs/en-US/vmware-install.xml index 736b122fd0f..467e1358638 100644 --- a/docs/en-US/vmware-install.xml +++ b/docs/en-US/vmware-install.xml @@ -21,253 +21,313 @@ specific language governing permissions and limitations under the License. --> -
- VMware vSphere Installation and Configuration - If you want to use the VMware vSphere hypervisor to run guest virtual machines, install vSphere on the host(s) in your cloud. - -
+ VMware vSphere Installation and Configuration + If you want to use the VMware vSphere hypervisor to run guest virtual machines, install + vSphere on the host(s) in your cloud. + +
Preparation Checklist for VMware For a smoother installation, gather the following information before you start: - Information listed in - Information listed in + + Information listed in + + + Information listed in +
- vCenter Checklist - You will need the following information about vCenter. - + vCenter Checklist + You will need the following information about vCenter. + - - - - + + + + - vCenter Requirement - Value - Notes + vCenter Requirement + Value + Notes - - + + - vCenter User - - This user must have admin privileges. + vCenter User + + This user must have admin privileges. - vCenter User Password - - Password for the above user. + vCenter User Password + + Password for the above user. - vCenter Datacenter Name - - Name of the datacenter. + vCenter Datacenter Name + + Name of the datacenter. - vCenter Cluster Name - - Name of the cluster. + vCenter Cluster Name + + Name of the cluster. - + - +
- Networking Checklist for VMware - You will need the following information about VLAN. - + Networking Checklist for VMware + You will need the following information about VLAN. + - - - - + + + + - VLAN Information - Value - Notes + VLAN Information + Value + Notes - - + + - ESXi VLAN - - VLAN on which all your ESXi hypervisors reside. + ESXi VLAN + + VLAN on which all your ESXi hypervisors reside. - ESXI VLAN IP Address - - IP Address Range in the ESXi VLAN. One address per Virtual Router is used from this range. + ESXI VLAN IP Address + + IP Address Range in the ESXi VLAN. One address per Virtual Router is used + from this range. - ESXi VLAN IP Gateway - - + ESXi VLAN IP Gateway + + - ESXi VLAN Netmask - - + ESXi VLAN Netmask + + - Management Server VLAN - - VLAN on which the &PRODUCT; Management server is installed. + Management Server VLAN + + VLAN on which the &PRODUCT; Management server is + installed. - Public VLAN - - VLAN for the Public Network. + Public VLAN + + VLAN for the Public Network. - Public VLAN Gateway - - + Public VLAN Gateway + + - Public VLAN Netmask - - + Public VLAN Netmask + + - Public VLAN IP Address Range - - Range of Public IP Addresses available for &PRODUCT; use. These addresses will be used for virtual router on &PRODUCT; to route private traffic to external networks. + Public VLAN IP Address Range + + Range of Public IP Addresses available for &PRODUCT; use. These addresses + will be used for virtual router on &PRODUCT; to route private traffic to external + networks. - VLAN Range for Customer use - - A contiguous range of non-routable VLANs. One VLAN will be assigned for each customer. + VLAN Range for Customer use + + A contiguous range of non-routable VLANs. One VLAN will be assigned for + each customer. - + - +
-
-
+
+
vSphere Installation Steps - If you haven't already, you'll need to download and purchase vSphere from the VMware Website (https://www.vmware.com/tryvmware/index.php?p=vmware-vsphere&lp=1) and install it by following the VMware vSphere Installation Guide. - - Following installation, perform the following configuration, which are described in the next few sections: + + If you haven't already, you'll need to download and purchase vSphere from the VMware + Website (https://www.vmware.com/tryvmware/index.php?p=vmware-vsphere&lp=1) and + install it by following the VMware vSphere Installation Guide. + + + Following installation, perform the following configuration, which are described in + the next few sections: - - - + + + - + Required Optional - + - + ESXi host setup NIC bonding - - - Configure host physical networking, virtual switch, vCenter Management Network, and extended port range + + + Configure host physical networking, virtual switch, vCenter Management + Network, and extended port range Multipath storage - - + + Prepare storage for iSCSI - - - - Configure clusters in vCenter and add hosts to them, or add hosts without clusters to vCenter - - + + + + Configure clusters in vCenter and add hosts to them, or add hosts + without clusters to vCenter + + - + - + -
-
+
+
ESXi Host setup - All ESXi hosts should enable CPU hardware virtualization support in BIOS. Please note hardware virtualization support is not enabled by default on most servers. -
-
+ All ESXi hosts should enable CPU hardware virtualization support in BIOS. Please note + hardware virtualization support is not enabled by default on most servers. +
+
Physical Host Networking - You should have a plan for cabling the vSphere hosts. Proper network configuration is required before adding a vSphere host to &PRODUCT;. To configure an ESXi host, you can use vClient to add it as standalone host to vCenter first. Once you see the host appearing in the vCenter inventory tree, click the host node in the inventory tree, and navigate to the Configuration tab. + You should have a plan for cabling the vSphere hosts. Proper network configuration is + required before adding a vSphere host to &PRODUCT;. To configure an ESXi host, you can use + vClient to add it as standalone host to vCenter first. Once you see the host appearing in the + vCenter inventory tree, click the host node in the inventory tree, and navigate to the + Configuration tab. - + - - + + vsphereclient.png: vSphere client - + - In the host configuration tab, click the "Hardware/Networking" link to bring up the networking configuration page as above. -
- Configure Virtual Switch - A default virtual switch vSwitch0 is created. &PRODUCT; requires all ESXi hosts in the cloud to use the same set of virtual switch names. If you change the default virtual switch name, you will need to configure one or more &PRODUCT; configuration variables as well. -
+ In the host configuration tab, click the "Hardware/Networking" link to bring up + the networking configuration page as above. +
+ Configure Virtual Switch + A default virtual switch vSwitch0 is created. &PRODUCT; requires all ESXi hosts in the + cloud to use the same set of virtual switch names. If you change the default virtual switch + name, you will need to configure one or more &PRODUCT; configuration variables as + well. +
Separating Traffic - &PRODUCT; allows you to use vCenter to configure three separate networks per ESXi host. These networks are identified by the name of the vSwitch they are connected to. The allowed networks for configuration are public (for traffic to/from the public internet), guest (for guest-guest traffic), and private (for management and usually storage traffic). You can use the default virtual switch for all three, or create one or two other vSwitches for those traffic types. - If you want to separate traffic in this way you should first create and configure vSwitches in vCenter according to the vCenter instructions. Take note of the vSwitch names you have used for each traffic type. You will configure &PRODUCT; to use these vSwitches. -
-
+ &PRODUCT; allows you to use vCenter to configure three separate networks per ESXi + host. These networks are identified by the name of the vSwitch they are connected to. The + allowed networks for configuration are public (for traffic to/from the public internet), + guest (for guest-guest traffic), and private (for management and usually storage traffic). + You can use the default virtual switch for all three, or create one or two other vSwitches + for those traffic types. + If you want to separate traffic in this way you should first create and configure + vSwitches in vCenter according to the vCenter instructions. Take note of the vSwitch names + you have used for each traffic type. You will configure &PRODUCT; to use these + vSwitches. +
+
Increasing Ports - By default a virtual switch on ESXi hosts is created with 56 ports. We recommend setting it to 4088, the maximum number of ports allowed. To do that, click the "Properties..." link for virtual switch (note this is not the Properties link for Networking). + By default a virtual switch on ESXi hosts is created with 56 ports. We recommend + setting it to 4088, the maximum number of ports allowed. To do that, click the + "Properties..." link for virtual switch (note this is not the Properties link + for Networking). - + - - + + vsphereclient.png: vSphere client - + - In vSwitch properties dialog, select the vSwitch and click Edit. You should see the following dialog: + In vSwitch properties dialog, select the vSwitch and click Edit. You should see the + following dialog: - + - - + + vsphereclient.png: vSphere client - + - In this dialog, you can change the number of switch ports. After you've done that, ESXi hosts are required to reboot in order for the setting to take effect. -
+ In this dialog, you can change the number of switch ports. After you've done + that, ESXi hosts are required to reboot in order for the setting to take effect. +
- Configure vCenter Management Network - In the vSwitch properties dialog box, you may see a vCenter management network. This same network will also be used as the &PRODUCT; management network. &PRODUCT; requires the vCenter management network to be configured properly. Select the management network item in the dialog, then click Edit. - + Configure vCenter Management Network + In the vSwitch properties dialog box, you may see a vCenter management network. This + same network will also be used as the &PRODUCT; management network. &PRODUCT; requires the + vCenter management network to be configured properly. Select the management network item in + the dialog, then click Edit. + - + - vsphereclient.png: vSphere client + vsphereclient.png: vSphere client - - Make sure the following values are set: - - VLAN ID set to the desired ID - vMotion enabled. - Management traffic enabled. - - If the ESXi hosts have multiple VMKernel ports, and ESXi is not using the default value "Management Network" as the management network name, you must follow these guidelines to configure the management network port group so that &PRODUCT; can find it: - - Use one label for the management network port across all ESXi hosts. - In the &PRODUCT; UI, go to Configuration - Global Settings and set vmware.management.portgroup to the management network label from the ESXi hosts. - + + Make sure the following values are set: + + + VLAN ID set to the desired ID + + + vMotion enabled. + + + Management traffic enabled. + + + If the ESXi hosts have multiple VMKernel ports, and ESXi is not using the default value + "Management Network" as the management network name, you must follow these + guidelines to configure the management network port group so that &PRODUCT; can find + it: + + + Use one label for the management network port across all ESXi hosts. + + + In the &PRODUCT; UI, go to Configuration - Global Settings and set + vmware.management.portgroup to the management network label from the ESXi hosts. + +
- Extend Port Range for &PRODUCT; Console Proxy - (Applies only to VMware vSphere version 4.x) - You need to extend the range of firewall ports that the console proxy works with on the hosts. This is to enable the console proxy to work with VMware-based VMs. The default additional port range is 59000-60000. To extend the port range, log in to the VMware ESX service console on each host and run the following commands: - + Extend Port Range for &PRODUCT; Console Proxy + (Applies only to VMware vSphere version 4.x) + You need to extend the range of firewall ports that the console proxy works with on the + hosts. This is to enable the console proxy to work with VMware-based VMs. The default + additional port range is 59000-60000. To extend the port range, log in to the VMware ESX + service console on each host and run the following commands: + esxcfg-firewall -o 59000-60000,tcp,in,vncextras esxcfg-firewall -o 59000-60000,tcp,out,vncextras
- Configure NIC Bonding for vSphere - NIC bonding on vSphere hosts may be done according to the vSphere installation guide. + Configure NIC Bonding for vSphere + NIC bonding on vSphere hosts may be done according to the vSphere installation + guide.
-
- -
+
Storage Preparation for vSphere (iSCSI only) - Use of iSCSI requires preparatory work in vCenter. You must add an iSCSI target and create an iSCSI datastore. + Use of iSCSI requires preparatory work in vCenter. You must add an iSCSI target and create + an iSCSI datastore. If you are using NFS, skip this section.
- Enable iSCSI initiator for ESXi hosts - + Enable iSCSI initiator for ESXi hosts + - In vCenter, go to hosts and Clusters/Configuration, and click Storage Adapters link. You will see: - + In vCenter, go to hosts and Clusters/Configuration, and click Storage Adapters link. + You will see: + - + - vsphereclient.png: vSphere client + vsphereclient.png: vSphere client - + - Select iSCSI software adapter and click Properties. - + Select iSCSI software adapter and click Properties. + - + - vsphereclient.png: vSphere client + vsphereclient.png: vSphere client - + - Click the Configure... button. - + Click the Configure... button. + - + - vsphereclient.png: vSphere client + vsphereclient.png: vSphere client - + - Check Enabled to enable the initiator. - Click OK to save. - + + Check Enabled to enable the initiator. + + + Click OK to save. + +
- Add iSCSI target - Under the properties dialog, add the iSCSI target info: - + Add iSCSI target + Under the properties dialog, add the iSCSI target info: + - + - vsphereclient.png: vSphere client + vsphereclient.png: vSphere client - - Repeat these steps for all ESXi hosts in the cluster. + + Repeat these steps for all ESXi hosts in the cluster.
- Create an iSCSI datastore - You should now create a VMFS datastore. Follow these steps to do so: - - Select Home/Inventory/Datastores. - Right click on the datacenter node. - Choose Add Datastore... command. - Follow the wizard to create a iSCSI datastore. - - This procedure should be done on one host in the cluster. It is not necessary to do this on all hosts. - + Create an iSCSI datastore + You should now create a VMFS datastore. Follow these steps to do so: + + + Select Home/Inventory/Datastores. + + + Right click on the datacenter node. + + + Choose Add Datastore... command. + + + Follow the wizard to create a iSCSI datastore. + + + This procedure should be done on one host in the cluster. It is not necessary to do this + on all hosts. + - + - vsphereclient.png: vSphere client + vsphereclient.png: vSphere client - +
- Multipathing for vSphere (Optional) - Storage multipathing on vSphere nodes may be done according to the vSphere installation guide. + Multipathing for vSphere (Optional) + Storage multipathing on vSphere nodes may be done according to the vSphere installation + guide.
-
-
+
+
Add Hosts or Configure Clusters (vSphere) - Use vCenter to create a vCenter cluster and add your desired hosts to the cluster. You will later add the entire cluster to &PRODUCT;. (see ). -
+ Use vCenter to create a vCenter cluster and add your desired hosts to the cluster. You + will later add the entire cluster to &PRODUCT;. (see ). +
+
+ Applying Hotfixes to a VMware vSphere Host + + + Disconnect the VMware vSphere cluster from &PRODUCT;. It should remain disconnected + long enough to apply the hotfix on the host. + + + Log in to the &PRODUCT; UI as root. + See . + + + Navigate to the VMware cluster, click Actions, and select Unmanage. + + + Watch the cluster status until it shows Unmanaged. + + + + + Perform the following on each of the ESXi hosts in the cluster: + + + Move each of the ESXi hosts in the cluster to maintenance mode. + + + Ensure that all the VMs are migrated to other hosts in that cluster. + + + If there is only one host in that cluster, shutdown all the VMs and move the host + into maintenance mode. + + + Apply the patch on the ESXi host. + + + Restart the host if prompted. + + + Cancel the maintenance mode on the host. + + + + + Reconnect the cluster to &PRODUCT;: + + + Log in to the &PRODUCT; UI as root. + + + Navigate to the VMware cluster, click Actions, and select Manage. + + + Watch the status to see that all the hosts come up. It might take several minutes + for the hosts to come up. + Alternatively, verify the host state is properly synchronized and updated in the + &PRODUCT; database. + + + + +
diff --git a/docs/en-US/working-with-snapshots.xml b/docs/en-US/working-with-snapshots.xml index d6b145875d4..e7e45177d97 100644 --- a/docs/en-US/working-with-snapshots.xml +++ b/docs/en-US/working-with-snapshots.xml @@ -29,4 +29,5 @@ Snapshots may be taken for volumes, including both root and data disks. The administrator places a limit on the number of stored snapshots per user. Users can create new volumes from the snapshot for recovery of particular files and they can create templates from snapshots to boot from a restored disk. Users can create snapshots manually or by setting up automatic recurring snapshot policies. Users can also create disk volumes from snapshots, which may be attached to a VM like any other disk volume. Snapshots of both root disks and data disks are supported. However, &PRODUCT; does not currently support booting a VM from a recovered root disk. A disk recovered from snapshot of a root disk is treated as a regular data disk; the data on recovered disk can be accessed by attaching the disk to a VM. A completed snapshot is copied from primary storage to secondary storage, where it is stored until deleted or purged by newer snapshot. +
diff --git a/packaging/centos63/cloud-agent.rc b/packaging/centos63/cloud-agent.rc index 084b71543a8..505118fcbc8 100755 --- a/packaging/centos63/cloud-agent.rc +++ b/packaging/centos63/cloud-agent.rc @@ -58,7 +58,7 @@ export JAVA_HOME SCP="" DCP="" ACP=`ls /usr/share/cloud/java/* | tr '\n' ':'` -JCP="/usr/share/java/commons-daemon.jar" +JCP="/usr/share/java/jna.jar:/usr/share/java/commons-daemon.jar" # We need to append the JSVC daemon JAR to the classpath # AgentShell implements the JSVC daemon methods diff --git a/packaging/centos63/cloud-usage.rc b/packaging/centos63/cloud-usage.rc new file mode 100755 index 00000000000..9aa01ea2b11 --- /dev/null +++ b/packaging/centos63/cloud-usage.rc @@ -0,0 +1,138 @@ +#!/bin/bash + +### BEGIN INIT INFO +# Provides: cloud usage +# Required-Start: $network $local_fs +# Required-Stop: $network $local_fs +# Default-Start: 3 4 5 +# Default-Stop: 0 1 2 6 +# Short-Description: Start/stop Apache CloudStack Usage Monitor +# Description: This scripts Starts/Stops the Apache CloudStack Usage Monitor +## The CloudStack Usage Monitor is a part of the Apache CloudStack project and is used +## for storing usage statistics from instances. +## JSVC (Java daemonizing) is used for starting and stopping the usage monitor. +### END INIT INFO + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +. /etc/rc.d/init.d/functions + +SHORTNAME="cloud-usage" +PIDFILE=/var/run/"$SHORTNAME".pid +LOCKFILE=/var/lock/subsys/"$SHORTNAME" +LOGFILE=/var/log/cloud/usage/usage.log +PROGNAME="CloudStack Usage Monitor" +CLASS="com.cloud.usage.UsageServer" +PROG="jsvc" +DAEMON="/usr/bin/jsvc" +USER=cloud + +unset OPTIONS +[ -r /etc/sysconfig/default/"$SHORTNAME" ] && source /etc/sysconfig/default/"$SHORTNAME" + +# The first existing directory is used for JAVA_HOME (if JAVA_HOME is not defined in $DEFAULT) +JDK_DIRS="/usr/lib/jvm/java-6-openjdk /usr/lib/jvm/java-6-openjdk-i386 /usr/lib/jvm/java-6-openjdk-amd64 /usr/lib/jvm/java-6-sun /usr/lib/jvm/jre-1.6.0 /usr/lib/j2sdk1.5-sun /usr/lib/jre-openjdk" + +for jdir in $JDK_DIRS; do + if [ -r "$jdir/bin/java" -a -z "${JAVA_HOME}" ]; then + JAVA_HOME="$jdir" + fi +done +export JAVA_HOME + +SCP="" +DCP="" +UCP=`ls /usr/share/cloud/usage/java/* | tr '\n' ':'` +JCP="/usr/share/java/commons-daemon.jar" + +# We need to append the JSVC daemon JAR to the classpath +# AgentShell implements the JSVC daemon methods +export CLASSPATH="$SCP:$DCP:$UCP:$JCP:/etc/sysconfig + +start() { + if [ -s "$PIDFILE" ] && kill -0 $(cat "$PIDFILE") >/dev/null 2>&1; then + echo "$PROGNAME apparently already running" + exit 0 + fi + + if hostname --fqdn >/dev/null 2>&1 ; then + true + else + echo "The host name does not resolve properly to an IP address. Cannot start $PROGNAME" + exit 1 + fi + + echo -n "Starting $PROGNAME" "$SHORTNAME" + + if daemon --pidfile $PIDFILE $DAEMON -cp "$CLASSPATH" -pidfile "$PIDFILE" -user "$USER" -errfile SYSLOG -Dpid=$$ $CLASS + RETVAL=$? + then + rc=0 + sleep 1 + if ! kill -0 $(cat "$PIDFILE") >/dev/null 2>&1; then + failure + rc=1 + fi + else + rc=1 + fi + + if [ $rc -eq 0 ]; then + success + else + failure + rm -f "$PIDFILE" + fi + echo +} + +stop() { + echo -n "Stopping $PROGNAME" "$SHORTNAME" + killproc -p $PIDFILE $DAEMON + if [ "$?" -eq 0 ]; then + success + else + failure + fi + rm -f "$PIDFILE" + echo +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status -p $PIDFILE $SHORTNAME + RETVAL=$? + ;; + restart | force-reload) + stop + sleep 3 + start + ;; + *) + echo "Usage: $0 {start|stop|restart|force-reload|status}" + RETVAL=3 +esac + +exit $RETVAL + diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index d828e673c97..50e1d11ef71 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -112,10 +112,21 @@ Requires: bridge-utils Requires: ebtables Requires: jsvc Requires: jna +Requires: jakarta-commons-daemon +Requires: jakarta-commons-daemon-jsvc Group: System Environment/Libraries %description agent The CloudStack agent for KVM hypervisors +%package usage +Summary: CloudStack Usage calculation server +Requires: java >= 1.6.0 +Requires: jsvc +Requires: jakarta-commons-daemon +Requires: jakarta-commons-daemon-jsvc +%description usage +The CloudStack usage calculation service + %prep echo Doing CloudStack build @@ -196,6 +207,7 @@ chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/management chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/agent chmod -R ugo+x ${RPM_BUILD_ROOT}/usr/share/%{name}/management/webapps/client/WEB-INF/classes/scripts +# KVM Agent mkdir -p ${RPM_BUILD_ROOT}/etc/cloud/agent mkdir -p ${RPM_BUILD_ROOT}/var/log/cloud/agent install -D packaging/centos63/cloud-agent.rc ${RPM_BUILD_ROOT}/etc/init.d/%{name}-agent @@ -204,12 +216,17 @@ install -D agent/target/transformed/environment.properties ${RPM_BUILD_ROOT}/etc install -D agent/target/transformed/log4j-cloud.xml ${RPM_BUILD_ROOT}/etc/cloud/agent/log4j-cloud.xml install -D agent/target/transformed/cloud-setup-agent ${RPM_BUILD_ROOT}/usr/bin/cloud-setup-agent install -D agent/target/transformed/cloud-ssh ${RPM_BUILD_ROOT}/usr/bin/cloud-ssh - install -D plugins/hypervisors/kvm/target/%{name}-plugin-hypervisor-kvm-%{_maventag}.jar ${RPM_BUILD_ROOT}/usr/share/cloud/java/%{name}-plugin-hypervisor-kvm-%{_maventag}.jar cp plugins/hypervisors/kvm/target/dependencies/* ${RPM_BUILD_ROOT}/usr/share/cloud/java mkdir -p ${RPM_BUILD_ROOT}/usr/share/cloud/scripts cp -r scripts/* ${RPM_BUILD_ROOT}/usr/share/cloud/scripts +# Usage server +install -D usage/target/%{name}-usage-%{_maventag}.jar ${RPM_BUILD_ROOT}/usr/share/cloud/usage/java/%{name}-usage-%{_maventag}.jar +cp usage/target/dependencies/* ${RPM_BUILD_ROOT}/usr/share/cloud/usage/java +install -D packaging/centos63/cloud-usage.rc ${RPM_BUILD_ROOT}/etc/init.d/%{name}-usage +mkdir -p ${RPM_BUILD_ROOT}/var/log/cloud/usage/ + %clean [ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} @@ -306,6 +323,11 @@ fi %attr(0644,root,root) /usr/share/cloud/java/*.jar %attr(0755,root,root) /usr/share/cloud/scripts +%files usage +%attr(0755,root,root) %{_sysconfdir}/init.d/cloud-usage +%attr(0644,root,root) /usr/share/cloud/usage/java/*.jar +%dir /var/log/cloud/usage + %changelog * Fri Oct 03 2012 Hugo Trippaers 4.1.0 - new style spec file diff --git a/patches/systemvm/debian/config/etc/iptables/iptables-router b/patches/systemvm/debian/config/etc/iptables/iptables-router index 28469fbc639..3f5bc5f736b 100644 --- a/patches/systemvm/debian/config/etc/iptables/iptables-router +++ b/patches/systemvm/debian/config/etc/iptables/iptables-router @@ -24,6 +24,7 @@ COMMIT :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] +:FW_OUTBOUND - [0:0] -A INPUT -d 224.0.0.18/32 -j ACCEPT -A INPUT -d 225.0.0.50/32 -j ACCEPT -A INPUT -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT @@ -37,10 +38,11 @@ COMMIT -A INPUT -i eth1 -p tcp -m state --state NEW --dport 3922 -j ACCEPT -A INPUT -i eth0 -p tcp -m state --state NEW --dport 80 -j ACCEPT -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT --A FORWARD -i eth0 -o eth2 -j ACCEPT -A FORWARD -i eth2 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT -A FORWARD -i eth0 -o eth0 -m state --state NEW -j ACCEPT -A FORWARD -i eth0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT +-A FORWARD -i eth0 -o eth2 -j FW_OUTBOUND +-I FW_OUTBOUND -m state --state RELATED,ESTABLISHED -j ACCEPT COMMIT *mangle :PREROUTING ACCEPT [0:0] diff --git a/patches/systemvm/debian/config/opt/cloud/bin/ipassoc.sh b/patches/systemvm/debian/config/opt/cloud/bin/ipassoc.sh index 5af5d9233a7..f326fac9e54 100755 --- a/patches/systemvm/debian/config/opt/cloud/bin/ipassoc.sh +++ b/patches/systemvm/debian/config/opt/cloud/bin/ipassoc.sh @@ -212,9 +212,9 @@ add_first_ip() { ip_addr_add $ethDev $pubIp sudo iptables -D FORWARD -i $ethDev -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT - sudo iptables -D FORWARD -i eth0 -o $ethDev -j ACCEPT + sudo iptables -D FORWARD -i eth0 -o $ethDev -j FW_OUTBOUND sudo iptables -A FORWARD -i $ethDev -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT - sudo iptables -A FORWARD -i eth0 -o $ethDev -j ACCEPT + sudo iptables -A FORWARD -i eth0 -o $ethDev -j FW_OUTBOUND add_snat $1 if [ $? -gt 0 -a $? -ne 2 ] @@ -246,7 +246,7 @@ remove_first_ip() { [ "$mask" == "" ] && mask="32" sudo iptables -D FORWARD -i $ethDev -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT - sudo iptables -D FORWARD -i eth0 -o $ethDev -j ACCEPT + sudo iptables -D FORWARD -i eth0 -o $ethDev -j FW_OUTBOUND remove_snat $1 sudo ip addr del dev $ethDev "$ipNoMask/$mask" diff --git a/patches/systemvm/debian/config/root/firewallRule_egress.sh b/patches/systemvm/debian/config/root/firewallRule_egress.sh new file mode 100755 index 00000000000..0da7718741d --- /dev/null +++ b/patches/systemvm/debian/config/root/firewallRule_egress.sh @@ -0,0 +1,171 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# $Id: firewallRule_egress.sh 9947 2013-01-17 19:34:24Z manuel $ $HeadURL: svn://svn.lab.vmops.com/repos/vmdev/java/patches/xenserver/root/firewallRule_egress.sh $ +# firewallRule_egress.sh -- allow some ports / protocols from vm instances +# @VERSION@ + +source /root/func.sh + +lock="biglock" +locked=$(getLockFile $lock) +if [ "$locked" != "1" ] +then + exit 1 +fi +#set -x +usage() { + printf "Usage: %s: -a protocol:startport:endport:sourcecidrs> \n" $(basename $0) >&2 + printf "sourcecidrs format: cidr1-cidr2-cidr3-...\n" +} + +fw_egress_remove_backup() { + sudo iptables -D FW_OUTBOUND -j _FW_EGRESS_RULES + sudo iptables -F _FW_EGRESS_RULES + sudo iptables -X _FW_EGRESS_RULES +} + +fw_egress_save() { + sudo iptables -E FW_EGRESS_RULES _FW_EGRESS_RULES +} + +fw_egress_chain () { +#supress errors 2>/dev/null + fw_egress_remove_backup + fw_egress_save + sudo iptables -N FW_EGRESS_RULES + sudo iptables -A FW_OUTBOUND -j FW_EGRESS_RULES +} + +fw_egress_backup_restore() { + sudo iptables -A FW_OUTBOUND -j FW_EGRESS_RULES + sudo iptables -E _FW_EGRESS_RULES FW_EGRESS_RULES + fw_egress_remove_backup +} + + +fw_entry_for_egress() { + local rule=$1 + + local prot=$(echo $rule | cut -d: -f2) + local sport=$(echo $rule | cut -d: -f3) + local eport=$(echo $rule | cut -d: -f4) + local cidrs=$(echo $rule | cut -d: -f5 | sed 's/-/ /g') + if [ "$sport" == "0" -a "$eport" == "0" ] + then + DPORT="" + else + DPORT="--dport $sport:$eport" + fi + logger -t cloud "$(basename $0): enter apply fw egress rules for guest $prot:$sport:$eport:$cidrs" + + for lcidr in $cidrs + do + [ "$prot" == "reverted" ] && continue; + if [ "$prot" == "icmp" ] + then + typecode="$sport/$eport" + [ "$eport" == "-1" ] && typecode="$sport" + [ "$sport" == "-1" ] && typecode="any" + sudo iptables -A FW_EGRESS_RULES -p $prot -s $lcidr --icmp-type $typecode \ + -j ACCEPT + result=$? + elif [ "$prot" == "all" ] + then + sudo iptables -A FW_EGRESS_RULES -p $prot -s $lcidr -j ACCEPT + result=$? + else + sudo iptables -A FW_EGRESS_RULES -p $prot -s $lcidr \ + $DPORT -j ACCEPT + result=$? + fi + + [ $result -gt 0 ] && + logger -t cloud "Error adding iptables entry for guest network $prot:$sport:$eport:$cidrs" && + break + done + + logger -t cloud "$(basename $0): exit apply egress firewall rules for guest network" + return $result +} + + +aflag=0 +rules="" +rules_list="" +ip="" +dev="" +shift +shift +while getopts 'a:' OPTION +do + case $OPTION in + a) aflag=1 + rules="$OPTARG" + ;; + ?) usage + unlock_exit 2 $lock $locked + ;; + esac +done + +if [ "$aflag" != "1" ] +then + usage + unlock_exit 2 $lock $locked +fi + +if [ -n "$rules" ] +then + rules_list=$(echo $rules | cut -d, -f1- --output-delimiter=" ") +fi + +# rule format +# protocal:sport:eport:cidr +#-a tcp:80:80:0.0.0.0/0::tcp:220:220:0.0.0.0/0:,tcp:222:222:192.168.10.0/24-75.57.23.0/22-88.100.33.1/32 +# if any entry is reverted , entry will be in the format reverted:0:0:0 +# example : tcp:80:80:0.0.0.0/0:, tcp:220:220:0.0.0.0/0:,200.1.1.2:reverted:0:0:0 + +success=0 + +fw_egress_chain +for r in $rules_list +do + fw_entry_for_egress $r + success=$? + if [ $success -gt 0 ] + then + logger -t cloud "failure to apply fw egress rules " + break + else + logger -t cloud "successful in applying fw egress rules" + fi +done + +if [ $success -gt 0 ] +then + logger -t cloud "restoring from backup for guest network" + fw_egress_backup_restore +else + logger -t cloud "deleting backup for guest network" +fi + +fw_egress_remove_backup + +unlock_exit $success $lock $locked + + diff --git a/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/ApiRateLimitTest.java b/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/ApiRateLimitTest.java index 85eeaaf4223..502b15cf316 100644 --- a/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/ApiRateLimitTest.java +++ b/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/ApiRateLimitTest.java @@ -181,7 +181,7 @@ public class ApiRateLimitTest { assertTrue("The first request should be allowed", isUnderLimit(key)); // Allow the token to expire - Thread.sleep(1001); + Thread.sleep(1020); assertTrue("Another request after interval should be allowed as well", isUnderLimit(key)); } @@ -219,7 +219,8 @@ public class ApiRateLimitTest { ApiLimitResponse response = _limitService.searchApiLimit(testAccount); assertEquals("apiIssued is incorrect", 5, response.getApiIssued()); assertEquals("apiAllowed is incorrect", 5, response.getApiAllowed()); - assertTrue("expiredAfter is incorrect", response.getExpireAfter() < 1000); + // using <= to account for inaccurate System.currentTimeMillis() clock in Windows environment + assertTrue("expiredAfter is incorrect", response.getExpireAfter() <= 1000); } diff --git a/plugins/hypervisors/baremetal/pom.xml b/plugins/hypervisors/baremetal/pom.xml new file mode 100755 index 00000000000..600eedb1440 --- /dev/null +++ b/plugins/hypervisors/baremetal/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + org.apache.cloudstack + cloudstack-plugins + 4.1.0-SNAPSHOT + ../../pom.xml + + cloud-plugin-hypervisor-baremetal + Apache CloudStack Plugin - Hypervisor Baremetal + + + commons-lang + commons-lang + 2.6 + + + + diff --git a/plugins/hypervisors/baremetal/resources/security_group_agent/cs-sgagent b/plugins/hypervisors/baremetal/resources/security_group_agent/cs-sgagent new file mode 100755 index 00000000000..02426d0aa10 --- /dev/null +++ b/plugins/hypervisors/baremetal/resources/security_group_agent/cs-sgagent @@ -0,0 +1,67 @@ +#!/bin/sh +# the following is chkconfig init header +# +# cs-sgagent: cloudStack baremetal sercurity group agent +# +# chkconfig: 345 97 03 +# description: This is a daemon instructed by CloudStack management server \ +# to perform baremetal security group related operations\ +# +# processname: cs-sgagent +# pidfile: /var/run/cssgagent.pid +# + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +check_status() { + pidfile='/var/run/cssgagent.pid' + if [ ! -f $pidfile ]; then + echo "cloudstack baremetal security group agent is stopped" + exit 1 + else + pid=`cat $pidfile` + ps -p $pid > /dev/null + if [ $? -eq 0 ]; then + echo "cloudstack baremetal security group agent is running, pid is $pid" + exit 0 + else + echo "cloudstack baremetal security group agent is stopped, but pidfile at $pidfile is not cleaned. It may be caused by the agent crashed at last time, manually cleaning it would be ok" + exit 1 + fi + fi +} + +if [ $# -eq 0 ]; then + echo "usage: $0 +[start|stop|restart|status]" + exit 1 +fi + +if [ "$@" = "status" ]; then + check_status +else + python -c "from security_group_agent import cs_sg_agent; cs_sg_agent.main()" $@ +fi + +if [ $? -eq 0 ]; then + echo "$@ cloudstack baremetal security group agent .... SUCCESS" + exit 0 +else + echo "$@ cloudstack baremetal security group agent .... FAILED" + exit 1 +fi diff --git a/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/__init__.py b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/__init__.py new file mode 100644 index 00000000000..f7f5f60ff05 --- /dev/null +++ b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/__init__.py @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Automatically generated by addcopyright.py at 01/29/2013 diff --git a/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/cs_sg_agent.py b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/cs_sg_agent.py new file mode 100755 index 00000000000..a292c0bcfb8 --- /dev/null +++ b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/cs_sg_agent.py @@ -0,0 +1,237 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Automatically generated by addcopyright.py at 01/29/2013 +''' +Created on Jan 2, 2013 + +@author: frank +''' +import cherrypy +import sglib +import xmlobject +import types +import uuid +import os.path +import sys +import os + +class SGRule(object): + def __init__(self): + self.protocol = None + self.start_port = None + self.end_port = None + self.allowed_ips = [] + +class IPSet(object): + IPSET_TYPE = 'hash:ip' + def __init__(self, setname, ips): + self.ips = ips + self.name = setname + + def create(self): + tmpname = str(uuid.uuid4()).replace('-', '')[0:30] + sglib.ShellCmd('ipset -N %s %s' % (tmpname, self.IPSET_TYPE))() + try: + for ip in self.ips: + sglib.ShellCmd('ipset -A %s %s' % (tmpname, ip))() + + try: + sglib.ShellCmd('ipset -N %s %s' % (self.name, self.IPSET_TYPE))() + cherrypy.log('created new ipset: %s' % self.name) + except Exception: + cherrypy.log('%s already exists, no need to create new' % self.name) + finally: + sglib.ShellCmd('ipset -W %s %s' % (tmpname, self.name))() + sglib.ShellCmd('ipset -F %s' % tmpname)() + sglib.ShellCmd('ipset -X %s' % tmpname)() + + @staticmethod + def destroy_sets(sets_to_keep): + sets = sglib.ShellCmd('ipset list')() + for s in sets.split('\n'): + if 'Name:' in s: + set_name = s.split(':', 1)[1].strip() + if not set_name in sets_to_keep: + sglib.ShellCmd('ipset destroy %s' % set_name)() + cherrypy.log('destroyed unused ipset: %s' % set_name) + +class SGAgent(object): + def __init__(self): + pass + + def _self_list(self, obj): + if isinstance(obj, types.ListType): + return obj + else: + return [obj] + + def set_rules(self, req): + body = req.body + doc = xmlobject.loads(body) + vm_name = doc.vmName.text_ + vm_id = doc.vmId.text_ + vm_ip = doc.vmIp.text_ + vm_mac = doc.vmMac.text_ + sig = doc.signature.text_ + seq = doc.sequenceNumber.text_ + + def parse_rules(rules, lst): + for i in self._self_list(rules): + r = SGRule() + r.protocol = i.protocol.text_ + r.start_port = i.startPort.text_ + r.end_port = i.endPort.text_ + if hasattr(i, 'ip'): + for ip in self._self_list(i.ip): + r.allowed_ips.append(ip.text_) + lst.append(r) + + i_rules = [] + if hasattr(doc, 'ingressRules'): + parse_rules(doc.ingressRules, i_rules) + + e_rules = [] + if hasattr(doc, 'egressRules'): + parse_rules(doc.egressRules, e_rules) + + def create_chain(name): + try: + sglib.ShellCmd('iptables -F %s' % name)() + except Exception: + sglib.ShellCmd('iptables -N %s' % name)() + + def apply_rules(rules, chainname, direction, action, current_set_names): + create_chain(chainname) + for r in i_rules: + allow_any = False + if '0.0.0.0/0' in r.allowed_ips: + allow_any = True + r.allowed_ips.remove('0.0.0.0/0') + + if r.allowed_ips: + setname = '_'.join([chainname, r.protocol, r.start_port, r.end_port]) + ipset = IPSet(setname, r.allowed_ips) + ipset.create() + current_set_names.append(setname) + + if r.protocol == 'all': + cmd = ['iptables -I', chainname, '-m state --state NEW -m set --set', setname, direction, '-j', action] + sglib.ShellCmd(' '.join(cmd))() + elif r.protocol != 'icmp': + port_range = ":".join([r.start_port, r.end_port]) + cmd = ['iptables', '-I', chainname, '-p', r.protocol, '-m', r.protocol, '--dport', port_range, '-m state --state NEW -m set --set', setname, direction, '-j', action] + sglib.ShellCmd(' '.join(cmd))() + else: + port_range = "/".join([r.start_port, r.end_port]) + if r.start_port == "-1": + port_range = "any" + cmd = ['iptables', '-I', i_chain_name, '-p', 'icmp', '--icmp-type', port_range, '-m set --set', setname, direction, '-j', action] + sglib.ShellCmd(' '.join(cmd))() + + + if allow_any and r.protocol != 'all': + if r.protocol != 'icmp': + port_range = ":".join([r.start_port, r.end_port]) + cmd = ['iptables', '-I', chainname, '-p', r.protocol, '-m', r.protocol, '--dport', port_range, '-m', 'state', '--state', 'NEW', '-j', action] + sglib.ShellCmd(' '.join(cmd))() + else: + port_range = "/".join([r.start_port, r.end_port]) + if r.start_port == "-1": + port_range = "any" + cmd = ['iptables', '-I', i_chain_name, '-p', 'icmp', '--icmp-type', port_range, '-j', action] + sglib.ShellCmd(' '.join(cmd))() + + current_sets = [] + i_chain_name = vm_name + '-in' + apply_rules(i_rules, i_chain_name, 'src', 'ACCEPT', current_sets) + e_chain_name = vm_name + '-eg' + apply_rules(e_rules, e_chain_name, 'dst', 'RETURN', current_sets) + + if e_rules: + sglib.ShellCmd('iptables -A %s -j RETURN' % e_chain_name) + else: + sglib.ShellCmd('iptables -A %s -j DROP' % e_chain_name) + + sglib.ShellCmd('iptables -A %s -j DROP' % i_chain_name) + IPSet.destroy_sets(current_sets) + + + def echo(self, req): + cherrypy.log("echo: I am alive") + + def index(self): + req = sglib.Request.from_cherrypy_request(cherrypy.request) + cmd_name = req.headers['command'] + + if not hasattr(self, cmd_name): + raise ValueError("SecurityGroupAgent doesn't have a method called '%s'" % cmd_name) + method = getattr(self, cmd_name) + + return method(req) + index.exposed = True + + @staticmethod + def start(): + cherrypy.log.access_file = '/var/log/cs-securitygroup.log' + cherrypy.log.error_file = '/var/log/cs-securitygroup.log' + cherrypy.server.socket_host = '0.0.0.0' + cherrypy.server.socket_port = 9988 + cherrypy.quickstart(SGAgent()) + + @staticmethod + def stop(): + cherrypy.engine.exit() + +PID_FILE = '/var/run/cssgagent.pid' +class SGAgentDaemon(sglib.Daemon): + def __init__(self): + super(SGAgentDaemon, self).__init__(PID_FILE) + self.is_stopped = False + self.agent = SGAgent() + sglib.Daemon.register_atexit_hook(self._do_stop) + + def _do_stop(self): + if self.is_stopped: + return + self.is_stopped = True + self.agent.stop() + + def run(self): + self.agent.start() + + def stop(self): + self.agent.stop() + super(SGAgentDaemon, self).stop() + +def main(): + usage = 'usage: python -c "from security_group_agent import cs_sg_agent; cs_sg_agent.main()" start|stop|restart' + if len(sys.argv) != 2 or not sys.argv[1] in ['start', 'stop', 'restart']: + print usage + sys.exit(1) + + cmd = sys.argv[1] + agentdaemon = SGAgentDaemon() + if cmd == 'start': + agentdaemon.start() + elif cmd == 'stop': + agentdaemon.stop() + else: + agentdaemon.restart() + + sys.exit(0) + \ No newline at end of file diff --git a/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/sglib.py b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/sglib.py new file mode 100755 index 00000000000..bf64effa190 --- /dev/null +++ b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/sglib.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Automatically generated by addcopyright.py at 01/29/2013 + +import sys, os, time, atexit +import traceback +import subprocess +from signal import SIGTERM +import cherrypy +import copy + +class Request(object): + def __init__(self): + self.headers = None + self.body = None + self.method = None + self.query_string = None + + @staticmethod + def from_cherrypy_request(creq): + req = Request() + req.headers = copy.copy(creq.headers) + req.body = creq.body.fp.read() if creq.body else None + req.method = copy.copy(creq.method) + req.query_string = copy.copy(creq.query_string) if creq.query_string else None + return req + +class ShellError(Exception): + '''shell error''' + +class ShellCmd(object): + ''' + classdocs + ''' + def __init__(self, cmd, workdir=None, pipe=True): + ''' + Constructor + ''' + self.cmd = cmd + if pipe: + self.process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE, executable='/bin/sh', cwd=workdir) + else: + self.process = subprocess.Popen(cmd, shell=True, executable='/bin/sh', cwd=workdir) + + self.stdout = None + self.stderr = None + self.return_code = None + + def __call__(self, is_exception=True): + (self.stdout, self.stderr) = self.process.communicate() + if is_exception and self.process.returncode != 0: + err = [] + err.append('failed to execute shell command: %s' % self.cmd) + err.append('return code: %s' % self.process.returncode) + err.append('stdout: %s' % self.stdout) + err.append('stderr: %s' % self.stderr) + raise ShellError('\n'.join(err)) + + self.return_code = self.process.returncode + return self.stdout + +class Daemon(object): + """ + A generic daemon class. + + Usage: subclass the Daemon class and override the run() method + """ + atexit_hooks = [] + + def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + self.pidfile = pidfile + + @staticmethod + def register_atexit_hook(hook): + Daemon.atexit_hooks.append(hook) + + @staticmethod + def _atexit(): + for hook in Daemon.atexit_hooks: + try: + hook() + except Exception: + content = traceback.format_exc() + err = 'Exception when calling atexit hook[%s]\n%s' % (hook.__name__, content) + #logger.error(err) + + def daemonize(self): + """ + do the UNIX double-fork magic, see Stevens' "Advanced + Programming in the UNIX Environment" for details (ISBN 0201563177) + http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 + """ + try: + pid = os.fork() + if pid > 0: + # exit first parent + sys.exit(0) + except OSError, e: + sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) + sys.exit(1) + + # decouple from parent environment + os.chdir("/") + os.setsid() + os.umask(0) + + # do second fork + try: + pid = os.fork() + if pid > 0: + # exit from second parent + sys.exit(0) + except OSError, e: + sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) + sys.exit(1) + + # redirect standard file descriptors + sys.stdout.flush() + sys.stderr.flush() + si = file(self.stdin, 'r') + so = file(self.stdout, 'a+') + se = file(self.stderr, 'a+', 0) + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + + # write pidfile + Daemon.register_atexit_hook(self.delpid) + atexit.register(Daemon._atexit) + pid = str(os.getpid()) + file(self.pidfile,'w').write("%s\n" % pid) + + def delpid(self): + os.remove(self.pidfile) + + def start(self): + """ + Start the daemon + """ + # Check for a pidfile to see if the daemon already runs + try: + pf = file(self.pidfile,'r') + pid = int(pf.read().strip()) + pf.close() + except IOError: + pid = None + + if pid: + pscmd = ShellCmd('ps -p %s > /dev/null' % pid) + pscmd(is_exception=False) + if pscmd.return_code == 0: + message = "Daemon already running, pid is %s\n" + sys.stderr.write(message % pid) + sys.exit(0) + + # Start the daemon + self.daemonize() + try: + self.run() + except Exception: + content = traceback.format_exc() + #logger.error(content) + sys.exit(1) + + def stop(self): + """ + Stop the daemon + """ + # Get the pid from the pidfile + try: + pf = file(self.pidfile,'r') + pid = int(pf.read().strip()) + pf.close() + except IOError: + pid = None + + if not pid: + message = "pidfile %s does not exist. Daemon not running?\n" + sys.stderr.write(message % self.pidfile) + return # not an error in a restart + + # Try killing the daemon process + try: + while 1: + os.kill(pid, SIGTERM) + time.sleep(0.1) + except OSError, err: + err = str(err) + if err.find("No such process") > 0: + if os.path.exists(self.pidfile): + os.remove(self.pidfile) + else: + print str(err) + sys.exit(1) + + def restart(self): + """ + Restart the daemon + """ + self.stop() + self.start() + + def run(self): + """ + You should override this method when you subclass Daemon. It will be called after the process has been + daemonized by start() or restart(). + """ diff --git a/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/xmlobject.py b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/xmlobject.py new file mode 100755 index 00000000000..cb66d265a5a --- /dev/null +++ b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/xmlobject.py @@ -0,0 +1,97 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Automatically generated by addcopyright.py at 01/29/2013 +''' +Created on Dec 25, 2012 + +@author: Frank +''' +import xml.etree.ElementTree as etree +import re +import types + +class XmlObject(object): + def __init__(self, tag): + self.__tag_name__ = tag + + def put_attr(self, name, val): + val = val.strip().strip('\t') + setattr(self, name + '_', val) + + def put_text(self, val): + val = val.strip().strip('\n').strip('\t') + if val == "": + setattr(self, 'text_', None) + else: + setattr(self, 'text_', val) + + def put_node(self, name, val): + if not hasattr(self, name): + setattr(self, name, val) + return + + nodes = getattr(self, name) + if not isinstance(nodes, types.ListType): + nodes = [] + old = getattr(self, name) + nodes.append(old) + nodes.append(val) + setattr(self, name, nodes) + else: + nodes.append(val) + setattr(self, name, nodes) + + def get(self, name, default=None): + if hasattr(self, name): + val = getattr(self, name) + if name.endswith('_'): + return val + else: + return val.text_ + else: + return default + + def __getattr__(self, name): + if name.endswith('__'): + n = name[:-1] + if hasattr(self, n): + return getattr(self, n) + else: + return None + else: + e = AttributeError('%s has no attribute %s. missing attribute %s in element <%s>' % (self.__class__.__name__, name, name, self.__tag_name__)) + setattr(e, 'missing_attrib', name) + setattr(e, 'tag_name', self.__tag_name__) + raise e + + +def _loads(node): + xo = XmlObject(node.tag) + for key in node.attrib.keys(): + xo.put_attr(key, node.attrib.get(key)) + if node.text: + xo.put_text(node.text) + for n in list(node): + sub_xo = _loads(n) + xo.put_node(n.tag, sub_xo) + return xo + +def loads(xmlstr): + xmlstr = re.sub(r'xmlns=".*"', '', xmlstr) + root = etree.fromstring(xmlstr) + return _loads(root) \ No newline at end of file diff --git a/plugins/hypervisors/baremetal/resources/security_group_agent/setup.py b/plugins/hypervisors/baremetal/resources/security_group_agent/setup.py new file mode 100755 index 00000000000..2de41d265ba --- /dev/null +++ b/plugins/hypervisors/baremetal/resources/security_group_agent/setup.py @@ -0,0 +1,44 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Automatically generated by addcopyright.py at 01/29/2013 +from setuptools import setup, find_packages +import sys, os + +version = '1.0' + +setup(name='security_group_agent', + version=version, + description="security group agent for CloudStack Baremetal", + long_description="""\ +security group agent for CloudStack Baremetal""", + classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers + keywords='security group cloudstack', + author='Frank Zhang', + author_email='frank.zhang@citrix.com', + url='http://incubator.apache.org/cloudstack/', + license='Apache License 2', + packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), + include_package_data=True, + zip_safe=True, + install_requires=[ + 'CherryPy', + ], + entry_points=""" + # -*- Entry points: -*- + """, + ) diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbDao.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbDao.java new file mode 100755 index 00000000000..0f20c677f5d --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbDao.java @@ -0,0 +1,25 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import com.cloud.utils.db.GenericDao; + +public interface BaremetalCmdbDao extends GenericDao { + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbDaoImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbDaoImpl.java new file mode 100755 index 00000000000..fcc95efba9c --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbDaoImpl.java @@ -0,0 +1,29 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import javax.ejb.Local; + +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +@Local(value = {BaremetalCmdbDao.class}) +@DB(txn = false) +public class BaremetalCmdbDaoImpl extends GenericDaoBase implements BaremetalCmdbDao { + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbVO.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbVO.java new file mode 100755 index 00000000000..ee3848a5e9d --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbVO.java @@ -0,0 +1,104 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import java.util.UUID; + +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="baremetal_cmdb") +public class BaremetalCmdbVO { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name="uuid") + private String uuid; + + @Column(name="zone_id") + private long zoneId; + + @Column(name="url") + private String url; + + @Column(name="password") + private String password; + + @Column(name="username") + private String username; + + public BaremetalCmdbVO() { + uuid = UUID.randomUUID().toString(); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpDao.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpDao.java new file mode 100644 index 00000000000..fe6d86c80d4 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpDao.java @@ -0,0 +1,25 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import com.cloud.utils.db.GenericDao; + +public interface BaremetalDhcpDao extends GenericDao { + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpDaoImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpDaoImpl.java new file mode 100644 index 00000000000..3d9c6deaabc --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpDaoImpl.java @@ -0,0 +1,42 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria2; + +@Local(value=BaremetalDhcpDao.class) +@DB(txn=false) +public class BaremetalDhcpDaoImpl extends GenericDaoBase implements BaremetalDhcpDao { + + public BaremetalDhcpDaoImpl() { + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpVO.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpVO.java new file mode 100644 index 00000000000..0bfd541ffbc --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpVO.java @@ -0,0 +1,118 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name="baremetal_dhcp_devices") +public class BaremetalDhcpVO { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name="uuid") + private String uuid; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "pod_id") + private long podId; + + @Column(name = "physical_network_id") + private long physicalNetworkId; + + @Column(name = "nsp_id") + private long networkServiceProviderId ; + + @Column(name = "device_type") + private String deviceType; + + public BaremetalDhcpVO() { + super(); + this.uuid = UUID.randomUUID().toString(); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + public long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public void setPhysicalNetworkId(long physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public String getDeviceType() { + return deviceType; + } + + public void setDeviceType(String deviceType) { + this.deviceType = deviceType; + } + + public long getNetworkServiceProviderId() { + return networkServiceProviderId; + } + + public void setNetworkServiceProviderId(long networkServiceProviderId) { + this.networkServiceProviderId = networkServiceProviderId; + } + + public long getPodId() { + return podId; + } + + public void setPodId(long podId) { + this.podId = podId; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeDao.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeDao.java new file mode 100644 index 00000000000..c640638e9ba --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeDao.java @@ -0,0 +1,26 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import com.cloud.utils.db.GenericDao; +import com.cloud.utils.db.GenericDaoBase; + +public interface BaremetalPxeDao extends GenericDao { + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeDaoImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeDaoImpl.java new file mode 100644 index 00000000000..c47d6b2fa37 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeDaoImpl.java @@ -0,0 +1,38 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria2; + +@Local(value = {BaremetalPxeDao.class}) +@DB(txn = false) +public class BaremetalPxeDaoImpl extends GenericDaoBase implements BaremetalPxeDao { +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeVO.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeVO.java new file mode 100644 index 00000000000..e0daa46d819 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeVO.java @@ -0,0 +1,115 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import java.util.UUID; + +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="baremetal_pxe_devices") +public class BaremetalPxeVO { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name="uuid") + private String uuid; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "pod_id") + private long podId; + + @Column(name = "physical_network_id") + private long physicalNetworkId; + + @Column(name = "nsp_id") + private long networkServiceProviderId ; + + @Column(name = "device_type") + private String deviceType; + + public long getId() { + return id; + } + + public BaremetalPxeVO() { + uuid = UUID.randomUUID().toString(); + } + + public void setId(long id) { + this.id = id; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + public long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public void setPhysicalNetworkId(long physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public long getNetworkServiceProviderId() { + return networkServiceProviderId; + } + + public void setNetworkServiceProviderId(long networkServiceProviderId) { + this.networkServiceProviderId = networkServiceProviderId; + } + + public long getPodId() { + return podId; + } + + public void setPodId(long podId) { + this.podId = podId; + } + + public String getDeviceType() { + return deviceType; + } + + public void setDeviceType(String deviceType) { + this.deviceType = deviceType; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/AddBaremetalHostCmd.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/AddBaremetalHostCmd.java new file mode 100755 index 00000000000..5222d103699 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/AddBaremetalHostCmd.java @@ -0,0 +1,41 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.manager; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.command.admin.host.AddHostCmd; + +public class AddBaremetalHostCmd extends AddHostCmd { + + @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, description="ip address intentionally allocated to this host after provisioning") + private String vmIpAddress; + + public AddBaremetalHostCmd() { + this.getFullUrlParams().put(ApiConstants.BAREMETAL_DISCOVER_NAME, BareMetalDiscoverer.class.getName()); + } + + public String getVmIpAddress() { + return vmIpAddress; + } + + public void setVmIpAddress(String vmIpAddress) { + this.vmIpAddress = vmIpAddress; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalDiscoverer.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalDiscoverer.java new file mode 100755 index 00000000000..64eeaea17c0 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalDiscoverer.java @@ -0,0 +1,280 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.manager; + +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 javax.naming.ConfigurationException; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.baremetal.networkservice.BareMetalResourceBase; +import com.cloud.configuration.Config; +import com.cloud.configuration.dao.ConfigurationDao; +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.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.network.Network; +import com.cloud.resource.Discoverer; +import com.cloud.resource.DiscovererBase; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ResourceStateAdapter; +import com.cloud.resource.ServerResource; +import com.cloud.resource.UnableDeleteHostException; +import com.cloud.utils.component.Inject; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; +import com.cloud.utils.script.Script2; +import com.cloud.utils.script.Script2.ParamType; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.dao.VMInstanceDao; + +@Local(value=Discoverer.class) +public class BareMetalDiscoverer extends DiscovererBase implements Discoverer, ResourceStateAdapter { + protected static final Logger s_logger = Logger.getLogger(BareMetalDiscoverer.class); + @Inject protected ClusterDao _clusterDao; + @Inject protected HostDao _hostDao; + @Inject protected DataCenterDao _dcDao; + @Inject protected VMInstanceDao _vmDao = null; + @Inject protected ResourceManager _resourceMgr; + @Inject protected ConfigurationDao _configDao; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _resourceMgr.registerResourceStateAdapter(this.getClass().getSimpleName(), this); + return super.configure(name, params); + } + + @Override + public boolean stop() { + _resourceMgr.unregisterResourceStateAdapter(this.getClass().getSimpleName()); + return super.stop(); + } + + @Override + public Map> find(long dcId, Long podId, Long clusterId, URI url, String username, String password, List hostTags) + throws DiscoveryException { + + String discoverName = _params.get(ApiConstants.BAREMETAL_DISCOVER_NAME); + if (!this.getClass().getName().equals(discoverName)) { + return null; + } + + 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 ipmiIp = ia.getHostAddress(); + String guid = UUID.nameUUIDFromBytes(ipmiIp.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 Script2 command = new Script2(scriptPath, s_logger); + command.add("ping"); + command.add("hostname="+ipmiIp); + command.add("usrname="+username); + command.add("password="+password, ParamType.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", ipmiIp, username, "******", result)); + return null; + } + + ClusterVO clu = _clusterDao.findById(clusterId); + if (clu.getGuid() == null) { + clu.setGuid(UUID.randomUUID().toString()); + _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(ApiConstants.PRIVATE_IP, ipmiIp); + params.put(ApiConstants.USERNAME, username); + params.put(ApiConstants.PASSWORD, password); + + String resourceClassName = _configDao.getValue(Config.ExternalBaremetalResourceClassName.key()); + BareMetalResourceBase resource = null; + if (resourceClassName != null) { + Class clazz = Class.forName(resourceClassName); + resource = (BareMetalResourceBase) clazz.newInstance(); + String externalUrl = _configDao.getValue(Config.ExternalBaremetalSystemUrl.key()); + if (externalUrl == null) { + throw new IllegalArgumentException(String.format("You must specify ExternalBaremetalSystemUrl in global config page as ExternalBaremetalResourceClassName is not null")); + } + details.put(BaremetalManager.ExternalBaremetalSystemUrl, externalUrl); + } else { + resource = new BareMetalResourceBase(); + } + resource.configure("Bare Metal Agent", params); + + String memCapacity = (String)params.get(ApiConstants.MEMORY); + String cpuCapacity = (String)params.get(ApiConstants.CPU_SPEED); + String cpuNum = (String)params.get(ApiConstants.CPU_NUMBER); + String mac = (String)params.get(ApiConstants.HOST_MAC); + if (hostTags != null && hostTags.size() != 0) { + details.put("hostTag", hostTags.get(0)); + } + details.put(ApiConstants.MEMORY, memCapacity); + details.put(ApiConstants.CPU_SPEED, cpuCapacity); + details.put(ApiConstants.CPU_NUMBER, cpuNum); + details.put(ApiConstants.HOST_MAC, mac); + details.put(ApiConstants.USERNAME, username); + details.put(ApiConstants.PASSWORD, password); + details.put(ApiConstants.PRIVATE_IP, ipmiIp); + String vmIp = (String)params.get(ApiConstants.IP_ADDRESS); + if (vmIp != null) { + details.put(ApiConstants.IP_ADDRESS, vmIp); + } + String isEchoScAgent = _configDao.getValue(Config.EnableBaremetalSecurityGroupAgentEcho.key()); + details.put(BaremetalManager.EchoSecurityGroupAgent, isEchoScAgent); + + 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)", ipmiIp, username, "******", 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; + } + + @Override + public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) { + // TODO Auto-generated method stub + return null; + } + + @Override + public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map details, + List hostTags) { + StartupCommand firstCmd = startup[0]; + if (!(firstCmd instanceof StartupRoutingCommand)) { + return null; + } + + StartupRoutingCommand ssCmd = ((StartupRoutingCommand) firstCmd); + if (ssCmd.getHypervisorType() != HypervisorType.BareMetal) { + return null; + } + + return _resourceMgr.fillRoutingHostVO(host, ssCmd, HypervisorType.BareMetal, details, hostTags); + } + + @Override + public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException { + if (host.getType() != Host.Type.Routing || host.getHypervisorType() != HypervisorType.BareMetal) { + return null; + } + + List deadVms = _vmDao.listByLastHostId(host.getId()); + for (VMInstanceVO vm : deadVms) { + if (vm.getState() == State.Running || vm.getHostId() != null) { + throw new CloudRuntimeException("VM " + vm.getId() + "is still running on host " + host.getId()); + } + _vmDao.remove(vm.getId()); + } + + return new DeleteHostAnswer(true); + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalGuru.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalGuru.java new file mode 100755 index 00000000000..b8a0af37f43 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalGuru.java @@ -0,0 +1,87 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.manager; + +import java.util.HashMap; +import java.util.Map; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.hypervisor.HypervisorGuru; +import com.cloud.hypervisor.HypervisorGuruBase; +import com.cloud.storage.GuestOSVO; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.utils.component.Inject; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.VMInstanceDao; + +@Local(value=HypervisorGuru.class) +public class BareMetalGuru extends HypervisorGuruBase implements HypervisorGuru { + private static final Logger s_logger = Logger.getLogger(BareMetalGuru.class); + @Inject GuestOSDao _guestOsDao; + @Inject HostDao _hostDao; + @Inject VMInstanceDao _vmDao; + + protected BareMetalGuru() { + super(); + } + + @Override + public HypervisorType getHypervisorType() { + return HypervisorType.BareMetal; + } + + @Override + public VirtualMachineTO implement(VirtualMachineProfile vm) { + VirtualMachineTO to = toVirtualMachineTO(vm); + + VMInstanceVO vo = _vmDao.findById(vm.getId()); + if (vo.getLastHostId() == null) { + to.setBootArgs(BaremetalManager.DO_PXE); + } + + Map details = new HashMap(); + details.put("template", vm.getTemplate().getUrl()); + to.setDetails(details); + + // 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/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java new file mode 100755 index 00000000000..15e63b9f90f --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java @@ -0,0 +1,217 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.manager; + +import java.util.Date; +import java.util.List; + +import javax.ejb.Local; + +import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd; +import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd; +import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd; +import org.apache.log4j.Logger; + +import com.cloud.configuration.Resource.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.resource.ResourceManager; +import com.cloud.storage.VMTemplateHostVO; +import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VMTemplateZoneVO; +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; + @Inject ResourceManager _resourceMgr; + + @Override + public TemplateProfile prepare(RegisterTemplateCmd cmd) throws ResourceAllocationException { + TemplateProfile profile = super.prepare(cmd); + + if (profile.getZoneId() == null || profile.getZoneId() == -1) { + List dcs = _dcDao.listAllIncludingRemoved(); + for (DataCenterVO dc : dcs) { + List pxeServers = _resourceMgr.listAllHostsInOneZoneByType(Host.Type.BaremetalPxe, dc.getId()); + if (pxeServers.size() == 0) { + throw new CloudRuntimeException("Please add PXE server before adding baremetal template in zone " + dc.getName()); + } + } + } else { + List pxeServers = _resourceMgr.listAllHostsInOneZoneByType(Host.Type.BaremetalPxe, 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 = _resourceMgr.listAllHostsInOneZoneByType(Host.Type.BaremetalPxe, 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 = _resourceMgr.listAllHostsInOneZoneByType(Host.Type.BaremetalPxe, 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); + } + + _resourceLimitMgr.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 + _resourceLimitMgr.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/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManager.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManager.java new file mode 100755 index 00000000000..1599050453a --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManager.java @@ -0,0 +1,28 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.manager; + +import com.cloud.network.Network.Provider; +import com.cloud.utils.component.Manager; + +public interface BaremetalManager extends Manager { + public static final String EchoSecurityGroupAgent = "EchoSecurityGroupAgent"; + public static final String ExternalBaremetalSystemUrl = "ExternalBaremetalSystemUrl"; + public static final String DO_PXE = "doPxe"; +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java new file mode 100755 index 00000000000..53888645ade --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java @@ -0,0 +1,112 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.manager; + +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.StopAnswer; +import com.cloud.agent.manager.Commands; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.utils.component.Inject; +import com.cloud.utils.fsm.StateListener; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineGuru; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.VirtualMachineName; +import com.cloud.vm.VirtualMachine.Event; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value = {BaremetalManager.class}) +public class BaremetalManagerImpl implements BaremetalManager, StateListener { + private static final Logger s_logger = Logger.getLogger(BaremetalManagerImpl.class); + + @Inject + protected HostDao _hostDao; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + VirtualMachine.State.getStateMachine().registerListener(this); + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return "Baremetal Manager"; + } + + @Override + public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Object opaque) { + return false; + } + + @Override + public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Object opaque) { + 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/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalDhcpCmd.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalDhcpCmd.java new file mode 100755 index 00000000000..6a26fe27032 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalDhcpCmd.java @@ -0,0 +1,149 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseCmd.CommandType; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.PlugService; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.log4j.Logger; + +import com.cloud.baremetal.database.BaremetalDhcpVO; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.UserContext; + +public class AddBaremetalDhcpCmd extends BaseAsyncCmd { + private static final String s_name = "addexternaldhcpresponse"; + public static final Logger s_logger = Logger.getLogger(AddBaremetalDhcpCmd.class); + + @PlugService BaremetalDhcpManager mgr; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.PHYSICAL_NETWORK_ID, type=CommandType.LONG, required=true, description="the Physical Network ID") + private Long physicalNetworkId; + + @Parameter(name=ApiConstants.POD_ID, type=CommandType.LONG, required = true, description="Pod Id") + private Long podId; + + @Parameter(name=ApiConstants.DHCP_SERVER_TYPE, type=CommandType.STRING, required = true, description="Type of dhcp device") + private String dhcpType; + + @Parameter(name=ApiConstants.URL, type=CommandType.STRING, required = true, description="URL of the external dhcp appliance.") + private String url; + + @Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, required = true, description="Credentials to reach external dhcp device") + private String username; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required = true, description="Credentials to reach external dhcp device") + private String password; + + @Override + public String getEventType() { + return EventTypes.EVENT_BAREMETAL_DHCP_SERVER_ADD; + } + + @Override + public String getEventDescription() { + return "Adding an external DHCP server"; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, + ResourceAllocationException, NetworkRuleConflictException { + try { + BaremetalDhcpVO vo = mgr.addDchpServer(this); + BaremetalDhcpResponse response = mgr.generateApiResponse(vo); + response.setObjectName(s_name); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (Exception e) { + s_logger.warn("Unable to add external dhcp server with url: " + getUrl(), e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return UserContext.current().getCaller().getId(); + } + + public Long getPodId() { + return podId; + } + + public void setPodId(Long podId) { + this.podId = podId; + } + + public String getDhcpType() { + return dhcpType; + } + + public void setDhcpType(String dhcpType) { + this.dhcpType = dhcpType; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + 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; + } + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public void setPhysicalNetworkId(Long physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalKickStartPxeCmd.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalKickStartPxeCmd.java new file mode 100755 index 00000000000..4c3d0b22576 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalKickStartPxeCmd.java @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd.CommandType; +import org.apache.cloudstack.api.Parameter; + +public class AddBaremetalKickStartPxeCmd extends AddBaremetalPxeCmd { + @Parameter(name=ApiConstants.TFTP_DIR, type=CommandType.STRING, required = true, description="Tftp root directory of PXE server") + private String tftpDir; + + public String getTftpDir() { + return tftpDir; + } + + public void setTftpDir(String tftpDir) { + this.tftpDir = tftpDir; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalPxeCmd.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalPxeCmd.java new file mode 100755 index 00000000000..a1d72a328c1 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalPxeCmd.java @@ -0,0 +1,144 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseCmd.CommandType; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.PlugService; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.log4j.Logger; + +import com.cloud.baremetal.database.BaremetalPxeVO; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.UserContext; + +public class AddBaremetalPxeCmd extends BaseAsyncCmd { + private static final String s_name = "addexternalpxeresponse"; + public static final Logger s_logger = Logger.getLogger(AddBaremetalPxeCmd.class); + + @PlugService BaremetalPxeManager pxeMgr; + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.PHYSICAL_NETWORK_ID, type=CommandType.LONG, required=true, description="the Physical Network ID") + private Long physicalNetworkId; + + @Parameter(name=ApiConstants.POD_ID, type=CommandType.LONG, description="Pod Id") + private Long podId; + + @Parameter(name=ApiConstants.URL, type=CommandType.STRING, required = true, description="URL of the external pxe device") + private String url; + + @Parameter(name=ApiConstants.PXE_SERVER_TYPE, type=CommandType.STRING, required = true, description="type of pxe device") + private String deviceType; + + @Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, required = true, description="Credentials to reach external pxe device") + private String username; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required = true, description="Credentials to reach external pxe device") + private String password; + + @Override + public String getEventType() { + return EventTypes.EVENT_BAREMETAL_PXE_SERVER_ADD; + } + + @Override + public String getEventDescription() { + return "Adding an external pxe server"; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, + ResourceAllocationException, NetworkRuleConflictException { + try { + BaremetalPxeVO vo = pxeMgr.addPxeServer(this); + } catch (Exception e) { + s_logger.warn("Unable to add external pxe server with url: " + getUrl(), e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return UserContext.current().getCaller().getId(); + } + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public void setPhysicalNetworkId(Long physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public Long getPodId() { + return podId; + } + + public void setPodId(Long podId) { + this.podId = podId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + 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; + } + + public String getDeviceType() { + return deviceType; + } + + public void setDeviceType(String deviceType) { + this.deviceType = deviceType; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalPxePingServerCmd.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalPxePingServerCmd.java new file mode 100755 index 00000000000..70796f3499f --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalPxePingServerCmd.java @@ -0,0 +1,80 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; + +public class AddBaremetalPxePingServerCmd extends AddBaremetalPxeCmd { + + @Parameter(name=ApiConstants.PING_STORAGE_SERVER_IP, type=CommandType.STRING, required = true, description="PING storage server ip") + private String pingStorageServerIp; + + @Parameter(name=ApiConstants.PING_DIR, type=CommandType.STRING, required = true, description="Root directory on PING storage server") + private String pingDir; + + @Parameter(name=ApiConstants.TFTP_DIR, type=CommandType.STRING, required = true, description="Tftp root directory of PXE server") + private String tftpDir; + + @Parameter(name=ApiConstants.PING_CIFS_USERNAME, type=CommandType.STRING, description="Username of PING storage server") + private String pingStorageServerUserName; + + @Parameter(name=ApiConstants.PING_CIFS_PASSWORD, type=CommandType.STRING, description="Password of PING storage server") + private String pingStorageServerPassword; + + public String getPingStorageServerIp() { + return pingStorageServerIp; + } + + public void setPingStorageServerIp(String pingStorageServerIp) { + this.pingStorageServerIp = pingStorageServerIp; + } + + public String getPingDir() { + return pingDir; + } + + public void setPingDir(String pingDir) { + this.pingDir = pingDir; + } + + public String getTftpDir() { + return tftpDir; + } + + public void setTftpDir(String tftpDir) { + this.tftpDir = tftpDir; + } + + public String getPingStorageServerUserName() { + return pingStorageServerUserName; + } + + public void setPingStorageServerUserName(String pingStorageServerUserName) { + this.pingStorageServerUserName = pingStorageServerUserName; + } + + public String getPingStorageServerPassword() { + return pingStorageServerPassword; + } + + public void setPingStorageServerPassword(String pingStorageServerPassword) { + this.pingStorageServerPassword = pingStorageServerPassword; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalPingServiceImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalPingServiceImpl.java new file mode 100755 index 00000000000..1f3defb9d30 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalPingServiceImpl.java @@ -0,0 +1,300 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +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 org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.baremetal.IpmISetBootDevCommand; +import com.cloud.agent.api.baremetal.IpmISetBootDevCommand.BootDev; +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.database.BaremetalPxeDao; +import com.cloud.baremetal.database.BaremetalPxeVO; +import com.cloud.baremetal.networkservice.BaremetalPxeManager.BaremetalPxeType; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.HostPodVO; +import com.cloud.deploy.DeployDestination; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDetailsDao; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.PhysicalNetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderVO; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ServerResource; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.SearchCriteria2; +import com.cloud.utils.db.SearchCriteriaService; +import com.cloud.utils.db.Transaction; +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=BaremetalPxeService.class) +public class BareMetalPingServiceImpl extends BareMetalPxeServiceBase implements BaremetalPxeService { + private static final Logger s_logger = Logger.getLogger(BareMetalPingServiceImpl.class); + @Inject ResourceManager _resourceMgr; + @Inject PhysicalNetworkDao _physicalNetworkDao; + @Inject PhysicalNetworkServiceProviderDao _physicalNetworkServiceProviderDao; + @Inject HostDetailsDao _hostDetailsDao; + @Inject BaremetalPxeDao _pxeDao; + + + @Override + public boolean prepare(VirtualMachineProfile profile, NicProfile pxeNic, DeployDestination dest, ReservationContext context) { + SearchCriteriaService sc = SearchCriteria2.create(BaremetalPxeVO.class); + sc.addAnd(sc.getEntity().getDeviceType(), Op.EQ, BaremetalPxeType.PING.toString()); + sc.addAnd(sc.getEntity().getPodId(), Op.EQ, dest.getPod().getId()); + BaremetalPxeVO pxeVo = sc.find(); + if (pxeVo == null) { + throw new CloudRuntimeException("No PING PXE server found in pod: " + dest.getPod().getId() + ", you need to add it before starting VM"); + } + long pxeServerId = pxeVo.getHostId(); + + 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); + if (!ans.getResult()) { + s_logger.warn("Unable tot program PXE server: " + pxeVo.getId() + " because " + ans.getDetails()); + return false; + } + + IpmISetBootDevCommand bootCmd = new IpmISetBootDevCommand(BootDev.pxe); + Answer anw = _agentMgr.send(dest.getHost().getId(), bootCmd); + if (!anw.getResult()) { + s_logger.warn("Unable to set host: " + dest.getHost().getId() + " to PXE boot because " + anw.getDetails()); + } + + return anw.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; + } + } + + + @Override + @DB + public BaremetalPxeVO addPxeServer(AddBaremetalPxeCmd cmd) { + AddBaremetalPxePingServerCmd pcmd = (AddBaremetalPxePingServerCmd)cmd; + + PhysicalNetworkVO pNetwork = null; + long zoneId; + + if (cmd.getPhysicalNetworkId() == null || cmd.getUrl() == null || cmd.getUsername() == null || cmd.getPassword() == null) { + throw new IllegalArgumentException("At least one of the required parameters(physical network id, url, username, password) is null"); + } + + pNetwork = _physicalNetworkDao.findById(cmd.getPhysicalNetworkId()); + if (pNetwork == null) { + throw new IllegalArgumentException("Could not find phyical network with ID: " + cmd.getPhysicalNetworkId()); + } + zoneId = pNetwork.getDataCenterId(); + + PhysicalNetworkServiceProviderVO ntwkSvcProvider = _physicalNetworkServiceProviderDao.findByServiceProvider(pNetwork.getId(), BaremetalPxeManager.BAREMETAL_PXE_SERVICE_PROVIDER.getName()); + if (ntwkSvcProvider == null) { + throw new CloudRuntimeException("Network Service Provider: " + BaremetalPxeManager.BAREMETAL_PXE_SERVICE_PROVIDER.getName() + + " is not enabled in the physical network: " + cmd.getPhysicalNetworkId() + "to add this device"); + } else if (ntwkSvcProvider.getState() == PhysicalNetworkServiceProvider.State.Shutdown) { + throw new CloudRuntimeException("Network Service Provider: " + ntwkSvcProvider.getProviderName() + + " is in shutdown state in the physical network: " + cmd.getPhysicalNetworkId() + "to add this device"); + } + + HostPodVO pod = _podDao.findById(cmd.getPodId()); + if (pod == null) { + throw new IllegalArgumentException("Could not find pod with ID: " + cmd.getPodId()); + } + + List pxes = _resourceMgr.listAllUpAndEnabledHosts(Host.Type.BaremetalPxe, null, cmd.getPodId(), zoneId); + if (pxes.size() != 0) { + throw new IllegalArgumentException("Already had a PXE server in Pod: " + cmd.getPodId() + " zone: " + zoneId); + } + + String storageServerIp = pcmd.getPingStorageServerIp(); + if (storageServerIp == null) { + throw new IllegalArgumentException("No IP for storage server specified"); + } + String pingDir = pcmd.getPingDir(); + if (pingDir == null) { + throw new IllegalArgumentException("No direcotry for storage server specified"); + } + String tftpDir = pcmd.getTftpDir(); + if (tftpDir == null) { + throw new IllegalArgumentException("No TFTP directory specified"); + } + + String cifsUsername = pcmd.getPingStorageServerUserName(); + if (cifsUsername == null || cifsUsername.equalsIgnoreCase("")) { + cifsUsername = "xxx"; + } + String cifsPassword = pcmd.getPingStorageServerPassword(); + if (cifsPassword == null || cifsPassword.equalsIgnoreCase("")) { + cifsPassword = "xxx"; + } + + + URI uri; + try { + uri = new URI(cmd.getUrl()); + } catch (Exception e) { + s_logger.debug(e); + throw new IllegalArgumentException(e.getMessage()); + } + String ipAddress = uri.getHost(); + + String guid = getPxeServerGuid(Long.toString(zoneId) + "-" + pod.getId(), BaremetalPxeType.PING.toString(), ipAddress); + + ServerResource resource = null; + Map params = new HashMap(); + params.put(BaremetalPxeService.PXE_PARAM_ZONE, Long.toString(zoneId)); + params.put(BaremetalPxeService.PXE_PARAM_POD, String.valueOf(pod.getId())); + params.put(BaremetalPxeService.PXE_PARAM_IP, ipAddress); + params.put(BaremetalPxeService.PXE_PARAM_USERNAME, cmd.getUsername()); + params.put(BaremetalPxeService.PXE_PARAM_PASSWORD, cmd.getPassword()); + params.put(BaremetalPxeService.PXE_PARAM_PING_STORAGE_SERVER_IP, storageServerIp); + params.put(BaremetalPxeService.PXE_PARAM_PING_ROOT_DIR, pingDir); + params.put(BaremetalPxeService.PXE_PARAM_TFTP_DIR, tftpDir); + params.put(BaremetalPxeService.PXE_PARAM_PING_STORAGE_SERVER_USERNAME, cifsUsername); + params.put(BaremetalPxeService.PXE_PARAM_PING_STORAGE_SERVER_PASSWORD, cifsPassword); + params.put(BaremetalPxeService.PXE_PARAM_GUID, guid); + + resource = new BaremetalPingPxeResource(); + try { + resource.configure("PING PXE resource", params); + } catch (Exception e) { + s_logger.debug(e); + throw new CloudRuntimeException(e.getMessage()); + } + + Host pxeServer = _resourceMgr.addHost(zoneId, resource, Host.Type.BaremetalPxe, params); + if (pxeServer == null) { + throw new CloudRuntimeException("Cannot add PXE server as a host"); + } + + BaremetalPxeVO vo = new BaremetalPxeVO(); + Transaction txn = Transaction.currentTxn(); + vo.setHostId(pxeServer.getId()); + vo.setNetworkServiceProviderId(ntwkSvcProvider.getId()); + vo.setPodId(pod.getId()); + vo.setPhysicalNetworkId(pcmd.getPhysicalNetworkId()); + vo.setDeviceType(BaremetalPxeType.PING.toString()); + txn.start(); + _pxeDao.persist(vo); + txn.commit(); + return vo; + } + + @Override + public BaremetalPxeResponse getApiResponse(BaremetalPxeVO vo) { + BaremetalPxePingResponse response = new BaremetalPxePingResponse(); + response.setId(String.valueOf(vo.getId())); + response.setPhysicalNetworkId(String.valueOf(vo.getPhysicalNetworkId())); + response.setPodId(String.valueOf(vo.getPodId())); + Map details = _hostDetailsDao.findDetails(vo.getHostId()); + response.setPingStorageServerIp(details.get(BaremetalPxeService.PXE_PARAM_PING_STORAGE_SERVER_IP)); + response.setPingDir(details.get(BaremetalPxeService.PXE_PARAM_PING_ROOT_DIR)); + response.setTftpDir(details.get(BaremetalPxeService.PXE_PARAM_TFTP_DIR)); + return response; + } + + + @Override + public List listPxeServers(ListBaremetalPxePingServersCmd cmd) { + SearchCriteriaService sc = SearchCriteria2.create(BaremetalPxeVO.class); + sc.addAnd(sc.getEntity().getDeviceType(), Op.EQ, BaremetalPxeType.PING.toString()); + if (cmd.getPodId() != null) { + sc.addAnd(sc.getEntity().getPodId(), Op.EQ, cmd.getPodId()); + if (cmd.getId() != null) { + sc.addAnd(sc.getEntity().getId(), Op.EQ, cmd.getId()); + } + } + List vos = sc.list(); + List responses = new ArrayList(vos.size()); + for (BaremetalPxeVO vo : vos) { + responses.add(getApiResponse(vo)); + } + return responses; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalPxeServiceBase.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalPxeServiceBase.java new file mode 100644 index 00000000000..85a9b3cff98 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalPxeServiceBase.java @@ -0,0 +1,68 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +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.host.dao.HostDao; +import com.cloud.utils.component.Inject; +import com.cloud.vm.dao.NicDao; + +public abstract class BareMetalPxeServiceBase implements BaremetalPxeService { + protected String _name; + @Inject DataCenterDao _dcDao; + @Inject HostDao _hostDao; + @Inject AgentManager _agentMgr; + @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; + } + + protected String getPxeServerGuid(String zoneId, String name, String ip) { + return zoneId + "-" + name + "-" + ip; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java new file mode 100755 index 00000000000..d615d1d4ac7 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java @@ -0,0 +1,618 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.log4j.Logger; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CheckNetworkAnswer; +import com.cloud.agent.api.CheckNetworkCommand; +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.SecurityGroupRulesCmd; +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.baremetal.manager.BaremetalManager; +import com.cloud.host.Host.Type; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.resource.ServerResource; +import com.cloud.server.ManagementServer; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.OutputInterpreter; +import com.cloud.utils.script.Script; +import com.cloud.utils.script.Script2; +import com.cloud.utils.script.Script2.ParamType; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.dao.VMInstanceDao; + +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +@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 Long hostId; + 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 boolean _isEchoScAgent; + protected IAgentControl _agentControl; + protected Script2 _pingCommand; + protected Script2 _setPxeBootCommand; + protected Script2 _setDiskBootCommand; + protected Script2 _rebootCommand; + protected Script2 _getStatusCommand; + protected Script2 _powerOnCommand; + protected Script2 _powerOffCommand; + protected Script2 _forcePowerOffCommand; + protected Script2 _bootOrRebootCommand; + protected String _vmName; + protected VMInstanceDao vmDao; + + 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"); + hostId = (Long) params.get("hostId"); + _ip = (String) params.get(ApiConstants.PRIVATE_IP); + _mac = (String) params.get(ApiConstants.HOST_MAC); + _username = (String) params.get(ApiConstants.USERNAME); + _password = (String) params.get(ApiConstants.PASSWORD); + _vmName = (String) params.get("vmName"); + String echoScAgent = (String) params.get(BaremetalManager.EchoSecurityGroupAgent); + + 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"); + } + + if (echoScAgent != null) { + _isEchoScAgent = Boolean.valueOf(echoScAgent); + } + + String injectScript = "scripts/util/ipmi.py"; + String scriptPath = Script.findScript("", injectScript); + if (scriptPath == null) { + throw new ConfigurationException("Cannot find ping script " + scriptPath); + } + _pingCommand = new Script2(scriptPath, s_logger); + _pingCommand.add("ping"); + _pingCommand.add("hostname=" + _ip); + _pingCommand.add("usrname=" + _username); + _pingCommand.add("password=" + _password, ParamType.PASSWORD); + + _setPxeBootCommand = new Script2(scriptPath, s_logger); + _setPxeBootCommand.add("boot_dev"); + _setPxeBootCommand.add("hostname=" + _ip); + _setPxeBootCommand.add("usrname=" + _username); + _setPxeBootCommand.add("password=" + _password, ParamType.PASSWORD); + _setPxeBootCommand.add("dev=pxe"); + + _setDiskBootCommand = new Script2(scriptPath, s_logger); + _setDiskBootCommand.add("boot_dev"); + _setDiskBootCommand.add("hostname=" + _ip); + _setDiskBootCommand.add("usrname=" + _username); + _setDiskBootCommand.add("password=" + _password, ParamType.PASSWORD); + _setDiskBootCommand.add("dev=disk"); + + _rebootCommand = new Script2(scriptPath, s_logger); + _rebootCommand.add("reboot"); + _rebootCommand.add("hostname=" + _ip); + _rebootCommand.add("usrname=" + _username); + _rebootCommand.add("password=" + _password, ParamType.PASSWORD); + + _getStatusCommand = new Script2(scriptPath, s_logger); + _getStatusCommand.add("ping"); + _getStatusCommand.add("hostname=" + _ip); + _getStatusCommand.add("usrname=" + _username); + _getStatusCommand.add("password=" + _password, ParamType.PASSWORD); + + _powerOnCommand = new Script2(scriptPath, s_logger); + _powerOnCommand.add("power"); + _powerOnCommand.add("hostname=" + _ip); + _powerOnCommand.add("usrname=" + _username); + _powerOnCommand.add("password=" + _password, ParamType.PASSWORD); + _powerOnCommand.add("action=on"); + + _powerOffCommand = new Script2(scriptPath, s_logger); + _powerOffCommand.add("power"); + _powerOffCommand.add("hostname=" + _ip); + _powerOffCommand.add("usrname=" + _username); + _powerOffCommand.add("password=" + _password, ParamType.PASSWORD); + _powerOffCommand.add("action=soft"); + + _forcePowerOffCommand = new Script2(scriptPath, s_logger); + _forcePowerOffCommand.add("power"); + _forcePowerOffCommand.add("hostname=" + _ip); + _forcePowerOffCommand.add("usrname=" + _username); + _forcePowerOffCommand.add("password=" + _password, ParamType.PASSWORD); + _forcePowerOffCommand.add("action=off"); + + _bootOrRebootCommand = new Script2(scriptPath, s_logger); + _bootOrRebootCommand.add("boot_or_reboot"); + _bootOrRebootCommand.add("hostname=" + _ip); + _bootOrRebootCommand.add("usrname=" + _username); + _bootOrRebootCommand.add("password=" + _password, ParamType.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 states = new HashMap(); + if (hostId != null) { + ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); + vmDao = locator.getDao(VMInstanceDao.class); + final List vms = vmDao.listByHostId(hostId); + for (VMInstanceVO vm : vms) { + states.put(vm.getInstanceName(), vm.getState()); + } + } + /* + * Map changes = new HashMap(); + * + * if (_vmName != null) { State state = getVmState(); if (state != null) + * { changes.put(_vmName, state); } } + */ + + return states; + } + + @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"); + + } + + protected CheckNetworkAnswer execute(CheckNetworkCommand cmd) { + return new CheckNetworkAnswer(cmd, true, "Success"); + } + + protected Answer execute(SecurityGroupRulesCmd cmd) { + SecurityGroupHttpClient hc = new SecurityGroupHttpClient(); + return hc.call(cmd.getGuestIp(), cmd); + } + + @Override + public Answer executeRequest(Command cmd) { + try { + 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 if (cmd instanceof SecurityGroupRulesCmd) { + return execute((SecurityGroupRulesCmd) cmd); + } else if (cmd instanceof CheckNetworkCommand) { + return execute((CheckNetworkCommand) cmd); + } else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } catch (Throwable t) { + s_logger.debug(t.getMessage(), t); + return new Answer(cmd, false, t.getMessage()); + } + } + + 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", false); + } + + return new RebootAnswer(cmd, "reboot succeeded", true); + } + + protected StopAnswer execute(final StopCommand cmd) { + boolean success = false; + int count = 0; + Script powerOff = _powerOffCommand; + + while (count < 10) { + if (!doScript(powerOff)) { + break; + } + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + break; + } + + 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"); + break; + } + + if (!isPowerOn(interpreter.getLines())) { + success = true; + break; + } else { + powerOff = _forcePowerOffCommand; + } + + count++; + } + + return success ? new StopAnswer(cmd, "Success", 0, true) : new StopAnswer(cmd, "IPMI power off failed", false); + } + + protected StartAnswer execute(StartCommand cmd) { + VirtualMachineTO vm = cmd.getVirtualMachine(); + State state = State.Stopped; + + try { + changeVmState(vm.getName(), State.Starting); + + 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 (!doScript(_rebootCommand)) { + return new StartAnswer(cmd, "IPMI reboot failed"); + } + } else { + if (!doScript(_powerOnCommand)) { + return new StartAnswer(cmd, "IPMI power on failed"); + } + } + + if (_isEchoScAgent) { + SecurityGroupHttpClient hc = new SecurityGroupHttpClient(); + boolean echoRet = hc.echo(vm.getNics()[0].getIp(), TimeUnit.MINUTES.toMillis(30), TimeUnit.MINUTES.toMillis(1)); + if (!echoRet) { + return new StartAnswer(cmd, String.format("Call security group agent on vm[%s] timeout", vm.getNics()[0].getIp())); + } + } + + 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(); + /* + * Disable sync until we find a way that only tracks status but not does + * action + * + * The scenario is: Baremetal will reboot host when creating template. + * Given most servers take a long time to boot up, there would be a + * period that mgmt server finds the host is stopped through fullsync. + * Then mgmt server updates database with marking the host as stopped, + * after that, the host comes up and full sync then indicates it's + * running. Because in database the host is already stopped, mgmt server + * sends out a stop command. As a result, creating image gets never + * happened. + * + * 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/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetaNetworkGuru.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetaNetworkGuru.java new file mode 100755 index 00000000000..5414106e275 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetaNetworkGuru.java @@ -0,0 +1,173 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.net.URI; + +import javax.ejb.Local; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.log4j.Logger; + +import com.cloud.dc.DataCenter; +import com.cloud.dc.Pod; +import com.cloud.dc.PodVlanMapVO; +import com.cloud.dc.Vlan; +import com.cloud.dc.Vlan.VlanType; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.PodVlanMapDao; +import com.cloud.dc.dao.VlanDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapcityException; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.IPAddressVO; +import com.cloud.network.Network; +import com.cloud.network.NetworkManager; +import com.cloud.network.Networks.AddressFormat; +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.addr.PublicIp; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.guru.DirectPodBasedNetworkGuru; +import com.cloud.network.guru.NetworkGuru; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.Transaction; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value = { NetworkGuru.class }) +public class BaremetaNetworkGuru extends DirectPodBasedNetworkGuru { + private static final Logger s_logger = Logger.getLogger(BaremetaNetworkGuru.class); + @Inject + private HostDao _hostDao; + @Inject + DataCenterDao _dcDao; + @Inject + VlanDao _vlanDao; + @Inject + NetworkManager _networkMgr; + @Inject + IPAddressDao _ipAddressDao; + @Inject + NetworkOfferingDao _networkOfferingDao; + @Inject + PodVlanMapDao _podVlanDao; + + @Override + public void reserve(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) + throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException { + if (dest.getHost().getHypervisorType() != HypervisorType.BareMetal) { + super.reserve(nic, network, vm, dest, context); + return; + } + + HostVO host = _hostDao.findById(dest.getHost().getId()); + _hostDao.loadDetails(host); + String intentIp = host.getDetail(ApiConstants.IP_ADDRESS); + if (intentIp == null) { + super.reserve(nic, network, vm, dest, context); + return; + } + + String oldIp = nic.getIp4Address(); + boolean getNewIp = false; + if (oldIp == null) { + getNewIp = true; + } else { + // we need to get a new ip address if we try to deploy a vm in a + // different pod + IPAddressVO ipVO = _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), oldIp); + if (ipVO != null) { + PodVlanMapVO mapVO = _podVlanDao.listPodVlanMapsByVlan(ipVO.getVlanId()); + if (mapVO.getPodId() != dest.getPod().getId()) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + + // release the old ip here + _networkMgr.markIpAsUnavailable(ipVO.getId()); + _ipAddressDao.unassignIpAddress(ipVO.getId()); + + txn.commit(); + + nic.setIp4Address(null); + getNewIp = true; + } + } + } + + if (getNewIp) { + // we don't set reservationStrategy to Create because we need this + // method to be called again for the case when vm fails to deploy in + // Pod1, and we try to redeploy it in Pod2 + getBaremetalIp(nic, dest.getPod(), vm, network, intentIp); + } + + DataCenter dc = _dcDao.findById(network.getDataCenterId()); + nic.setDns1(dc.getDns1()); + nic.setDns2(dc.getDns2()); + + /* + * Pod pod = dest.getPod(); Pair ip = + * _dcDao.allocatePrivateIpAddress(dest.getDataCenter().getId(), + * dest.getPod().getId(), nic.getId(), context.getReservationId(), + * intentIp); if (ip == null) { throw new + * InsufficientAddressCapacityException + * ("Unable to get a management ip address", Pod.class, pod.getId()); } + * + * nic.setIp4Address(ip.first()); + * nic.setMacAddress(NetUtils.long2Mac(NetUtils + * .createSequenceBasedMacAddress(ip.second()))); + * nic.setGateway(pod.getGateway()); nic.setFormat(AddressFormat.Ip4); + * String netmask = NetUtils.getCidrNetmask(pod.getCidrSize()); + * nic.setNetmask(netmask); + * nic.setBroadcastType(BroadcastDomainType.Native); + * nic.setBroadcastUri(null); nic.setIsolationUri(null); + */ + + s_logger.debug("Allocated a nic " + nic + " for " + vm); + } + + private void getBaremetalIp(NicProfile nic, Pod pod, VirtualMachineProfile vm, Network network, String requiredIp) + throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException { + DataCenter dc = _dcDao.findById(pod.getDataCenterId()); + if (nic.getIp4Address() == null) { + s_logger.debug(String.format("Requiring ip address: %s", nic.getIp4Address())); + PublicIp ip = _networkMgr.assignPublicIpAddress(dc.getId(), pod.getId(), vm.getOwner(), VlanType.DirectAttached, network.getId(), requiredIp, false); + nic.setIp4Address(ip.getAddress().toString()); + nic.setFormat(AddressFormat.Ip4); + nic.setGateway(ip.getGateway()); + nic.setNetmask(ip.getNetmask()); + if (ip.getVlanTag() != null && ip.getVlanTag().equalsIgnoreCase(Vlan.UNTAGGED)) { + nic.setIsolationUri(URI.create("ec2://" + Vlan.UNTAGGED)); + nic.setBroadcastUri(URI.create("vlan://" + Vlan.UNTAGGED)); + nic.setBroadcastType(BroadcastDomainType.Native); + } + nic.setReservationId(String.valueOf(ip.getVlanTag())); + nic.setMacAddress(ip.getMacAddress()); + } + nic.setDns1(dc.getDns1()); + nic.setDns2(dc.getDns2()); + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java new file mode 100755 index 00000000000..968aa6df3d8 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java @@ -0,0 +1,178 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.baremetal.database.BaremetalDhcpVO; +import com.cloud.baremetal.database.BaremetalPxeVO; +import com.cloud.dc.Pod; +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.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.GuestType; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.element.DhcpServiceProvider; +import com.cloud.network.element.IpDeployer; +import com.cloud.network.element.NetworkElement; +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.SearchCriteria.Op; +import com.cloud.utils.db.SearchCriteria2; +import com.cloud.utils.db.SearchCriteriaService; +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.VirtualMachine.Type; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value = NetworkElement.class) +public class BaremetalDhcpElement extends AdapterBase implements DhcpServiceProvider { + private static final Logger s_logger = Logger.getLogger(BaremetalDhcpElement.class); + private static final Map> capabilities; + + @Inject NicDao _nicDao; + @Inject BaremetalDhcpManager _dhcpMgr; + + static { + Capability cap = new Capability(BaremetalDhcpManager.BAREMETAL_DHCP_SERVICE_CAPABITLITY); + Map baremetalCaps = new HashMap(); + baremetalCaps.put(cap, null); + capabilities = new HashMap>(); + capabilities.put(Service.Dhcp, baremetalCaps); + } + + @Override + public Map> getCapabilities() { + return capabilities; + } + + @Override + public Provider getProvider() { + return BaremetalDhcpManager.BAREMETAL_DHCP_SERVICE_PROVIDER; + } + + private boolean canHandle(DeployDestination dest, TrafficType trafficType, GuestType networkType) { + Pod pod = dest.getPod(); + if (pod != null && dest.getDataCenter().getNetworkType() == NetworkType.Basic && trafficType == TrafficType.Guest) { + SearchCriteriaService sc = SearchCriteria2.create(BaremetalDhcpVO.class); + sc.addAnd(sc.getEntity().getPodId(), Op.EQ, pod.getId()); + return sc.find() != null; + } + + return false; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + if (offering.isSystemOnly() || !canHandle(dest, offering.getTrafficType(), network.getGuestType())) { + s_logger.debug("BaremetalDhcpElement can not handle networkoffering: " + offering.getName()); + return false; + } + 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 (vm.getType() != Type.User || vm.getHypervisorType() != HypervisorType.BareMetal) { + return false; + } + + 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(); + return true; + } + + @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, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean isReady(PhysicalNetworkServiceProvider provider) { + return true; + } + + @Override + public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException { + return true; + } + + @Override + public boolean canEnableIndividualServices() { + return false; + } + + @Override + public boolean verifyServicesCombination(Set services) { + return true; + } + + public boolean addDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, + ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { + if (vm.getHypervisorType() != HypervisorType.BareMetal || !canHandle(dest, network.getTrafficType(), network.getGuestType())) { + return false; + } + return _dhcpMgr.addVirtualMachineIntoNetwork(network, nic, vm, dest, context); + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManager.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManager.java new file mode 100644 index 00000000000..a9c63bfed2b --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManager.java @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.util.List; + +import com.cloud.baremetal.database.BaremetalDhcpVO; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.network.Network; +import com.cloud.network.Network.Provider; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Manager; +import com.cloud.utils.component.PluggableService; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +public interface BaremetalDhcpManager extends Manager, PluggableService { + public static enum BaremetalDhcpType { + DNSMASQ, + DHCPD, + } + + boolean addVirtualMachineIntoNetwork(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException; + + BaremetalDhcpVO addDchpServer(AddBaremetalDhcpCmd cmd); + + BaremetalDhcpResponse generateApiResponse(BaremetalDhcpVO vo); + + List listBaremetalDhcps(ListBaremetalDhcpCmd cmd); + + public static final String BAREMETAL_DHCP_SERVICE_CAPABITLITY = "BaremetalDhcp"; + public static final String BAREMETAL_DHCP_SERVICE_PROPERTIES = "baremetaldhcp_commands.properties"; + public static final Provider BAREMETAL_DHCP_SERVICE_PROVIDER = new Provider("BaremetalDhcpProvider", true); +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManagerImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManagerImpl.java new file mode 100755 index 00000000000..ae8482ca961 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManagerImpl.java @@ -0,0 +1,323 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +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.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupExternalDhcpCommand; +import com.cloud.agent.api.routing.DhcpEntryCommand; +import com.cloud.baremetal.database.BaremetalDhcpDao; +import com.cloud.baremetal.database.BaremetalDhcpVO; +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.ResourceUnavailableException; +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.network.Network; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.PhysicalNetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderVO; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ResourceStateAdapter; +import com.cloud.resource.ServerResource; +import com.cloud.resource.UnableDeleteHostException; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.SearchCriteria2; +import com.cloud.utils.db.SearchCriteriaService; +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.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.UserVmDao; + +@Local(value = { BaremetalDhcpManager.class }) +public class BaremetalDhcpManagerImpl implements BaremetalDhcpManager, ResourceStateAdapter { + private static final org.apache.log4j.Logger s_logger = Logger.getLogger(BaremetalDhcpManagerImpl.class); + protected String _name; + @Inject + DataCenterDao _dcDao; + @Inject + HostDao _hostDao; + @Inject + AgentManager _agentMgr; + @Inject + HostPodDao _podDao; + @Inject + UserVmDao _userVmDao; + @Inject + ResourceManager _resourceMgr; + @Inject + NicDao _nicDao; + @Inject + PhysicalNetworkDao _physicalNetworkDao; + @Inject + PhysicalNetworkServiceProviderDao _physicalNetworkServiceProviderDao; + @Inject + BaremetalDhcpDao _extDhcpDao; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _resourceMgr.registerResourceStateAdapter(this.getClass().getSimpleName(), this); + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + _resourceMgr.unregisterResourceStateAdapter(this.getClass().getSimpleName()); + return true; + } + + @Override + public String getName() { + return _name; + } + + protected String getDhcpServerGuid(String zoneId, String name, String ip) { + return zoneId + "-" + name + "-" + ip; + } + + @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 = _resourceMgr.listAllUpAndEnabledHosts(Type.BaremetalDhcp, 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); + } + } + + @Override + public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) { + // TODO Auto-generated method stub + return null; + } + + @Override + public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map details, + List hostTags) { + if (!(startup[0] instanceof StartupExternalDhcpCommand)) { + return null; + } + + host.setType(Host.Type.BaremetalDhcp); + return host; + } + + @Override + public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException { + // TODO Auto-generated method stub + return null; + } + + + @Override + @DB + public BaremetalDhcpVO addDchpServer(AddBaremetalDhcpCmd cmd) { + PhysicalNetworkVO pNetwork = null; + long zoneId; + + if (cmd.getPhysicalNetworkId() == null || cmd.getUrl() == null || cmd.getUsername() == null || cmd.getPassword() == null) { + throw new IllegalArgumentException("At least one of the required parameters(physical network id, url, username, password) is null"); + } + + pNetwork = _physicalNetworkDao.findById(cmd.getPhysicalNetworkId()); + if (pNetwork == null) { + throw new IllegalArgumentException("Could not find phyical network with ID: " + cmd.getPhysicalNetworkId()); + } + zoneId = pNetwork.getDataCenterId(); + DataCenterVO zone = _dcDao.findById(zoneId); + + PhysicalNetworkServiceProviderVO ntwkSvcProvider = _physicalNetworkServiceProviderDao.findByServiceProvider(pNetwork.getId(), + BaremetalDhcpManager.BAREMETAL_DHCP_SERVICE_PROVIDER.getName()); + if (ntwkSvcProvider == null) { + throw new CloudRuntimeException("Network Service Provider: " + BaremetalDhcpManager.BAREMETAL_DHCP_SERVICE_PROVIDER.getName() + " is not enabled in the physical network: " + + cmd.getPhysicalNetworkId() + "to add this device"); + } else if (ntwkSvcProvider.getState() == PhysicalNetworkServiceProvider.State.Shutdown) { + throw new CloudRuntimeException("Network Service Provider: " + ntwkSvcProvider.getProviderName() + + " is in shutdown state in the physical network: " + cmd.getPhysicalNetworkId() + "to add this device"); + } + + HostPodVO pod = _podDao.findById(cmd.getPodId()); + if (pod == null) { + throw new IllegalArgumentException("Could not find pod with ID: " + cmd.getPodId()); + } + + List dhcps = _resourceMgr.listAllUpAndEnabledHosts(Host.Type.BaremetalDhcp, null, cmd.getPodId(), zoneId); + if (dhcps.size() != 0) { + throw new IllegalArgumentException("Already had a DHCP server in Pod: " + cmd.getPodId() + " zone: " + zoneId); + } + + URI uri; + try { + uri = new URI(cmd.getUrl()); + } catch (Exception e) { + s_logger.debug(e); + throw new IllegalArgumentException(e.getMessage()); + } + + String ipAddress = uri.getHost(); + String guid = getDhcpServerGuid(Long.toString(zoneId) + "-" + Long.toString(cmd.getPodId()), "ExternalDhcp", ipAddress); + Map params = new HashMap(); + params.put("type", cmd.getDhcpType()); + params.put("zone", Long.toString(zoneId)); + params.put("pod", cmd.getPodId().toString()); + params.put("ip", ipAddress); + params.put("username", cmd.getUsername()); + params.put("password", cmd.getPassword()); + params.put("guid", guid); + params.put("gateway", pod.getGateway()); + String dns = zone.getDns1(); + if (dns == null) { + dns = zone.getDns2(); + } + params.put("dns", dns); + + ServerResource resource = null; + try { + if (cmd.getDhcpType().equalsIgnoreCase(BaremetalDhcpType.DNSMASQ.toString())) { + resource = new BaremetalDnsmasqResource(); + resource.configure("Dnsmasq resource", params); + } else if (cmd.getDhcpType().equalsIgnoreCase(BaremetalDhcpType.DHCPD.toString())) { + resource = new BaremetalDhcpdResource(); + resource.configure("Dhcpd resource", params); + } else { + throw new CloudRuntimeException("Unsupport DHCP server type: " + cmd.getDhcpType()); + } + } catch (Exception e) { + s_logger.debug(e); + throw new CloudRuntimeException(e.getMessage()); + } + + Host dhcpServer = _resourceMgr.addHost(zoneId, resource, Host.Type.BaremetalDhcp, params); + if (dhcpServer == null) { + throw new CloudRuntimeException("Cannot add external Dhcp server as a host"); + } + + BaremetalDhcpVO vo = new BaremetalDhcpVO(); + vo.setDeviceType(cmd.getDhcpType()); + vo.setHostId(dhcpServer.getId()); + vo.setNetworkServiceProviderId(ntwkSvcProvider.getId()); + vo.setPhysicalNetworkId(cmd.getPhysicalNetworkId()); + vo.setPodId(cmd.getPodId()); + Transaction txn = Transaction.currentTxn(); + txn.start(); + _extDhcpDao.persist(vo); + txn.commit(); + return vo; + } + + @Override + public BaremetalDhcpResponse generateApiResponse(BaremetalDhcpVO vo) { + BaremetalDhcpResponse response = new BaremetalDhcpResponse(); + response.setDeviceType(vo.getDeviceType()); + response.setId(String.valueOf(vo.getId())); + response.setPhysicalNetworkId(String.valueOf(vo.getPhysicalNetworkId())); + response.setProviderId(String.valueOf(vo.getNetworkServiceProviderId())); + return response; + } + + @Override + public List listBaremetalDhcps(ListBaremetalDhcpCmd cmd) { + SearchCriteriaService sc = SearchCriteria2.create(BaremetalDhcpVO.class); + if (cmd.getDeviceType() != null) { + sc.addAnd(sc.getEntity().getDeviceType(), Op.EQ, cmd.getDeviceType()); + } + if (cmd.getPodId() != null) { + sc.addAnd(sc.getEntity().getPodId(), Op.EQ, cmd.getPodId()); + if (cmd.getId() != null) { + sc.addAnd(sc.getEntity().getId(), Op.EQ, cmd.getId()); + } + } + List vos = sc.list(); + List responses = new ArrayList(vos.size()); + for (BaremetalDhcpVO vo : vos) { + responses.add(generateApiResponse(vo)); + } + return responses; + } + + @Override + public List> getCommands() { + return null; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpResourceBase.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpResourceBase.java new file mode 100644 index 00000000000..054141580e9 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpResourceBase.java @@ -0,0 +1,174 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +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 BaremetalDhcpResourceBase implements ServerResource { + private static final Logger s_logger = Logger.getLogger(BaremetalDhcpResourceBase.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.BaremetalDhcp; + } + + @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/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpResponse.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpResponse.java new file mode 100755 index 00000000000..952ac41a701 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpResponse.java @@ -0,0 +1,71 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class BaremetalDhcpResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) @Param(description="device id of ") + private String id; + + @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) @Param(description="the physical network to which this external dhcp device belongs to") + private String physicalNetworkId; + + @SerializedName(ApiConstants.PROVIDER) @Param(description="name of the provider") + private String providerId; + + @SerializedName(ApiConstants.DHCP_SERVER_TYPE) @Param(description="name of the provider") + private String deviceType; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getPhysicalNetworkId() { + return physicalNetworkId; + } + + public void setPhysicalNetworkId(String physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public String getProviderId() { + return providerId; + } + + public void setProviderId(String providerId) { + this.providerId = providerId; + } + + public String getDeviceType() { + return deviceType; + } + + public void setDeviceType(String deviceType) { + this.deviceType = deviceType; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpdResource.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpdResource.java new file mode 100755 index 00000000000..a27a6f26896 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpdResource.java @@ -0,0 +1,139 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +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 BaremetalDhcpdResource extends BaremetalDhcpResourceBase { + private static final Logger s_logger = Logger.getLogger(BaremetalDhcpdResource.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, "******")); + 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, "******")); + } + + 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/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDnsmasqResource.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDnsmasqResource.java new file mode 100644 index 00000000000..6841c525107 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDnsmasqResource.java @@ -0,0 +1,129 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +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 BaremetalDnsmasqResource extends BaremetalDhcpResourceBase { + private static final Logger s_logger = Logger.getLogger(BaremetalDnsmasqResource.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/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartPxeResource.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartPxeResource.java new file mode 100755 index 00000000000..938b3ac1d46 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartPxeResource.java @@ -0,0 +1,201 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.apache.commons.lang.StringUtils; +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.VmDataCommand; +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 BaremetalKickStartPxeResource extends BaremetalPxeResourceBase { + private static final Logger s_logger = Logger.getLogger(BaremetalKickStartPxeResource.class); + private static final String _name = "BaremetalKickStartPxeResource"; + String _tftpDir; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + super.configure(name, params); + _tftpDir = (String) params.get(BaremetalPxeService.PXE_PARAM_TFTP_DIR); + if (_tftpDir == null) { + throw new ConfigurationException("No tftp directory specified"); + } + + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); + + s_logger.debug(String.format("Trying to connect to kickstart PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, "******")); + 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 kickstart PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, + "******")); + } + + String cmd = String.format("[ -f /%1$s/pxelinux.0 ]", _tftpDir); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, cmd)) { + throw new ConfigurationException("Miss files in TFTP directory at " + _tftpDir + " check if pxelinux.0 are here"); + } + + SCPClient scp = new SCPClient(sshConnection); + String prepareScript = "scripts/network/ping/prepare_kickstart_bootfile.py"; + String prepareScriptPath = Script.findScript("", prepareScript); + if (prepareScriptPath == null) { + throw new ConfigurationException("Can not find prepare_kickstart_bootfile.py at " + prepareScriptPath); + } + scp.put(prepareScriptPath, "/usr/bin/", "0755"); + + String cpScript = "scripts/network/ping/prepare_kickstart_kernel_initrd.py"; + String cpScriptPath = Script.findScript("", cpScript); + if (cpScriptPath == null) { + throw new ConfigurationException("Can not find prepare_kickstart_kernel_initrd.py at " + cpScriptPath); + } + scp.put(cpScriptPath, "/usr/bin/", "0755"); + + String userDataScript = "scripts/network/ping/baremetal_user_data.py"; + String userDataScriptPath = Script.findScript("", userDataScript); + if (userDataScriptPath == null) { + throw new ConfigurationException("Can not find baremetal_user_data.py at " + userDataScriptPath); + } + scp.put(userDataScriptPath, "/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()); + } + } + + private Answer execute(VmDataCommand cmd) { + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); + try { + List vmData = cmd.getVmData(); + StringBuilder sb = new StringBuilder(); + for (String[] data : vmData) { + String folder = data[0]; + String file = data[1]; + String contents = (data[2] == null) ? "none" : data[2]; + sb.append(cmd.getVmIpAddress()); + sb.append(","); + sb.append(folder); + sb.append(","); + sb.append(file); + sb.append(","); + sb.append(contents); + sb.append(";"); + } + String arg = StringUtils.stripEnd(sb.toString(), ";"); + + 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/baremetal_user_data.py '%s'", arg); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { + return new Answer(cmd, false, "Failed to add user data, 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 PrepareKickstartPxeServerCommand) { + return execute((PrepareKickstartPxeServerCommand) cmd); + } else if (cmd instanceof VmDataCommand) { + return execute((VmDataCommand)cmd); + } else { + return super.executeRequest(cmd); + } + } + + private Answer execute(PrepareKickstartPxeServerCommand 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 copyTo = String.format("%s/%s", _tftpDir, cmd.getTemplateUuid()); + String script = String.format("python /usr/bin/prepare_kickstart_kernel_initrd.py %s %s", cmd.getRepo(), copyTo); + + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { + return new Answer(cmd, false, "prepare kickstart at pxe server " + _ip + " failed, command:" + script); + } + + String kernelPath = String.format("%s/vmlinuz", cmd.getTemplateUuid()); + String initrdPath = String.format("%s/initrd.img", cmd.getTemplateUuid()); + script = String.format("python /usr/bin/prepare_kickstart_bootfile.py %s %s %s %s %s %s", _tftpDir, cmd.getMac(), kernelPath, initrdPath, cmd.getKsFile(), cmd.getMac()); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { + return new Answer(cmd, false, "prepare kickstart at pxe server " + _ip + " failed, command:" + script); + } + + s_logger.debug("Prepare kickstart PXE server successfully"); + return new Answer(cmd, true, "Success"); + } catch (Exception e){ + s_logger.debug("Prepare for kickstart server failed", e); + return new Answer(cmd, false, e.getMessage()); + } finally { + if (sshConnection != null) { + sshConnection.close(); + } + } + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java new file mode 100755 index 00000000000..4a2369b1ea4 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java @@ -0,0 +1,238 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +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 org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.baremetal.IpmISetBootDevCommand; +import com.cloud.agent.api.baremetal.IpmISetBootDevCommand.BootDev; +import com.cloud.baremetal.database.BaremetalPxeDao; +import com.cloud.baremetal.database.BaremetalPxeVO; +import com.cloud.baremetal.networkservice.BaremetalPxeManager.BaremetalPxeType; +import com.cloud.deploy.DeployDestination; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDetailsDao; +import com.cloud.network.NetworkVO; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.PhysicalNetworkVO; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderVO; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ServerResource; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.SearchCriteria2; +import com.cloud.utils.db.SearchCriteriaService; +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.VirtualMachineProfile; + +@Local(value = BaremetalPxeService.class) +public class BaremetalKickStartServiceImpl extends BareMetalPxeServiceBase implements BaremetalPxeService { + private static final Logger s_logger = Logger.getLogger(BaremetalKickStartServiceImpl.class); + @Inject + ResourceManager _resourceMgr; + @Inject + PhysicalNetworkDao _physicalNetworkDao; + @Inject + PhysicalNetworkServiceProviderDao _physicalNetworkServiceProviderDao; + @Inject + HostDetailsDao _hostDetailsDao; + @Inject + BaremetalPxeDao _pxeDao; + @Inject + NetworkDao _nwDao; + @Inject + VMTemplateDao _tmpDao; + + @Override + public boolean prepare(VirtualMachineProfile profile, NicProfile nic, DeployDestination dest, ReservationContext context) { + NetworkVO nwVO = _nwDao.findById(nic.getNetworkId()); + SearchCriteriaService sc = SearchCriteria2.create(BaremetalPxeVO.class); + sc.addAnd(sc.getEntity().getDeviceType(), Op.EQ, BaremetalPxeType.KICK_START.toString()); + sc.addAnd(sc.getEntity().getPhysicalNetworkId(), Op.EQ, nwVO.getPhysicalNetworkId()); + BaremetalPxeVO pxeVo = sc.find(); + if (pxeVo == null) { + throw new CloudRuntimeException("No kickstart PXE server found in pod: " + dest.getPod().getId() + ", you need to add it before starting VM"); + } + VMTemplateVO template = _tmpDao.findById(profile.getTemplateId()); + + try { + String tpl = profile.getTemplate().getUrl(); + assert tpl != null : "How can a null template get here!!!"; + String[] tpls = tpl.split(";"); + assert tpls.length == 2 : "Template is not correctly encoded. " + tpl; + PrepareKickstartPxeServerCommand cmd = new PrepareKickstartPxeServerCommand(); + cmd.setKsFile(tpls[0]); + cmd.setRepo(tpls[1]); + cmd.setMac(nic.getMacAddress()); + cmd.setTemplateUuid(template.getUuid()); + Answer aws = _agentMgr.send(pxeVo.getHostId(), cmd); + if (!aws.getResult()) { + s_logger.warn("Unable to set host: " + dest.getHost().getId() + " to PXE boot because " + aws.getDetails()); + return aws.getResult(); + } + + IpmISetBootDevCommand bootCmd = new IpmISetBootDevCommand(BootDev.pxe); + aws = _agentMgr.send(dest.getHost().getId(), bootCmd); + if (!aws.getResult()) { + s_logger.warn("Unable to set host: " + dest.getHost().getId() + " to PXE boot because " + aws.getDetails()); + } + + return aws.getResult(); + } catch (Exception e) { + s_logger.warn("Cannot prepare PXE server", e); + return false; + } + } + + @Override + public boolean prepareCreateTemplate(Long pxeServerId, UserVm vm, String templateUrl) { + // TODO Auto-generated method stub + return false; + } + + @Override + @DB + public BaremetalPxeVO addPxeServer(AddBaremetalPxeCmd cmd) { + AddBaremetalKickStartPxeCmd kcmd = (AddBaremetalKickStartPxeCmd)cmd; + PhysicalNetworkVO pNetwork = null; + long zoneId; + + if (cmd.getPhysicalNetworkId() == null || cmd.getUrl() == null || cmd.getUsername() == null || cmd.getPassword() == null) { + throw new IllegalArgumentException("At least one of the required parameters(physical network id, url, username, password) is null"); + } + + pNetwork = _physicalNetworkDao.findById(cmd.getPhysicalNetworkId()); + if (pNetwork == null) { + throw new IllegalArgumentException("Could not find phyical network with ID: " + cmd.getPhysicalNetworkId()); + } + zoneId = pNetwork.getDataCenterId(); + + PhysicalNetworkServiceProviderVO ntwkSvcProvider = _physicalNetworkServiceProviderDao.findByServiceProvider(pNetwork.getId(), BaremetalPxeManager.BAREMETAL_PXE_SERVICE_PROVIDER.getName()); + if (ntwkSvcProvider == null) { + throw new CloudRuntimeException("Network Service Provider: " + BaremetalPxeManager.BAREMETAL_PXE_SERVICE_PROVIDER.getName() + + " is not enabled in the physical network: " + cmd.getPhysicalNetworkId() + "to add this device"); + } else if (ntwkSvcProvider.getState() == PhysicalNetworkServiceProvider.State.Shutdown) { + throw new CloudRuntimeException("Network Service Provider: " + ntwkSvcProvider.getProviderName() + + " is in shutdown state in the physical network: " + cmd.getPhysicalNetworkId() + "to add this device"); + } + + List pxes = _resourceMgr.listAllHostsInOneZoneByType(Host.Type.BaremetalPxe, zoneId); + if (!pxes.isEmpty()) { + throw new IllegalArgumentException("Already had a PXE server zone: " + zoneId); + } + + String tftpDir = kcmd.getTftpDir(); + if (tftpDir == null) { + throw new IllegalArgumentException("No TFTP directory specified"); + } + + URI uri; + try { + uri = new URI(cmd.getUrl()); + } catch (Exception e) { + s_logger.debug(e); + throw new IllegalArgumentException(e.getMessage()); + } + String ipAddress = uri.getHost(); + + String guid = getPxeServerGuid(Long.toString(zoneId), BaremetalPxeType.KICK_START.toString(), ipAddress); + + ServerResource resource = null; + Map params = new HashMap(); + params.put(BaremetalPxeService.PXE_PARAM_ZONE, Long.toString(zoneId)); + params.put(BaremetalPxeService.PXE_PARAM_IP, ipAddress); + params.put(BaremetalPxeService.PXE_PARAM_USERNAME, cmd.getUsername()); + params.put(BaremetalPxeService.PXE_PARAM_PASSWORD, cmd.getPassword()); + params.put(BaremetalPxeService.PXE_PARAM_TFTP_DIR, tftpDir); + params.put(BaremetalPxeService.PXE_PARAM_GUID, guid); + resource = new BaremetalKickStartPxeResource(); + try { + resource.configure("KickStart PXE resource", params); + } catch (Exception e) { + throw new CloudRuntimeException(e.getMessage(), e); + } + + Host pxeServer = _resourceMgr.addHost(zoneId, resource, Host.Type.BaremetalPxe, params); + if (pxeServer == null) { + throw new CloudRuntimeException("Cannot add PXE server as a host"); + } + + BaremetalPxeVO vo = new BaremetalPxeVO(); + Transaction txn = Transaction.currentTxn(); + vo.setHostId(pxeServer.getId()); + vo.setNetworkServiceProviderId(ntwkSvcProvider.getId()); + vo.setPhysicalNetworkId(kcmd.getPhysicalNetworkId()); + vo.setDeviceType(BaremetalPxeType.KICK_START.toString()); + txn.start(); + _pxeDao.persist(vo); + txn.commit(); + return vo; + } + + @Override + public BaremetalPxeResponse getApiResponse(BaremetalPxeVO vo) { + BaremetalPxeKickStartResponse response = new BaremetalPxeKickStartResponse(); + response.setId(String.valueOf(vo.getId())); + response.setPhysicalNetworkId(String.valueOf(vo.getPhysicalNetworkId())); + response.setPodId(String.valueOf(vo.getPodId())); + Map details = _hostDetailsDao.findDetails(vo.getHostId()); + response.setTftpDir(details.get(BaremetalPxeService.PXE_PARAM_TFTP_DIR)); + return response; + } + + @Override + public List listPxeServers(ListBaremetalPxePingServersCmd cmd) { + SearchCriteriaService sc = SearchCriteria2.create(BaremetalPxeVO.class); + sc.addAnd(sc.getEntity().getDeviceType(), Op.EQ, BaremetalPxeType.KICK_START.toString()); + if (cmd.getPodId() != null) { + sc.addAnd(sc.getEntity().getPodId(), Op.EQ, cmd.getPodId()); + if (cmd.getId() != null) { + sc.addAnd(sc.getEntity().getId(), Op.EQ, cmd.getId()); + } + } + List vos = sc.list(); + List responses = new ArrayList(vos.size()); + for (BaremetalPxeVO vo : vos) { + responses.add(getApiResponse(vo)); + } + return responses; + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPingPxeResource.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPingPxeResource.java new file mode 100755 index 00000000000..2fb54154489 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPingPxeResource.java @@ -0,0 +1,260 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.util.HashMap; +import java.util.List; +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.agent.api.routing.VmDataCommand; +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 BaremetalPingPxeResource extends BaremetalPxeResourceBase { + private static final Logger s_logger = Logger.getLogger(BaremetalPingPxeResource.class); + private static final String _name = "BaremetalPingPxeResource"; + 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(BaremetalPxeService.PXE_PARAM_PING_STORAGE_SERVER_IP); + _pingDir = (String)params.get(BaremetalPxeService.PXE_PARAM_PING_ROOT_DIR); + _tftpDir = (String)params.get(BaremetalPxeService.PXE_PARAM_TFTP_DIR); + _cifsUserName = (String)params.get(BaremetalPxeService.PXE_PARAM_PING_STORAGE_SERVER_USERNAME); + _cifsPassword = (String)params.get(BaremetalPxeService.PXE_PARAM_PING_STORAGE_SERVER_PASSWORD); + + if (_podId == null) { + throw new ConfigurationException("No Pod specified"); + } + + 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, "******")); + 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, + "******")); + } + + 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"); + + String userDataScript = "scripts/network/ping/baremetal_user_data.py"; + String userDataScriptPath = Script.findScript("", userDataScript); + if (userDataScriptPath == null) { + throw new ConfigurationException("Can not find baremetal_user_data.py at " + userDataScriptPath); + } + scp.put(userDataScriptPath, "/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()); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { + return new PreparePxeServerAnswer(cmd, "prepare PING at " + _ip + " failed, command:" + script); + } + s_logger.debug("Prepare Ping PXE server successfully"); + + 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()); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { + return new Answer(cmd, false, "prepare for creating template failed, command:" + script); + } + s_logger.debug("Prepare for creating template successfully"); + + 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 if (cmd instanceof VmDataCommand) { + return execute((VmDataCommand)cmd); + } else { + return super.executeRequest(cmd); + } + } + + private Answer execute(VmDataCommand cmd) { + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); + try { + List vmData = cmd.getVmData(); + StringBuilder sb = new StringBuilder(); + for (String[] data : vmData) { + String folder = data[0]; + String file = data[1]; + String contents = (data[2] == null) ? "none" : data[2]; + sb.append(cmd.getVmIpAddress()); + sb.append(","); + sb.append(folder); + sb.append(","); + sb.append(file); + sb.append(","); + sb.append(contents); + sb.append(";"); + } + String arg = org.apache.commons.lang.StringUtils.stripEnd(sb.toString(), ";"); + + 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/baremetal_user_data.py '%s'", arg); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { + return new Answer(cmd, false, "Failed to add user data, 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(); + } + } + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java new file mode 100755 index 00000000000..99b9c432072 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java @@ -0,0 +1,178 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.baremetal.database.BaremetalPxeVO; +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.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.GuestType; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.element.NetworkElement; +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.SearchCriteria.Op; +import com.cloud.utils.db.SearchCriteria2; +import com.cloud.utils.db.SearchCriteriaService; +import com.cloud.utils.db.Transaction; +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.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.VMInstanceDao; + +@Local(value = NetworkElement.class) +public class BaremetalPxeElement extends AdapterBase implements NetworkElement { + private static final Logger s_logger = Logger.getLogger(BaremetalPxeElement.class); + private static final Map> capabilities; + + @Inject BaremetalPxeManager _pxeMgr;; + @Inject VMInstanceDao _vmDao; + @Inject NicDao _nicDao; + + static { + Capability cap = new Capability(BaremetalPxeManager.BAREMETAL_PXE_CAPABILITY); + Map baremetalCaps = new HashMap(); + baremetalCaps.put(cap, null); + capabilities = new HashMap>(); + capabilities.put(BaremetalPxeManager.BAREMETAL_PXE_SERVICE, baremetalCaps); + } + + @Override + public Map> getCapabilities() { + return capabilities; + } + + @Override + public Provider getProvider() { + return BaremetalPxeManager.BAREMETAL_PXE_SERVICE_PROVIDER; + } + + private boolean canHandle(DeployDestination dest, TrafficType trafficType, GuestType networkType) { + Pod pod = dest.getPod(); + if (pod != null && dest.getDataCenter().getNetworkType() == NetworkType.Basic && trafficType == TrafficType.Guest) { + SearchCriteriaService sc = SearchCriteria2.create(BaremetalPxeVO.class); + sc.addAnd(sc.getEntity().getPodId(), Op.EQ, pod.getId()); + return sc.find() != null; + } + + return false; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + if (offering.isSystemOnly() || !canHandle(dest, offering.getTrafficType(), network.getGuestType())) { + s_logger.debug("BaremetalPxeElement can not handle network offering: " + offering.getName()); + return false; + } + return true; + } + + @Override + @DB + public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, + ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + if (vm.getType() != Type.User || vm.getHypervisorType() != HypervisorType.BareMetal) { + return false; + } + + VMInstanceVO vo = _vmDao.findById(vm.getId()); + if (vo.getLastHostId() == null) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + nic.setMacAddress(dest.getHost().getPrivateMacAddress()); + NicVO nicVo = _nicDao.findById(nic.getId()); + assert vo != null : "Where ths nic " + nic.getId() + " going???"; + nicVo.setMacAddress(nic.getMacAddress()); + _nicDao.update(nicVo.getId(), nicVo); + txn.commit(); + + /*This vm is just being created */ + if (!_pxeMgr.prepare(vm, nic, dest, context)) { + throw new CloudRuntimeException("Cannot prepare pxe server"); + } + } + + return false; + } + + @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, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean isReady(PhysicalNetworkServiceProvider provider) { + return true; + } + + @Override + public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException { + return true; + } + + @Override + public boolean canEnableIndividualServices() { + return false; + } + + @Override + public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean verifyServicesCombination(Set services) { + // TODO Auto-generated method stub + return false; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeKickStartResponse.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeKickStartResponse.java new file mode 100755 index 00000000000..09c6cc6769d --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeKickStartResponse.java @@ -0,0 +1,37 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class BaremetalPxeKickStartResponse extends BaremetalPxeResponse { + @SerializedName(ApiConstants.TFTP_DIR) @Param(description="Tftp root directory of PXE server") + private String tftpDir; + + public String getTftpDir() { + return tftpDir; + } + + public void setTftpDir(String tftpDir) { + this.tftpDir = tftpDir; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManager.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManager.java new file mode 100755 index 00000000000..e0a51627fc4 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManager.java @@ -0,0 +1,65 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.util.List; + +import com.cloud.baremetal.database.BaremetalPxeVO; +import com.cloud.deploy.DeployDestination; +import com.cloud.host.HostVO; +import com.cloud.network.Network; +import com.cloud.network.Network.Provider; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Manager; +import com.cloud.utils.component.PluggableService; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachineProfile; + +public interface BaremetalPxeManager extends Manager, PluggableService { + public enum BaremetalPxeType { + PING, + KICK_START, + } + + boolean prepare(VirtualMachineProfile profile, NicProfile nic, DeployDestination dest, ReservationContext context); + + boolean prepareCreateTemplate(Long pxeServerId, UserVm vm, String templateUrl); + + BaremetalPxeType getPxeServerType(HostVO host); + + BaremetalPxeVO addPxeServer(AddBaremetalPxeCmd cmd); + + BaremetalPxeResponse getApiResponse(BaremetalPxeVO vo); + + List listPxeServers(ListBaremetalPxePingServersCmd cmd); + + boolean addUserData(NicProfile nic, VirtualMachineProfile vm); + + public static final Network.Service BAREMETAL_PXE_SERVICE = new Network.Service("BaremetalPxeService"); + public static final String BAREMETAL_PXE_CAPABILITY = "BaremetalPxe"; + public static final String BAREMETAL_PXE_SERVICE_PROPERTIES = "baremetalpxe_commands.properties"; + public static final Provider BAREMETAL_PXE_SERVICE_PROVIDER = new Provider("BaremetalPxeProvider", true);; + public static final Provider BAREMETAL_USERDATA_PROVIDER = new Provider("BaremetaUserdataProvider", true); +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManagerImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManagerImpl.java new file mode 100755 index 00000000000..94010ece567 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManagerImpl.java @@ -0,0 +1,242 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + + +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.api.Answer; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupPxeServerCommand; +import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.baremetal.database.BaremetalPxeVO; +import com.cloud.configuration.dao.ConfigurationDao; +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.network.PhysicalNetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ResourceStateAdapter; +import com.cloud.resource.ServerResource; +import com.cloud.resource.UnableDeleteHostException; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.uservm.UserVm; +import com.cloud.utils.StringUtils; +import com.cloud.utils.component.Adapters; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.SearchCriteria2; +import com.cloud.utils.db.SearchCriteriaService; +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; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.UserVmDao; + +@Local(value = {BaremetalPxeManager.class}) +public class BaremetalPxeManagerImpl implements BaremetalPxeManager, ResourceStateAdapter { + private static final org.apache.log4j.Logger s_logger = Logger.getLogger(BaremetalPxeManagerImpl.class); + protected String _name; + @Inject DataCenterDao _dcDao; + @Inject HostDao _hostDao; + @Inject AgentManager _agentMgr; + @Inject ResourceManager _resourceMgr; + @Inject(adapter=BaremetalPxeService.class) + protected Adapters _services; + @Inject UserVmDao _vmDao; + @Inject ServiceOfferingDao _serviceOfferingDao; + @Inject NicDao _nicDao; + @Inject ConfigurationDao _configDao; + @Inject PhysicalNetworkDao _phynwDao; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + _resourceMgr.registerResourceStateAdapter(this.getClass().getSimpleName(), this); + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + _resourceMgr.unregisterResourceStateAdapter(this.getClass().getSimpleName()); + return true; + } + + @Override + public String getName() { + return _name; + } + + protected BaremetalPxeService getServiceByType(String type) { + BaremetalPxeService _service; + _service = _services.get(type); + if (_service == null) { + throw new CloudRuntimeException("Cannot find PXE service for " + type); + } + return _service; + } + + @Override + public boolean prepare(VirtualMachineProfile profile, NicProfile nic, DeployDestination dest, ReservationContext context) { + //TODO: select type from template + BaremetalPxeType type = BaremetalPxeType.KICK_START; + return getServiceByType(type.toString()).prepare(profile, nic, dest, context); + } + + @Override + public boolean prepareCreateTemplate(Long pxeServerId, UserVm vm, String templateUrl) { + //TODO: select type from template + BaremetalPxeType type = BaremetalPxeType.PING; + return getServiceByType(type.toString()).prepareCreateTemplate(pxeServerId, vm, templateUrl); + } + + @Override + public BaremetalPxeType getPxeServerType(HostVO host) { + if (host.getResource().equalsIgnoreCase(BaremetalPingPxeResource.class.getName())) { + return BaremetalPxeType.PING; + } else { + throw new CloudRuntimeException("Unkown PXE server resource " + host.getResource()); + } + } + + @Override + public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) { + // TODO Auto-generated method stub + return null; + } + + @Override + public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map details, + List hostTags) { + if (!(startup[0] instanceof StartupPxeServerCommand)) { + return null; + } + + host.setType(Host.Type.BaremetalPxe); + return host; + } + + @Override + public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException { + // TODO Auto-generated method stub + return null; + } + + @Override + public BaremetalPxeVO addPxeServer(AddBaremetalPxeCmd cmd) { + return getServiceByType(cmd.getDeviceType()).addPxeServer(cmd); + } + + @Override + public BaremetalPxeResponse getApiResponse(BaremetalPxeVO vo) { + return getServiceByType(vo.getDeviceType()).getApiResponse(vo); + } + + @Override + public List listPxeServers(ListBaremetalPxePingServersCmd cmd) { + return getServiceByType(BaremetalPxeManager.BaremetalPxeType.PING.toString()).listPxeServers(cmd); + } + + @Override + public boolean addUserData(NicProfile nic, VirtualMachineProfile profile) { + UserVmVO vm = (UserVmVO) profile.getVirtualMachine(); + _vmDao.loadDetails(vm); + + String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId()).getDisplayText(); + String zoneName = _dcDao.findById(vm.getDataCenterIdToDeployIn()).getName(); + NicVO nvo = _nicDao.findById(nic.getId()); + VmDataCommand cmd = new VmDataCommand(nvo.getIp4Address(), vm.getInstanceName()); + cmd.addVmData("userdata", "user-data", vm.getUserData()); + cmd.addVmData("metadata", "service-offering", StringUtils.unicodeEscape(serviceOffering)); + cmd.addVmData("metadata", "availability-zone", StringUtils.unicodeEscape(zoneName)); + cmd.addVmData("metadata", "local-ipv4", nic.getIp4Address()); + cmd.addVmData("metadata", "local-hostname", StringUtils.unicodeEscape(vm.getInstanceName())); + cmd.addVmData("metadata", "public-ipv4", nic.getIp4Address()); + cmd.addVmData("metadata", "public-hostname", StringUtils.unicodeEscape(vm.getInstanceName())); + cmd.addVmData("metadata", "instance-id", String.valueOf(vm.getId())); + cmd.addVmData("metadata", "vm-id", String.valueOf(vm.getInstanceName())); + cmd.addVmData("metadata", "public-keys", null); + String cloudIdentifier = _configDao.getValue("cloud.identifier"); + if (cloudIdentifier == null) { + cloudIdentifier = ""; + } else { + cloudIdentifier = "CloudStack-{" + cloudIdentifier + "}"; + } + cmd.addVmData("metadata", "cloud-identifier", cloudIdentifier); + + List phys = _phynwDao.listByZone(vm.getDataCenterIdToDeployIn()); + if (phys.isEmpty()) { + throw new CloudRuntimeException(String.format("Cannot find physical network in zone %s", vm.getDataCenterIdToDeployIn())); + } + if (phys.size() > 1) { + throw new CloudRuntimeException(String.format("Baremetal only supports one physical network in zone, but zone %s has %s physical networks", vm.getDataCenterIdToDeployIn(), phys.size())); + } + PhysicalNetworkVO phy = phys.get(0); + + SearchCriteriaService sc = SearchCriteria2.create(BaremetalPxeVO.class); + //TODO: handle both kickstart and PING + //sc.addAnd(sc.getEntity().getPodId(), Op.EQ, vm.getPodIdToDeployIn()); + sc.addAnd(sc.getEntity().getPhysicalNetworkId(), Op.EQ, phy.getId()); + BaremetalPxeVO pxeVo = sc.find(); + if (pxeVo == null) { + throw new CloudRuntimeException("No PXE server found in pod: " + vm.getPodIdToDeployIn() + ", you need to add it before starting VM"); + } + + try { + Answer ans = _agentMgr.send(pxeVo.getHostId(), cmd); + if (!ans.getResult()) { + s_logger.debug(String.format("Add userdata to vm:%s failed because %s", vm.getInstanceName(), ans.getDetails())); + return false; + } else { + return true; + } + } catch (Exception e) { + s_logger.debug(String.format("Add userdata to vm:%s failed", vm.getInstanceName()), e); + return false; + } + } + + @Override + public List> getCommands() { + // TODO Auto-generated method stub + return null; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxePingResponse.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxePingResponse.java new file mode 100755 index 00000000000..adbf0530e00 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxePingResponse.java @@ -0,0 +1,59 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class BaremetalPxePingResponse extends BaremetalPxeResponse { + @SerializedName(ApiConstants.PING_STORAGE_SERVER_IP) @Param(description="PING storage server ip") + private String pingStorageServerIp; + + @SerializedName(ApiConstants.PING_DIR) @Param(description="Root directory on PING storage server") + private String pingDir; + + @SerializedName(ApiConstants.TFTP_DIR) @Param(description="Tftp root directory of PXE server") + private String tftpDir; + + public String getPingStorageServerIp() { + return pingStorageServerIp; + } + + public void setPingStorageServerIp(String pingStorageServerIp) { + this.pingStorageServerIp = pingStorageServerIp; + } + + public String getPingDir() { + return pingDir; + } + + public void setPingDir(String pingDir) { + this.pingDir = pingDir; + } + + public String getTftpDir() { + return tftpDir; + } + + public void setTftpDir(String tftpDir) { + this.tftpDir = tftpDir; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeResourceBase.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeResourceBase.java new file mode 100755 index 00000000000..34175c8b326 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeResourceBase.java @@ -0,0 +1,157 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +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 BaremetalPxeResourceBase implements ServerResource { + private static final Logger s_logger = Logger.getLogger(BaremetalPxeResourceBase.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(BaremetalPxeService.PXE_PARAM_GUID); + _ip = (String)params.get(BaremetalPxeService.PXE_PARAM_IP); + _username = (String)params.get(BaremetalPxeService.PXE_PARAM_USERNAME); + _password = (String)params.get(BaremetalPxeService.PXE_PARAM_PASSWORD); + _zoneId = (String)params.get(BaremetalPxeService.PXE_PARAM_ZONE); + _podId = (String)params.get(BaremetalPxeService.PXE_PARAM_POD); + + if (_guid == null) { + throw new ConfigurationException("No Guid specified"); + } + + if (_zoneId == null) { + throw new ConfigurationException("No Zone 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.BaremetalPxe; + } + + @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/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeResponse.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeResponse.java new file mode 100755 index 00000000000..2103020cfef --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeResponse.java @@ -0,0 +1,71 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class BaremetalPxeResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) @Param(description="device id of ") + private String id; + + @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) @Param(description="the physical network to which this external dhcp device belongs to") + private String physicalNetworkId; + + @SerializedName(ApiConstants.PROVIDER) @Param(description="name of the provider") + private String providerId; + + @SerializedName(ApiConstants.POD_ID) @Param(description="pod id where the device is in") + private String podId; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getPhysicalNetworkId() { + return physicalNetworkId; + } + + public void setPhysicalNetworkId(String physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public String getProviderId() { + return providerId; + } + + public void setProviderId(String providerId) { + this.providerId = providerId; + } + + public String getPodId() { + return podId; + } + + public void setPodId(String podId) { + this.podId = podId; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeService.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeService.java new file mode 100644 index 00000000000..8504f82a86f --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeService.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.util.List; + +import com.cloud.baremetal.database.BaremetalPxeVO; +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.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachineProfile; + +public interface BaremetalPxeService extends Adapter { + + public boolean prepare(VirtualMachineProfile profile, NicProfile nic, DeployDestination dest, ReservationContext context); + + public boolean prepareCreateTemplate(Long pxeServerId, UserVm vm, String templateUrl); + + BaremetalPxeVO addPxeServer(AddBaremetalPxeCmd cmd); + + BaremetalPxeResponse getApiResponse(BaremetalPxeVO vo); + + List listPxeServers(ListBaremetalPxePingServersCmd cmd); + + public static final String PXE_PARAM_TYPE = "type"; + public static final String PXE_PARAM_ZONE = "zone"; + public static final String PXE_PARAM_POD = "pod"; + public static final String PXE_PARAM_IP = "ip"; + public static final String PXE_PARAM_GUID = "guid"; + public static final String PXE_PARAM_TFTP_DIR = "tftpDir"; + public static final String PXE_PARAM_USERNAME = "username"; + public static final String PXE_PARAM_PASSWORD = "password"; + public static final String PXE_PARAM_PING_STORAGE_SERVER_IP = "pingStorageServerIp"; + public static final String PXE_PARAM_PING_ROOT_DIR = "pingDir"; + public static final String PXE_PARAM_PING_STORAGE_SERVER_USERNAME = "pingStorageServerUserName"; + public static final String PXE_PARAM_PING_STORAGE_SERVER_PASSWORD = "pingStorageServerPassword"; +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalUserdataElement.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalUserdataElement.java new file mode 100755 index 00000000000..b5fd6f64da7 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalUserdataElement.java @@ -0,0 +1,175 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.util.HashMap; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ejb.Local; + +import com.cloud.baremetal.manager.BaremetalManager; +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.ResourceUnavailableException; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.GuestType; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.element.IpDeployer; +import com.cloud.network.element.NetworkElement; +import com.cloud.network.element.UserDataServiceProvider; +import com.cloud.offering.NetworkOffering; +import com.cloud.uservm.UserVm; +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 BaremetalUserdataElement extends AdapterBase implements NetworkElement, UserDataServiceProvider { + private static Map> capabilities; + + @Inject + private BaremetalPxeManager pxeMgr; + + static { + capabilities = new HashMap>(); + capabilities.put(Service.UserData, null); + } + + private boolean canHandle(DeployDestination dest) { + if (dest.getDataCenter().getNetworkType() == NetworkType.Basic && dest.getHost().getHypervisorType() == HypervisorType.BareMetal) { + return true; + } + return false; + } + + @Override + public boolean addPasswordAndUserdata(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, + ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { + if (!canHandle(dest)) { + return false; + } + + if (vm.getType() != VirtualMachine.Type.User) { + return false; + } + + return pxeMgr.addUserData(nic, (VirtualMachineProfile) vm); + } + + @Override + public boolean savePassword(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean saveSSHKey(Network network, NicProfile nic, VirtualMachineProfile vm, String SSHPublicKey) throws ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public Map> getCapabilities() { + return capabilities; + } + + @Override + public Provider getProvider() { + return BaremetalPxeManager.BAREMETAL_USERDATA_PROVIDER; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, + ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isReady(PhysicalNetworkServiceProvider provider) { + return true; + } + + @Override + public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean canEnableIndividualServices() { + // TODO Auto-generated method stub + return false; + } + + + @Override + public boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile vm) + throws ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean verifyServicesCombination(Set services) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/ListBaremetalDhcpCmd.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/ListBaremetalDhcpCmd.java new file mode 100755 index 00000000000..ba5128b6db0 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/ListBaremetalDhcpCmd.java @@ -0,0 +1,102 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseCmd.CommandType; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.PlugService; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.log4j.Logger; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; + +public class ListBaremetalDhcpCmd extends BaseListCmd { + private static final Logger s_logger = Logger.getLogger(ListBaremetalDhcpCmd.class); + private static final String s_name = "listexternaldhcpresponse"; + @PlugService BaremetalDhcpManager _dhcpMgr; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ID, type = CommandType.LONG, description = "DHCP server device ID") + private Long id; + + @Parameter(name = ApiConstants.POD_ID, type = CommandType.LONG, description = "Pod ID where pxe server is in") + private Long podId; + + @Parameter(name = ApiConstants.DHCP_SERVER_TYPE, type = CommandType.STRING, description = "Type of DHCP device") + private String deviceType; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getPodId() { + return podId; + } + + public void setPodId(Long podId) { + this.podId = podId; + } + + public String getDeviceType() { + return deviceType; + } + + public void setDeviceType(String deviceType) { + this.deviceType = deviceType; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, + ResourceAllocationException, NetworkRuleConflictException { + try { + ListResponse response = new ListResponse(); + List dhcpResponses = _dhcpMgr.listBaremetalDhcps(this); + response.setResponses(dhcpResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (Exception e) { + s_logger.debug("Exception happend while executing ListBaremetalDhcpCmd"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + + } + + @Override + public String getCommandName() { + return s_name; + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/ListBaremetalPxePingServersCmd.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/ListBaremetalPxePingServersCmd.java new file mode 100755 index 00000000000..dceb8bfa8f2 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/ListBaremetalPxePingServersCmd.java @@ -0,0 +1,92 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseCmd.CommandType; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.PlugService; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.log4j.Logger; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; + +public class ListBaremetalPxePingServersCmd extends BaseListCmd { + private static final Logger s_logger = Logger.getLogger(ListBaremetalPxePingServersCmd.class); + private static final String s_name = "listpingpxeserverresponse"; + + @PlugService + BaremetalPxeManager _pxeMgr; + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.LONG, description = "Ping pxe server device ID") + private Long id; + + @Parameter(name = ApiConstants.POD_ID, type = CommandType.LONG, description = "Pod ID where pxe server is in") + private Long podId; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getPodId() { + return podId; + } + + public void setPodId(Long podId) { + this.podId = podId; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, + ResourceAllocationException, NetworkRuleConflictException { + try { + ListResponse response = new ListResponse(); + List pxeResponses = _pxeMgr.listPxeServers(this); + response.setResponses(pxeResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (Exception e) { + s_logger.debug("Exception happend while executing ListPingPxeServersCmd" ,e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public String getCommandName() { + return s_name; + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/PrepareKickstartPxeServerCommand.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/PrepareKickstartPxeServerCommand.java new file mode 100755 index 00000000000..89515475062 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/PrepareKickstartPxeServerCommand.java @@ -0,0 +1,74 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import com.cloud.agent.api.Command; + +public class PrepareKickstartPxeServerCommand extends Command { + private String ksFile; + private String repo; + private String templateUuid; + private String mac; + private String ksDevice; + + @Override + public boolean executeInSequence() { + return true; + } + + public String getKsFile() { + return ksFile; + } + + public void setKsFile(String ksFile) { + this.ksFile = ksFile; + } + + public String getRepo() { + return repo; + } + + public void setRepo(String repo) { + this.repo = repo; + } + + public String getTemplateUuid() { + return templateUuid; + } + + public void setTemplateUuid(String templateUuid) { + this.templateUuid = templateUuid; + } + + public String getMac() { + return mac; + } + + public void setMac(String mac) { + this.mac = mac; + } + + public String getKsDevice() { + return ksDevice; + } + + public void setKsDevice(String ksDevice) { + this.ksDevice = ksDevice; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/SecurityGroupHttpClient.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/SecurityGroupHttpClient.java new file mode 100755 index 00000000000..7ad351c2e33 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/SecurityGroupHttpClient.java @@ -0,0 +1,38 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +package com.cloud.baremetal.networkservice; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.SecurityGroupRulesCmd; + +public class SecurityGroupHttpClient { + + public Answer call(String guestIp, SecurityGroupRulesCmd cmd) { + // TODO Auto-generated method stub + return null; + } + + public boolean echo(String ip, long millis, long millis2) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 8a9f71bf7d5..2ed8478a67d 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -903,7 +903,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements String fname = interfaces[i].getName(); s_logger.debug("matchPifFileInDirectory: file name '"+fname+"'"); if (fname.startsWith("eth") || fname.startsWith("bond") - || fname.startsWith("vlan")) { + || fname.startsWith("vlan") || fname.startsWith("em")) { return fname; } } diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index dd917f75a6e..96c4348cbda 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -153,6 +153,7 @@ import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; 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.FirewallRuleTO; import com.cloud.agent.api.to.IpAddressTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.PortForwardingRuleTO; @@ -190,6 +191,7 @@ import com.cloud.network.HAProxyConfigurator; import com.cloud.network.LoadBalancerConfigurator; import com.cloud.network.Networks; import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.rules.FirewallRule; import com.cloud.resource.ServerResource; import com.cloud.serializer.GsonHelper; import com.cloud.storage.Storage; @@ -618,10 +620,16 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa protected SetFirewallRulesAnswer execute(SetFirewallRulesCommand cmd) { String controlIp = getRouterSshControlIp(cmd); String[] results = new String[cmd.getRules().length]; + FirewallRuleTO[] allrules = cmd.getRules(); + FirewallRule.TrafficType trafficType = allrules[0].getTrafficType(); String[][] rules = cmd.generateFwRules(); String args = ""; args += " -F "; + if (trafficType == FirewallRule.TrafficType.Egress){ + args+= " -E "; + } + StringBuilder sb = new StringBuilder(); String[] fwRules = rules[0]; if (fwRules.length > 0) { @@ -634,13 +642,28 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa try { VmwareManager mgr = getServiceContext().getStockObject( VmwareManager.CONTEXT_STOCK_NAME); - Pair result = SshHelper.sshExecute(controlIp, + + Pair result = null; + + if (trafficType == FirewallRule.TrafficType.Egress){ + result = SshHelper.sshExecute(controlIp, + DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), + null, "/root/firewallRule_egress.sh " + args); + } else { + result = SshHelper.sshExecute(controlIp, DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), null, "/root/firewall_rule.sh " + args); + } - if (s_logger.isDebugEnabled()) + if (s_logger.isDebugEnabled()) { + if (trafficType == FirewallRule.TrafficType.Egress){ + s_logger.debug("Executing script on domain router " + controlIp + + ": /root/firewallRule_egress.sh " + args); + } else { s_logger.debug("Executing script on domain router " + controlIp + ": /root/firewall_rule.sh " + args); + } + } if (!result.first()) { s_logger.error("SetFirewallRulesCommand failure on setting one rule. args: " diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 065d3be0bdd..e9690eb5eca 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -53,6 +53,8 @@ import javax.ejb.Local; import javax.naming.ConfigurationException; import javax.xml.parsers.DocumentBuilderFactory; +import com.cloud.agent.api.to.*; +import com.cloud.network.rules.FirewallRule; import org.apache.log4j.Logger; import org.apache.xmlrpc.XmlRpcException; import org.w3c.dom.Document; @@ -7182,14 +7184,18 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe String callResult; Connection conn = getConnection(); String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); - + FirewallRuleTO[] allrules = cmd.getRules(); + FirewallRule.TrafficType trafficType = allrules[0].getTrafficType(); if (routerIp == null) { return new SetFirewallRulesAnswer(cmd, false, results); } String[][] rules = cmd.generateFwRules(); String args = ""; - args += routerIp + " -F "; + args += routerIp + " -F"; + if (trafficType == FirewallRule.TrafficType.Egress){ + args+= " -E"; + } StringBuilder sb = new StringBuilder(); String[] fwRules = rules[0]; if (fwRules.length > 0) { diff --git a/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java b/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java index 5038cc801ac..c2874aa29d9 100644 --- a/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java +++ b/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java @@ -277,6 +277,7 @@ public class JuniperSRXExternalFirewallElement extends ExternalFirewallDeviceMan firewallCapabilities.put(Capability.SupportedProtocols, "tcp,udp,icmp"); firewallCapabilities.put(Capability.MultipleIps, "true"); firewallCapabilities.put(Capability.TrafficStatistics, "per public ip"); + firewallCapabilities.put(Capability.SupportedTrafficDirection, "ingress"); capabilities.put(Service.Firewall, firewallCapabilities); // Disabling VPN for Juniper in Acton as it 1) Was never tested 2) probably just doesn't work diff --git a/plugins/pom.xml b/plugins/pom.xml index 6a4c26f71df..f91c6eed58b 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -43,6 +43,7 @@ hypervisors/kvm event-bus/rabbitmq hypervisors/simulator + hypervisors/baremetal network-elements/elastic-loadbalancer network-elements/ovs network-elements/nicira-nvp diff --git a/scripts/network/domr/call_firewall.sh b/scripts/network/domr/call_firewall.sh index 08da3411cbe..f6ad0be1316 100755 --- a/scripts/network/domr/call_firewall.sh +++ b/scripts/network/domr/call_firewall.sh @@ -46,28 +46,25 @@ then exit 1 fi fflag= -while getopts ':F' OPTION +eflag= +while getopts ':FE' OPTION do case $OPTION in F) fflag=1 - ;; + ;; + E) eflag=1 + ;; \?) ;; esac done -if [ -n "$fflag" ] +if [ -n "$eflag" ] +then + ssh -p 3922 -q -o StrictHostKeyChecking=no -i $cert root@$domRIp "/root/firewallRule_egress.sh $*" +elif [ -n "$fflag" ] then ssh -p 3922 -q -o StrictHostKeyChecking=no -i $cert root@$domRIp "/root/firewall_rule.sh $*" else ssh -p 3922 -q -o StrictHostKeyChecking=no -i $cert root@$domRIp "/root/firewall.sh $*" fi exit $? - - - - - - - - - diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 3c80a689638..54639a87b3e 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -2166,10 +2166,15 @@ public class ApiResponseHelper implements ResponseGenerator { List cidrs = ApiDBUtils.findFirewallSourceCidrs(fwRule.getId()); response.setCidrList(StringUtils.join(cidrs, ",")); - - IpAddress ip = ApiDBUtils.findIpAddressById(fwRule.getSourceIpAddressId()); - response.setPublicIpAddressId(ip.getId()); - response.setPublicIpAddress(ip.getAddress().addr()); + + if (fwRule.getTrafficType() == FirewallRule.TrafficType.Ingress) { + IpAddress ip = ApiDBUtils.findIpAddressById(fwRule.getSourceIpAddressId()); + response.setPublicIpAddressId(ip.getId()); + response.setPublicIpAddress(ip.getAddress().addr()); + } else if (fwRule.getTrafficType() == FirewallRule.TrafficType.Egress) { + response.setPublicIpAddress(null); + response.setNetworkId(fwRule.getNetworkId()); + } FirewallRule.State state = fwRule.getState(); String stateToSet = state.toString(); diff --git a/server/src/com/cloud/api/ApiServlet.java b/server/src/com/cloud/api/ApiServlet.java index 0f8924a080c..3244351858d 100755 --- a/server/src/com/cloud/api/ApiServlet.java +++ b/server/src/com/cloud/api/ApiServlet.java @@ -402,13 +402,10 @@ public class ApiServlet extends HttpServlet { } } } - sb.append(" }"); - sb.append(", \"cloudstack-version\": \""); - sb.append(ApiDBUtils.getVersion()); - sb.append("\" }"); + sb.append(" } }"); } else { sb.append(""); - sb.append(""); + sb.append(""); sb.append("" + inactiveInterval + ""); Enumeration attrNames = session.getAttributeNames(); if (attrNames != null) { @@ -435,13 +432,10 @@ public class ApiServlet extends HttpServlet { private String getLogoutSuccessResponse(String responseType) { StringBuffer sb = new StringBuffer(); if (BaseCmd.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) { - sb.append("{ \"logoutresponse\" : { \"description\" : \"success\" }"); - sb.append(", \"cloudstack-version\": \""); - sb.append(ApiDBUtils.getVersion()); - sb.append("\" }"); + sb.append("{ \"logoutresponse\" : { \"description\" : \"success\" } }"); } else { sb.append(""); - sb.append(""); + sb.append(""); sb.append("success"); sb.append(""); } diff --git a/server/src/com/cloud/api/response/ApiResponseSerializer.java b/server/src/com/cloud/api/response/ApiResponseSerializer.java index 11aee3d9390..3b1d9a6368b 100644 --- a/server/src/com/cloud/api/response/ApiResponseSerializer.java +++ b/server/src/com/cloud/api/response/ApiResponseSerializer.java @@ -122,9 +122,7 @@ public class ApiResponseSerializer { sb.append("{ }"); } } - sb.append(", \"cloudstack-version\": \""); - sb.append(ApiDBUtils.getVersion()); - sb.append("\" }"); + sb.append(" }"); return sb.toString(); } return null; @@ -133,7 +131,7 @@ public class ApiResponseSerializer { private static String toXMLSerializedString(ResponseObject result) { StringBuilder sb = new StringBuilder(); sb.append(""); - sb.append("<").append(result.getResponseName()).append(" cloudstack-version=\"").append(ApiDBUtils.getVersion()).append("\">"); + sb.append("<").append(result.getResponseName()).append(" cloud-stack-version=\"").append(ApiDBUtils.getVersion()).append("\">"); if (result instanceof ListResponse) { Integer count = ((ListResponse) result).getCount(); diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 4ae144e6ce1..5e4996b144c 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -358,7 +358,13 @@ public enum Config { DetailBatchQuerySize("Advanced", ManagementServer.class, Integer.class, "detail.batch.query.size", "2000", "Default entity detail batch query size for listing", null), ConcurrentSnapshotsThresholdPerHost("Advanced", ManagementServer.class, Long.class, "concurrent.snapshots.threshold.perhost", - null, "Limits number of snapshots that can be handled by the host concurrently; default is NULL - unlimited", null); + null, "Limits number of snapshots that can be handled by the host concurrently; default is NULL - unlimited", null), + + ExternalBaremetalSystemUrl("Advanced", ManagementServer.class, String.class, "external.baremetal.system.url", null, "url of external baremetal system that CloudStack will talk to", null), + ExternalBaremetalResourceClassName("Advanced", ManagementServer.class, String.class, "external,baremetal.resource.classname", null, "class name for handling external baremetal resource", null), + EnableBaremetalSecurityGroupAgentEcho("Advanced", ManagementServer.class, Boolean.class, "enable.baremetal.securitygroup.agent.echo", "false", "After starting provision process, periodcially echo security agent installed in the template. Treat provisioning as success only if echo successfully", null), + IntervalToEchoBaremetalSecurityGroupAgent("Advanced", ManagementServer.class, Integer.class, "interval.baremetal.securitygroup.agent.echo", "10", "Interval to echo baremetal security group agent, in seconds", null), + TimeoutToEchoBaremetalSecurityGroupAgent("Advanced", ManagementServer.class, Integer.class, "timeout.baremetal.securitygroup.agent.echo", "3600", "Timeout to echo baremetal security group agent, in seconds, the provisioning process will be treated as a failure", null); private final String _category; private final Class _componentClass; diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java index 546f1bf613c..ffe75610293 100755 --- a/server/src/com/cloud/network/NetworkManager.java +++ b/server/src/com/cloud/network/NetworkManager.java @@ -144,6 +144,8 @@ public interface NetworkManager { UserDataServiceProvider getPasswordResetProvider(Network network); + UserDataServiceProvider getSSHKeyResetProvider(Network network); + boolean applyIpAssociations(Network network, boolean continueOnError) throws ResourceUnavailableException; boolean applyIpAssociations(Network network, boolean rulesRevoked, boolean continueOnError, List publicIps) throws ResourceUnavailableException; @@ -262,7 +264,7 @@ public interface NetworkManager { * @throws InsufficientCapacityException * @throws ResourceUnavailableException */ - NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, VirtualMachineProfileImpl vmProfile, boolean prepare) throws InsufficientVirtualNetworkCapcityException, + NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, VirtualMachineProfile vmProfile, boolean prepare) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException; diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index d962e49ad3e..3d83487e89b 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -2235,20 +2235,23 @@ public class NetworkManagerImpl implements NetworkManager, Manager, Listener { boolean success = true; Network network = _networksDao.findById(rules.get(0).getNetworkId()); + FirewallRuleVO.TrafficType trafficType = rules.get(0).getTrafficType(); + List publicIps = new ArrayList(); - // get the list of public ip's owned by the network - List userIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), null); - List publicIps = new ArrayList(); - if (userIps != null && !userIps.isEmpty()) { - for (IPAddressVO userIp : userIps) { + if (! (rules.get(0).getPurpose() == FirewallRule.Purpose.Firewall && trafficType == FirewallRule.TrafficType.Egress)) { + // get the list of public ip's owned by the network + List userIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), null); + if (userIps != null && !userIps.isEmpty()) { + for (IPAddressVO userIp : userIps) { PublicIp publicIp = new PublicIp(userIp, _vlanDao.findById(userIp.getVlanId()), NetUtils.createSequenceBasedMacAddress(userIp.getMacAddress())); publicIps.add(publicIp); - } - } + } + } // rules can not programmed unless IP is associated with network service provider, so run IP assoication for // the network so as to ensure IP is associated before applying rules (in add state) applyIpAssociations(network, false, continueOnError, publicIps); + } try { applier.applyRules(network, purpose, rules); @@ -2260,8 +2263,10 @@ public class NetworkManagerImpl implements NetworkManager, Manager, Listener { success = false; } - // if all the rules configured on public IP are revoked then dis-associate IP with network service provider - applyIpAssociations(network, true, continueOnError, publicIps); + if (! (rules.get(0).getPurpose() == FirewallRule.Purpose.Firewall && trafficType == FirewallRule.TrafficType.Egress) ) { + // if all the rules configured on public IP are revoked then dis-associate IP with network service provider + applyIpAssociations(network, true, continueOnError, publicIps); + } return success; } @@ -2410,9 +2415,15 @@ public class NetworkManagerImpl implements NetworkManager, Manager, Listener { } // apply firewall rules - List firewallRulesToApply = _firewallDao.listByNetworkAndPurpose(networkId, Purpose.Firewall); - if (!_firewallMgr.applyFirewallRules(firewallRulesToApply, false, caller)) { - s_logger.warn("Failed to reapply firewall rule(s) as a part of network id=" + networkId + " restart"); + List firewallIngressRulesToApply = _firewallDao.listByNetworkPurposeTrafficType(networkId, Purpose.Firewall, FirewallRule.TrafficType.Ingress); + if (!_firewallMgr.applyFirewallRules(firewallIngressRulesToApply, false, caller)) { + s_logger.warn("Failed to reapply Ingress firewall rule(s) as a part of network id=" + networkId + " restart"); + success = false; + } + + List firewallEgressRulesToApply = _firewallDao.listByNetworkPurposeTrafficType(networkId, Purpose.Firewall, FirewallRule.TrafficType.Egress); + if (!_firewallMgr.applyFirewallRules(firewallEgressRulesToApply, false, caller)) { + s_logger.warn("Failed to reapply firewall Egress rule(s) as a part of network id=" + networkId + " restart"); success = false; } @@ -2576,6 +2587,18 @@ public class NetworkManagerImpl implements NetworkManager, Manager, Listener { return (UserDataServiceProvider)_networkModel.getElementImplementingProvider(passwordProvider); } + @Override + public UserDataServiceProvider getSSHKeyResetProvider(Network network) { + String SSHKeyProvider = _ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Service.UserData); + + if (SSHKeyProvider == null) { + s_logger.debug("Network " + network + " doesn't support service " + Service.UserData.getName()); + return null; + } + + return (UserDataServiceProvider)_networkModel.getElementImplementingProvider(SSHKeyProvider); + } + protected boolean isSharedNetworkOfferingWithServices(long networkOfferingId) { NetworkOfferingVO networkOffering = _networkOfferingDao.findById(networkOfferingId); if ( (networkOffering.getGuestType() == Network.GuestType.Shared) && ( @@ -2964,23 +2987,43 @@ public class NetworkManagerImpl implements NetworkManager, Manager, Listener { } // revoke all firewall rules for the network w/o applying them on the DB - List firewallRules = _firewallDao.listByNetworkAndPurpose(networkId, Purpose.Firewall); + List firewallRules = _firewallDao.listByNetworkPurposeTrafficType(networkId, Purpose.Firewall, FirewallRule.TrafficType.Ingress); if (s_logger.isDebugEnabled()) { - s_logger.debug("Releasing " + firewallRules.size() + " firewall rules for network id=" + networkId + " as a part of shutdownNetworkRules"); + s_logger.debug("Releasing " + firewallRules.size() + " firewall ingress rules for network id=" + networkId + " as a part of shutdownNetworkRules"); } for (FirewallRuleVO firewallRule : firewallRules) { - s_logger.trace("Marking firewall rule " + firewallRule + " with Revoke state"); + s_logger.trace("Marking firewall ingress rule " + firewallRule + " with Revoke state"); firewallRule.setState(FirewallRule.State.Revoke); } try { if (!_firewallMgr.applyRules(firewallRules, true, false)) { - s_logger.warn("Failed to cleanup firewall rules as a part of shutdownNetworkRules"); + s_logger.warn("Failed to cleanup firewall ingress rules as a part of shutdownNetworkRules"); success = false; } } catch (ResourceUnavailableException ex) { - s_logger.warn("Failed to cleanup firewall rules as a part of shutdownNetworkRules due to ", ex); + s_logger.warn("Failed to cleanup firewall ingress rules as a part of shutdownNetworkRules due to ", ex); + success = false; + } + + List firewallEgressRules = _firewallDao.listByNetworkPurposeTrafficType(networkId, Purpose.Firewall, FirewallRule.TrafficType.Egress); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Releasing " + firewallEgressRules.size() + " firewall egress rules for network id=" + networkId + " as a part of shutdownNetworkRules"); + } + + for (FirewallRuleVO firewallRule : firewallEgressRules) { + s_logger.trace("Marking firewall egress rule " + firewallRule + " with Revoke state"); + firewallRule.setState(FirewallRule.State.Revoke); + } + + try { + if (!_firewallMgr.applyRules(firewallEgressRules, true, false)) { + s_logger.warn("Failed to cleanup firewall egress rules as a part of shutdownNetworkRules"); + success = false; + } + } catch (ResourceUnavailableException ex) { + s_logger.warn("Failed to cleanup firewall egress rules as a part of shutdownNetworkRules due to ", ex); success = false; } @@ -3304,20 +3347,19 @@ public class NetworkManagerImpl implements NetworkManager, Manager, Listener { } @Override - public NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, VirtualMachineProfileImpl vmProfile, boolean prepare) + public NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, VirtualMachineProfile vmProfile, boolean prepare) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { VirtualMachine vm = vmProfile.getVirtualMachine(); - NetworkVO networkVO = _networksDao.findById(network.getId()); DataCenter dc = _configMgr.getZone(network.getDataCenterId()); Host host = _hostDao.findById(vm.getHostId()); DeployDestination dest = new DeployDestination(dc, null, null, host); NicProfile nic = getNicProfileForVm(network, requested, vm); - //1) allocate nic (if needed) - if (nic == null) { + //1) allocate nic (if needed) Always allocate if it is a user vm + if (nic == null || (vmProfile.getType() == VirtualMachine.Type.User)) { int deviceId = _nicDao.countNics(vm.getId()); nic = allocateNic(requested, network, false, @@ -3332,6 +3374,7 @@ public class NetworkManagerImpl implements NetworkManager, Manager, Listener { //2) prepare nic if (prepare) { + NetworkVO networkVO = _networksDao.findById(network.getId()); nic = prepareNic(vmProfile, dest, context, nic.getId(), networkVO); s_logger.debug("Nic is prepared successfully for vm " + vm + " in network " + network); } diff --git a/server/src/com/cloud/network/NetworkModelImpl.java b/server/src/com/cloud/network/NetworkModelImpl.java index a99e9c50cbe..ce53d6b2f5e 100644 --- a/server/src/com/cloud/network/NetworkModelImpl.java +++ b/server/src/com/cloud/network/NetworkModelImpl.java @@ -1692,6 +1692,9 @@ public class NetworkModelImpl implements NetworkModel, Manager{ } else { nic = _nicDao.findByInstanceIdAndNetworkId(networkId, vm.getId()); } + if (nic == null) { + return null; + } NetworkVO network = _networksDao.findById(networkId); Integer networkRate = getNetworkRate(network.getId(), vm.getId()); @@ -1836,4 +1839,4 @@ public class NetworkModelImpl implements NetworkModel, Manager{ return offering.isInline(); } -} \ No newline at end of file +} diff --git a/server/src/com/cloud/network/dao/FirewallRulesDao.java b/server/src/com/cloud/network/dao/FirewallRulesDao.java index cc184c84fce..b5b7f99267d 100644 --- a/server/src/com/cloud/network/dao/FirewallRulesDao.java +++ b/server/src/com/cloud/network/dao/FirewallRulesDao.java @@ -56,6 +56,7 @@ public interface FirewallRulesDao extends GenericDao { long countRulesByIpId(long sourceIpId); List listByNetworkPurposeTrafficTypeAndNotRevoked(long networkId, FirewallRule.Purpose purpose, FirewallRule.TrafficType trafficType); - + List listByNetworkPurposeTrafficType(long networkId, FirewallRule.Purpose purpose, FirewallRule.TrafficType trafficType); + List listByIpAndPurposeWithState(Long addressId, FirewallRule.Purpose purpose, FirewallRule.State state); } diff --git a/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java b/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java index 2fff15aff8d..8bc0c47adfb 100644 --- a/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java +++ b/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java @@ -67,6 +67,7 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), Op.EQ); AllFieldsSearch.and("networkId", AllFieldsSearch.entity().getNetworkId(), Op.EQ); AllFieldsSearch.and("related", AllFieldsSearch.entity().getRelated(), Op.EQ); + AllFieldsSearch.and("trafficType", AllFieldsSearch.entity().getTrafficType(), Op.EQ); AllFieldsSearch.done(); NotRevokedSearch = createSearchBuilder(); @@ -161,6 +162,20 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i return listBy(sc); } + @Override + public List listByNetworkPurposeTrafficTypeAndNotRevoked(long networkId, FirewallRule.Purpose purpose, TrafficType trafficType) { + SearchCriteria sc = NotRevokedSearch.create(); + sc.setParameters("networkId", networkId); + sc.setParameters("state", State.Revoke); + if (purpose != null) { + sc.setParameters("purpose", purpose); + } + sc.setParameters("trafficType", trafficType); + + return listBy(sc); + } + + @Override public boolean setStateToAdd(FirewallRuleVO rule) { SearchCriteria sc = AllFieldsSearch.create(); @@ -275,10 +290,9 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i } @Override - public List listByNetworkPurposeTrafficTypeAndNotRevoked(long networkId, Purpose purpose, TrafficType trafficType) { - SearchCriteria sc = NotRevokedSearch.create(); + public List listByNetworkPurposeTrafficType(long networkId, Purpose purpose, TrafficType trafficType) { + SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("networkId", networkId); - sc.setParameters("state", State.Revoke); if (purpose != null) { sc.setParameters("purpose", purpose); @@ -288,6 +302,8 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i return listBy(sc); } + + @Override @DB public boolean remove(Long id) { Transaction txn = Transaction.currentTxn(); diff --git a/server/src/com/cloud/network/element/CloudZonesNetworkElement.java b/server/src/com/cloud/network/element/CloudZonesNetworkElement.java index 67597261e00..40d4e04a97b 100644 --- a/server/src/com/cloud/network/element/CloudZonesNetworkElement.java +++ b/server/src/com/cloud/network/element/CloudZonesNetworkElement.java @@ -251,6 +251,12 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem return false; } + @Override + public boolean saveSSHKey(Network network, NicProfile nic, VirtualMachineProfile vm, String SSHPublicKey) throws ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + @Override public boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException { // TODO Auto-generated method stub diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java index 2b54ae0fe36..8293ad76465 100755 --- a/server/src/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VirtualRouterElement.java @@ -565,6 +565,8 @@ public class VirtualRouterElement extends AdapterBase implements VirtualRouterEl Map firewallCapabilities = new HashMap(); firewallCapabilities.put(Capability.TrafficStatistics, "per public ip"); firewallCapabilities.put(Capability.SupportedProtocols, "tcp,udp,icmp"); + firewallCapabilities.put(Capability.SupportedEgressProtocols, "tcp,udp,icmp, all"); + firewallCapabilities.put(Capability.SupportedTrafficDirection, "ingress, egress"); firewallCapabilities.put(Capability.MultipleIps, "true"); capabilities.put(Service.Firewall, firewallCapabilities); @@ -664,6 +666,24 @@ public class VirtualRouterElement extends AdapterBase implements VirtualRouterEl return _routerMgr.savePasswordToRouter(network, nic, uservm, routers); } + @Override + public boolean saveSSHKey(Network network, NicProfile nic, VirtualMachineProfile vm, String SSHPublicKey) + throws ResourceUnavailableException { + if (!canHandle(network, null)) { + return false; + } + List routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER); + if (routers == null || routers.isEmpty()) { + s_logger.debug("Can't find virtual router element in network " + network.getId()); + return true; + } + + @SuppressWarnings("unchecked") + VirtualMachineProfile uservm = (VirtualMachineProfile) vm; + + return _routerMgr.saveSSHPublicKeyToRouter(network, nic, uservm, routers, SSHPublicKey); + } + @Override public boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException { @@ -860,8 +880,8 @@ public class VirtualRouterElement extends AdapterBase implements VirtualRouterEl // for Basic zone, add all Running routers - we have to send Dhcp/vmData/password info to them when // network.dns.basiczone.updates is set to "all" - Long podId = dest.getPod().getId(); if (isPodBased && _routerMgr.getDnsBasicZoneUpdate().equalsIgnoreCase("all")) { + Long podId = dest.getPod().getId(); List allRunningRoutersOutsideThePod = _routerDao.findByNetworkOutsideThePod(network.getId(), podId, State.Running, Role.VIRTUAL_ROUTER); routers.addAll(allRunningRoutersOutsideThePod); diff --git a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java index 31fed3e0436..a3f60cb65f8 100644 --- a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java +++ b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java @@ -16,6 +16,25 @@ // under the License. package com.cloud.network.firewall; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.api.command.user.firewall.ListFirewallRulesCmd; +import org.apache.log4j.Logger; + +import com.mysql.jdbc.ConnectionPropertiesImpl; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.command.user.firewall.ListEgressFirewallRulesCmd; +import org.apache.cloudstack.api.command.user.firewall.ListFirewallRulesCmd; import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.domain.dao.DomainDao; @@ -27,9 +46,16 @@ import com.cloud.event.dao.UsageEventDao; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.network.*; +import com.cloud.network.IPAddressVO; +import com.cloud.network.IpAddress; +import com.cloud.network.Network; +import com.cloud.network.NetworkVO; import com.cloud.network.Network.Capability; import com.cloud.network.Network.Service; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.NetworkManager; +import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkRuleApplier; import com.cloud.network.dao.FirewallRulesCidrsDao; import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.IPAddressDao; @@ -143,32 +169,44 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ne } @Override - public FirewallRule createFirewallRule(FirewallRule rule) throws NetworkRuleConflictException { + public FirewallRule createEgressFirewallRule(FirewallRule rule) throws NetworkRuleConflictException { Account caller = UserContext.current().getCaller(); - return createFirewallRule(rule.getSourceIpAddressId(), caller, rule.getXid(), rule.getSourcePortStart(), + return createFirewallRule(null, caller, rule.getXid(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(), rule.getSourceCidrList(), rule.getIcmpCode(), - rule.getIcmpType(), null, rule.getType(), rule.getNetworkId()); + rule.getIcmpType(), null, rule.getType(), rule.getNetworkId(), rule.getTrafficType()); } + public FirewallRule createIngressFirewallRule(FirewallRule rule) throws NetworkRuleConflictException { + Account caller = UserContext.current().getCaller(); + Long sourceIpAddressId = rule.getSourceIpAddressId(); + + return createFirewallRule(sourceIpAddressId, caller, rule.getXid(), rule.getSourcePortStart(), + rule.getSourcePortEnd(), rule.getProtocol(), rule.getSourceCidrList(), rule.getIcmpCode(), + rule.getIcmpType(), null, rule.getType(), rule.getNetworkId(), rule.getTrafficType()); + } + @DB @Override @ActionEvent(eventType = EventTypes.EVENT_FIREWALL_OPEN, eventDescription = "creating firewall rule", create = true) - public FirewallRule createFirewallRule(long ipAddrId, Account caller, String xId, Integer portStart, - Integer portEnd, String protocol, List sourceCidrList, Integer icmpCode, Integer icmpType, - Long relatedRuleId, FirewallRule.FirewallRuleType type, long networkId) throws NetworkRuleConflictException { - - IPAddressVO ipAddress = _ipAddressDao.findById(ipAddrId); - // Validate ip address - if (ipAddress == null && type == FirewallRule.FirewallRuleType.User) { - throw new InvalidParameterValueException("Unable to create firewall rule; ip id=" + ipAddrId + - " doesn't exist in the system"); + public FirewallRule createFirewallRule(Long ipAddrId, Account caller, String xId, Integer portStart, + Integer portEnd, String protocol, List sourceCidrList, Integer icmpCode, Integer icmpType, + Long relatedRuleId, FirewallRule.FirewallRuleType type, Long networkId, FirewallRule.TrafficType trafficType) throws NetworkRuleConflictException { + + IPAddressVO ipAddress = null; + if (ipAddrId != null){ + // this for ingress firewall rule, for egress id is null + ipAddress = _ipAddressDao.findById(ipAddrId); + // Validate ip address + if (ipAddress == null && type == FirewallRule.FirewallRuleType.User) { + throw new InvalidParameterValueException("Unable to create firewall rule; " + + "couldn't locate IP address by id in the system"); + } + _networkModel.checkIpForService(ipAddress, Service.Firewall, null); } - - _networkModel.checkIpForService(ipAddress, Service.Firewall, null); - - validateFirewallRule(caller, ipAddress, portStart, portEnd, protocol, Purpose.Firewall, type); - + + validateFirewallRule(caller, ipAddress, portStart, portEnd, protocol, Purpose.Firewall, type, networkId, trafficType); + // icmp code and icmp type can't be passed in for any other protocol rather than icmp if (!protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO) && (icmpCode != null || icmpType != null)) { throw new InvalidParameterValueException("Can specify icmpCode and icmpType for ICMP protocol only"); @@ -182,15 +220,21 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ne Long domainId = null; if (ipAddress != null) { + //Ingress firewall rule accountId = ipAddress.getAllocatedToAccountId(); domainId = ipAddress.getAllocatedInDomainId(); + } else if (networkId != null) { + //egress firewall rule + Network network = _networkModel.getNetwork(networkId); + accountId = network.getAccountId(); + domainId = network.getDomainId(); } Transaction txn = Transaction.currentTxn(); txn.start(); FirewallRuleVO newRule = new FirewallRuleVO(xId, ipAddrId, portStart, portEnd, protocol.toLowerCase(), networkId, - accountId, domainId, Purpose.Firewall, sourceCidrList, icmpCode, icmpType, relatedRuleId, null); + accountId, domainId, Purpose.Firewall, sourceCidrList, icmpCode, icmpType, relatedRuleId, trafficType); newRule.setType(type); newRule = _firewallDao.persist(newRule); @@ -211,7 +255,9 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ne public Pair, Integer> listFirewallRules(ListFirewallRulesCmd cmd) { Long ipId = cmd.getIpAddressId(); Long id = cmd.getId(); + Long networkId = null; Map tags = cmd.getTags(); + FirewallRule.TrafficType trafficType = cmd.getTrafficType(); Account caller = UserContext.current().getCaller(); List permittedAccounts = new ArrayList(); @@ -235,7 +281,13 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ne _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); sb.and("id", sb.entity().getId(), Op.EQ); - sb.and("ip", sb.entity().getSourceIpAddressId(), Op.EQ); + sb.and("trafficType", sb.entity().getTrafficType(), Op.EQ); + if (cmd instanceof ListEgressFirewallRulesCmd ) { + networkId =((ListEgressFirewallRulesCmd)cmd).getNetworkId(); + sb.and("networkId", sb.entity().getNetworkId(), Op.EQ); + } else { + sb.and("ip", sb.entity().getSourceIpAddressId(), Op.EQ); + } sb.and("purpose", sb.entity().getPurpose(), Op.EQ); @@ -270,9 +322,14 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ne if (ipId != null) { sc.setParameters("ip", ipId); + } else if (cmd instanceof ListEgressFirewallRulesCmd) { + if (networkId != null) { + sc.setParameters("networkId", networkId); + } } sc.setParameters("purpose", Purpose.Firewall); + sc.setParameters("trafficType", trafficType); Pair, Integer> result = _firewallDao.searchAndCount(sc, filter); return new Pair, Integer>(result.first(), result.second()); @@ -280,10 +337,17 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ne @Override public void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflictException { + List rules; + if(newRule.getSourceIpAddressId() != null){ + rules = _firewallDao.listByIpAndPurposeAndNotRevoked(newRule.getSourceIpAddressId(), null); + assert (rules.size() >= 1) : "For network rules, we now always first persist the rule and then check for " + + "network conflicts so we should at least have one rule at this point."; + } else { + // fetches only firewall egress rules. + rules = _firewallDao.listByNetworkPurposeTrafficTypeAndNotRevoked(newRule.getNetworkId(), Purpose.Firewall, newRule.getTrafficType()); + assert (rules.size() >= 1); + } - List rules = _firewallDao.listByIpAndPurposeAndNotRevoked(newRule.getSourceIpAddressId(), null); - assert (rules.size() >= 1) : "For network rules, we now always first persist the rule and then check for " + - "network conflicts so we should at least have one rule at this point."; for (FirewallRuleVO rule : rules) { if (rule.getId() == newRule.getId()) { @@ -371,7 +435,7 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ne @Override public void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, - String proto, Purpose purpose, FirewallRuleType type) { + String proto, Purpose purpose, FirewallRuleType type, Long networkId, FirewallRule.TrafficType trafficType ) { if (portStart != null && !NetUtils.isValidPort(portStart)) { throw new InvalidParameterValueException("publicPort is an invalid value: " + portStart); } @@ -388,38 +452,56 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ne return; } - // Validate ip address - _accountMgr.checkAccess(caller, null, true, ipAddress); - - Long networkId = null; - - if (ipAddress.getAssociatedWithNetworkId() == null) { - throw new InvalidParameterValueException("Unable to create firewall rule ; ip id=" + - ipAddress.getId() + " is not associated with any network"); - } else { - networkId = ipAddress.getAssociatedWithNetworkId(); - } - - Network network = _networkModel.getNetwork(networkId); - assert network != null : "Can't create port forwarding rule as network associated with public ip address is null?"; - - // Verify that the network guru supports the protocol specified - Map caps = null; - - if (purpose == Purpose.LoadBalancing) { - if (!_elbEnabled) { - caps = _networkModel.getNetworkServiceCapabilities(network.getId(), Service.Lb); + if (ipAddress!=null){ + if (ipAddress.getAssociatedWithNetworkId() == null) { + throw new InvalidParameterValueException("Unable to create firewall rule ; ip with specified id is not associated with any network"); + } else { + networkId = ipAddress.getAssociatedWithNetworkId(); } - } else if (purpose == Purpose.PortForwarding) { - caps = _networkModel.getNetworkServiceCapabilities(network.getId(), Service.PortForwarding); - } - if (caps != null) { - String supportedProtocols = caps.get(Capability.SupportedProtocols).toLowerCase(); - if (!supportedProtocols.contains(proto.toLowerCase())) { - throw new InvalidParameterValueException("Protocol " + proto + " is not supported in zone " + network.getDataCenterId()); - } else if (proto.equalsIgnoreCase(NetUtils.ICMP_PROTO) && purpose != Purpose.Firewall) { - throw new InvalidParameterValueException("Protocol " + proto + " is currently supported only for rules with purpose " + Purpose.Firewall); + // Validate ip address + _accountMgr.checkAccess(caller, null, true, ipAddress); + + Network network = _networkModel.getNetwork(networkId); + assert network != null : "Can't create port forwarding rule as network associated with public ip address is null?"; + + if (trafficType == FirewallRule.TrafficType.Egress) { + _accountMgr.checkAccess(caller, null, true, network); + } + + // Verify that the network guru supports the protocol specified + Map caps = null; + + if (purpose == Purpose.LoadBalancing) { + if (!_elbEnabled) { + caps = _networkModel.getNetworkServiceCapabilities(network.getId(), Service.Lb); + } + } else if (purpose == Purpose.PortForwarding) { + caps = _networkModel.getNetworkServiceCapabilities(network.getId(), Service.PortForwarding); + }else if (purpose == Purpose.Firewall){ + caps = _networkModel.getNetworkServiceCapabilities(network.getId(),Service.Firewall); + } + + if (caps != null) { + String supportedProtocols; + String supportedTrafficTypes = null; + if (purpose == FirewallRule.Purpose.Firewall) { + supportedTrafficTypes = caps.get(Capability.SupportedTrafficDirection).toLowerCase(); + } + + if (purpose == FirewallRule.Purpose.Firewall && trafficType == FirewallRule.TrafficType.Egress) { + supportedProtocols = caps.get(Capability.SupportedEgressProtocols).toLowerCase(); + } else { + supportedProtocols = caps.get(Capability.SupportedProtocols).toLowerCase(); + } + + if (!supportedProtocols.contains(proto.toLowerCase())) { + throw new InvalidParameterValueException("Protocol " + proto + " is not supported in zone " + network.getDataCenterId()); + } else if (proto.equalsIgnoreCase(NetUtils.ICMP_PROTO) && purpose != Purpose.Firewall) { + throw new InvalidParameterValueException("Protocol " + proto + " is currently supported only for rules with purpose " + Purpose.Firewall); + } else if (purpose == Purpose.Firewall && !supportedTrafficTypes.contains(trafficType.toString().toLowerCase())) { + throw new InvalidParameterValueException("Traffic Type " + trafficType + " is currently supported by Firewall in network " + networkId); + } } } } @@ -513,11 +595,17 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ne } @Override - public boolean applyFirewallRules(long ipId, Account caller) throws ResourceUnavailableException { + public boolean applyIngressFirewallRules(long ipId, Account caller) throws ResourceUnavailableException { List rules = _firewallDao.listByIpAndPurpose(ipId, Purpose.Firewall); return applyFirewallRules(rules, false, caller); } + @Override + public boolean applyEgressFirewallRules (FirewallRule rule, Account caller) throws ResourceUnavailableException { + List rules = _firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), Purpose.Firewall, FirewallRule.TrafficType.Egress); + return applyFirewallRules(rules, false, caller); + } + @Override public boolean applyFirewallRules(List rules, boolean continueOnError, Account caller) { @@ -565,10 +653,19 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ne revokeRule(rule, caller, userId, false); boolean success = false; + Long networkId = rule.getNetworkId(); if (apply) { - List rules = _firewallDao.listByIpAndPurpose(rule.getSourceIpAddressId(), Purpose.Firewall); - return applyFirewallRules(rules, false, caller); + // ingress firewall rule + if (rule.getSourceIpAddressId() != null){ + //feteches ingress firewall, ingress firewall rules associated with the ip + List rules = _firewallDao.listByIpAndPurpose(rule.getSourceIpAddressId(), Purpose.Firewall); + return applyFirewallRules(rules, false, caller); + //egress firewall rule + } else if ( networkId != null){ + List rules = _firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), Purpose.Firewall, FirewallRule.TrafficType.Egress); + return applyFirewallRules(rules, false, caller); + } } else { success = true; } @@ -663,7 +760,7 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ne List oneCidr = new ArrayList(); oneCidr.add(NetUtils.ALL_CIDRS); return createFirewallRule(ipAddrId, caller, null, startPort, endPort, protocol, oneCidr, icmpCode, icmpType, - relatedRuleId, FirewallRule.FirewallRuleType.User, networkId); + relatedRuleId, FirewallRule.FirewallRuleType.User, networkId, FirewallRule.TrafficType.Ingress); } @Override @@ -755,7 +852,7 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ne for (Long ipId : ipsToReprogram) { s_logger.debug("Applying firewall rules for ip address id=" + ipId + " as a part of vm expunge"); try { - success = success && applyFirewallRules(ipId, _accountMgr.getSystemAccount()); + success = success && applyIngressFirewallRules(ipId, _accountMgr.getSystemAccount()); } catch (ResourceUnavailableException ex) { s_logger.warn("Failed to apply port forwarding rules for ip id=" + ipId); success = false; @@ -771,7 +868,7 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ne for (FirewallRuleVO rule : systemRules) { try { this.createFirewallRule(ip.getId(), acct, rule.getXid(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(), - rule.getSourceCidrList(), rule.getIcmpCode(), rule.getIcmpType(), rule.getRelated(), FirewallRuleType.System, rule.getNetworkId()); + rule.getSourceCidrList(), rule.getIcmpCode(), rule.getIcmpType(), rule.getRelated(), FirewallRuleType.System, rule.getNetworkId(), rule.getTrafficType()); } catch (Exception e) { s_logger.debug("Failed to add system wide firewall rule, due to:" + e.toString()); } diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 289ee1892cb..8e47545974c 100755 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -1053,9 +1053,6 @@ public class LoadBalancingRulesManagerImpl implements LoadBalancingRulesMa throw ex; } - _firewallMgr.validateFirewallRule(caller.getCaller(), ipAddr, srcPortStart, srcPortEnd, lb.getProtocol(), - Purpose.LoadBalancing, FirewallRuleType.User); - Long networkId = ipAddr.getAssociatedWithNetworkId(); if (networkId == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to create load balancer rule ; specified sourceip id is not associated with any network"); @@ -1063,6 +1060,10 @@ public class LoadBalancingRulesManagerImpl implements LoadBalancingRulesMa throw ex; } + + _firewallMgr.validateFirewallRule(caller.getCaller(), ipAddr, srcPortStart, srcPortEnd, lb.getProtocol(), + Purpose.LoadBalancing, FirewallRuleType.User, networkId, null); + NetworkVO network = _networkDao.findById(networkId); _accountMgr.checkAccess(caller.getCaller(), null, true, ipAddr); diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java index 29808717299..f49ab79b500 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java @@ -63,6 +63,9 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA boolean savePasswordToRouter(Network network, NicProfile nic, VirtualMachineProfile profile, List routers) throws ResourceUnavailableException; + boolean saveSSHPublicKeyToRouter(Network network, NicProfile nic, VirtualMachineProfile profile, + List routers, String SSHPublicKey) throws ResourceUnavailableException; + boolean saveUserDataToRouter(Network network, NicProfile nic, VirtualMachineProfile profile, List routers) throws ResourceUnavailableException; diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index e1c78e1a4e7..132dbaa17cd 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -40,8 +40,8 @@ import java.util.concurrent.TimeUnit; import javax.ejb.Local; import javax.naming.ConfigurationException; - import org.apache.cloudstack.api.command.admin.router.UpgradeRouterCmd; +import com.cloud.agent.api.to.*; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -474,6 +474,28 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian }); } + @Override + public boolean saveSSHPublicKeyToRouter(Network network, final NicProfile nic, VirtualMachineProfile profile, List routers, final String SSHPublicKey) throws ResourceUnavailableException { + _userVmDao.loadDetails((UserVmVO) profile.getVirtualMachine()); + + final VirtualMachineProfile updatedProfile = profile; + + return applyRules(network, routers, "save SSHkey entry", false, null, false, new RuleApplier() { + @Override + public boolean execute(Network network, VirtualRouter router) throws ResourceUnavailableException { + // for basic zone, send vm data/password information only to the router in the same pod + Commands cmds = new Commands(OnError.Stop); + NicVO nicVo = _nicDao.findById(nic.getId()); + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(updatedProfile.getTemplateId()); + if(template != null && template.getEnablePassword()) { + createPasswordCommand(router, updatedProfile, nicVo, cmds); + } + createVmDataCommand(router, updatedProfile.getVirtualMachine(), nicVo, SSHPublicKey, cmds); + return sendCommandsToRouter(router, cmds); + } + }); + } + @Override public boolean saveUserDataToRouter(Network network, final NicProfile nic, VirtualMachineProfile profile, List routers) throws ResourceUnavailableException { _userVmDao.loadDetails((UserVmVO) profile.getVirtualMachine()); @@ -2202,13 +2224,25 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian s_logger.debug("Resending ipAssoc, port forwarding, load balancing rules as a part of Virtual router start"); ArrayList publicIps = getPublicIpsToApply(router, provider, guestNetworkId); + List firewallRulesEgress = new ArrayList(); + + // Fetch firewall Egress rules. + if (_networkModel.isProviderSupportServiceInNetwork(guestNetworkId, Service.Firewall, provider)) { + firewallRulesEgress.addAll(_rulesDao.listByNetworkPurposeTrafficType(guestNetworkId, Purpose.Firewall,FirewallRule.TrafficType.Egress)); + } + + // Re-apply firewall Egress rules + s_logger.debug("Found " + firewallRulesEgress.size() + " firewall Egress rule(s) to apply as a part of domR " + router + " start."); + if (!firewallRulesEgress.isEmpty()) { + createFirewallRulesCommands(firewallRulesEgress, router, cmds, guestNetworkId); + } if (publicIps != null && !publicIps.isEmpty()) { List vpns = new ArrayList(); List pfRules = new ArrayList(); List staticNatFirewallRules = new ArrayList(); List staticNats = new ArrayList(); - List firewallRules = new ArrayList(); + List firewallRulesIngress = new ArrayList(); //Get information about all the rules (StaticNats and StaticNatRules; PFVPN to reapply on domR start) for (PublicIpAddress ip : publicIps) { @@ -2219,7 +2253,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian staticNatFirewallRules.addAll(_rulesDao.listByIpAndPurpose(ip.getId(), Purpose.StaticNat)); } if (_networkModel.isProviderSupportServiceInNetwork(guestNetworkId, Service.Firewall, provider)) { - firewallRules.addAll(_rulesDao.listByIpAndPurpose(ip.getId(), Purpose.Firewall)); + firewallRulesIngress.addAll(_rulesDao.listByIpAndPurpose(ip.getId(), Purpose.Firewall)); } if (_networkModel.isProviderSupportServiceInNetwork(guestNetworkId, Service.Vpn, provider)) { @@ -2237,17 +2271,17 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } } } - - //Re-apply static nats + + // Re-apply static nats s_logger.debug("Found " + staticNats.size() + " static nat(s) to apply as a part of domR " + router + " start."); if (!staticNats.isEmpty()) { createApplyStaticNatCommands(staticNats, router, cmds, guestNetworkId); } - - //Re-apply firewall rules - s_logger.debug("Found " + staticNats.size() + " firewall rule(s) to apply as a part of domR " + router + " start."); - if (!firewallRules.isEmpty()) { - createFirewallRulesCommands(firewallRules, router, cmds, guestNetworkId); + + // Re-apply firewall Ingress rules + s_logger.debug("Found " + firewallRulesIngress.size() + " firewall Ingress rule(s) to apply as a part of domR " + router + " start."); + if (!firewallRulesIngress.isEmpty()) { + createFirewallRulesCommands(firewallRulesIngress, router, cmds, guestNetworkId); } // Re-apply port forwarding rules @@ -2904,7 +2938,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian int srcPort = rule.getSourcePortStart(); List destinations = rule.getDestinations(); List stickinessPolicies = rule.getStickinessPolicies(); - LoadBalancerTO lb = new LoadBalancerTO(uuid, srcIp, srcPort, protocol, algorithm, revoked, false, false, destinations, stickinessPolicies); + LoadBalancerTO lb = new LoadBalancerTO(uuid, srcIp, srcPort, protocol, algorithm, revoked, false, inline, destinations, stickinessPolicies); lbs[i++] = lb; } String routerPublicIp = null; @@ -3232,9 +3266,17 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian if (rules != null) { rulesTO = new ArrayList(); for (FirewallRule rule : rules) { - IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId()); - FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, sourceIp.getAddress().addr()); - rulesTO.add(ruleTO); + FirewallRule.TrafficType traffictype = rule.getTrafficType(); + if(traffictype == FirewallRule.TrafficType.Ingress){ + IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId()); + FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, sourceIp.getAddress().addr(),Purpose.Firewall,traffictype); + rulesTO.add(ruleTO); + } + else if (rule.getTrafficType() == FirewallRule.TrafficType.Egress){ + assert (rule.getSourceIpAddressId()==null) : "ipAddressId should be null for egress firewall rule. "; + FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null,"",Purpose.Firewall,traffictype); + rulesTO.add(ruleTO); + } } } diff --git a/server/src/com/cloud/network/rules/FirewallManager.java b/server/src/com/cloud/network/rules/FirewallManager.java index 8473c56f2b9..25cce7c4b24 100644 --- a/server/src/com/cloud/network/rules/FirewallManager.java +++ b/server/src/com/cloud/network/rules/FirewallManager.java @@ -45,7 +45,7 @@ public interface FirewallManager extends FirewallService { void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflictException; void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, String proto, - Purpose purpose, FirewallRuleType type); + Purpose purpose, FirewallRuleType type, Long networkid, FirewallRule.TrafficType trafficType); boolean applyRules(List rules, boolean continueOnError, boolean updateRulesInDB) throws ResourceUnavailableException; @@ -68,8 +68,8 @@ public interface FirewallManager extends FirewallService { */ boolean revokeFirewallRule(long ruleId, boolean apply, Account caller, long userId); - FirewallRule createFirewallRule(long ipAddrId, Account caller, String xId, Integer portStart, Integer portEnd, String protocol, List sourceCidrList, Integer icmpCode, Integer icmpType, Long relatedRuleId, - FirewallRule.FirewallRuleType type, long networkId) + FirewallRule createFirewallRule(Long ipAddrId, Account caller, String xId, Integer portStart, Integer portEnd, String protocol, List sourceCidrList, Integer icmpCode, Integer icmpType, Long relatedRuleId, + FirewallRule.FirewallRuleType type, Long networkId, FirewallRule.TrafficType traffictype) throws NetworkRuleConflictException; FirewallRule createRuleForAllCidrs(long ipAddrId, Account caller, Integer startPort, Integer endPort, String protocol, Integer icmpCode, Integer icmpType, Long relatedRuleId, long networkId) throws NetworkRuleConflictException; diff --git a/server/src/com/cloud/network/rules/FirewallRuleVO.java b/server/src/com/cloud/network/rules/FirewallRuleVO.java index 63ace5baa6e..e4936738fac 100644 --- a/server/src/com/cloud/network/rules/FirewallRuleVO.java +++ b/server/src/com/cloud/network/rules/FirewallRuleVO.java @@ -45,7 +45,7 @@ import org.apache.cloudstack.api.InternalIdentity; @Table(name="firewall_rules") @Inheritance(strategy=InheritanceType.JOINED) @DiscriminatorColumn(name="purpose", discriminatorType=DiscriminatorType.STRING, length=32) -public class FirewallRuleVO implements FirewallRule { +public class FirewallRuleVO implements Identity, FirewallRule { protected final FirewallRulesCidrsDaoImpl _firewallRulesCidrsDao = ComponentLocator.inject(FirewallRulesCidrsDaoImpl.class); @Id @@ -87,8 +87,8 @@ public class FirewallRuleVO implements FirewallRule { Date created; @Column(name="network_id") - long networkId; - + Long networkId; + @Column(name="icmp_code") Integer icmpCode; @@ -209,11 +209,6 @@ public class FirewallRuleVO implements FirewallRule { } this.accountId = accountId; this.domainId = domainId; - - if (ipAddressId == null) { - assert (purpose == Purpose.NetworkACL) : "ipAddressId can be null for " + Purpose.NetworkACL + " only"; - } - this.sourceIpAddressId = ipAddressId; this.sourcePortStart = portStart; this.sourcePortEnd = portEnd; diff --git a/server/src/com/cloud/network/rules/RulesManagerImpl.java b/server/src/com/cloud/network/rules/RulesManagerImpl.java index 74e85962251..605ffd6e794 100755 --- a/server/src/com/cloud/network/rules/RulesManagerImpl.java +++ b/server/src/com/cloud/network/rules/RulesManagerImpl.java @@ -204,7 +204,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { try { _firewallMgr.validateFirewallRule(caller, ipAddress, rule.getSourcePortStart(), rule.getSourcePortEnd(), - rule.getProtocol(), Purpose.PortForwarding, FirewallRuleType.User); + rule.getProtocol(), Purpose.PortForwarding, FirewallRuleType.User, networkId, rule.getTrafficType()); Long accountId = ipAddress.getAllocatedToAccountId(); Long domainId = ipAddress.getAllocatedInDomainId(); @@ -323,7 +323,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { throw new NetworkRuleConflictException("Can't do static nat on ip address: " + ipAddress.getAddress()); } - _firewallMgr.validateFirewallRule(caller, ipAddress, rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(), Purpose.StaticNat, FirewallRuleType.User); + _firewallMgr.validateFirewallRule(caller, ipAddress, rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(), Purpose.StaticNat, FirewallRuleType.User,null, rule.getTrafficType() ); Long networkId = ipAddress.getAssociatedWithNetworkId(); Long accountId = ipAddress.getAllocatedToAccountId(); diff --git a/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java b/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java index 841bb9394f6..858c3624b3f 100755 --- a/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java +++ b/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java @@ -254,7 +254,7 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag //now apply vpn rules on the backend s_logger.debug("Reapplying firewall rules for ip id=" + ipId + " as a part of disable remote access vpn"); - success = _firewallMgr.applyFirewallRules(ipId, caller); + success = _firewallMgr.applyIngressFirewallRules(ipId, caller); } if (success) { @@ -368,7 +368,7 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag try { boolean firewallOpened = true; if (openFirewall) { - firewallOpened = _firewallMgr.applyFirewallRules(vpn.getServerAddressId(), caller); + firewallOpened = _firewallMgr.applyIngressFirewallRules(vpn.getServerAddressId(), caller); } if (firewallOpened) { diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index bb17633690d..0b5c53113ba 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -1510,10 +1510,11 @@ public class ManagementServerImpl implements ManagementServer { Account caller = UserContext.current().getCaller(); _accountMgr.checkAccess(caller, domain); - // domain name is unique in the cloud + // domain name is unique under the parent domain if (domainName != null) { SearchCriteria sc = _domainDao.createSearchCriteria(); sc.addAnd("name", SearchCriteria.Op.EQ, domainName); + sc.addAnd("parent", SearchCriteria.Op.EQ, domain.getParent()); List domains = _domainDao.search(sc, null); boolean sameDomain = (domains.size() == 1 && domains.get(0).getId() == domainId); diff --git a/server/src/com/cloud/upgrade/dao/Upgrade40to41.java b/server/src/com/cloud/upgrade/dao/Upgrade40to41.java index cd9e20c6a46..d3a8cd5a9d3 100644 --- a/server/src/com/cloud/upgrade/dao/Upgrade40to41.java +++ b/server/src/com/cloud/upgrade/dao/Upgrade40to41.java @@ -22,12 +22,22 @@ import com.cloud.utils.script.Script; import java.io.File; import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +import org.apache.log4j.Logger; /** * @author htrippaers * */ public class Upgrade40to41 implements DbUpgrade { + final static Logger s_logger = Logger.getLogger(Upgrade40to41.class); /** * @@ -78,7 +88,8 @@ public class Upgrade40to41 implements DbUpgrade { */ @Override public void performDataMigration(Connection conn) { - + upgradeEIPNetworkOfferings(conn); + upgradeEgressFirewallRules(conn); } /* (non-Javadoc) @@ -89,4 +100,104 @@ public class Upgrade40to41 implements DbUpgrade { return new File[0]; } + private void upgradeEIPNetworkOfferings(Connection conn) { + PreparedStatement pstmt = null; + ResultSet rs = null; + + try { + pstmt = conn.prepareStatement("select id, elastic_ip_service from `cloud`.`network_offerings` where traffic_type='Guest'"); + rs = pstmt.executeQuery(); + while (rs.next()) { + long id = rs.getLong(1); + // check if elastic IP service is enabled for network offering + if (rs.getLong(2) != 0) { + //update network offering with eip_associate_public_ip set to true + pstmt = conn.prepareStatement("UPDATE `cloud`.`network_offerings` set eip_associate_public_ip=? where id=?"); + pstmt.setBoolean(1, true); + pstmt.setLong(2, id); + pstmt.executeUpdate(); + } + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to set elastic_ip_service for network offerings with EIP service enabled.", e); + } finally { + try { + if (rs != null) { + rs.close(); + } + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + } + } + } + + + private void upgradeEgressFirewallRules(Connection conn) { + PreparedStatement pstmt = null; + ResultSet rs = null; + ResultSet rsId = null; + ResultSet rsNw = null; + try { + // update the existing ingress rules traffic type + pstmt = conn.prepareStatement("update `cloud`.`firewall_rules` set traffic_type='Ingress' where purpose='Firewall' and ip_address_id is not null and traffic_type is null"); + s_logger.debug("Updating firewall Ingress rule traffic type: " + pstmt); + pstmt.executeUpdate(); + + pstmt = conn.prepareStatement("select network_id FROM `cloud`.`ntwk_service_map` where service='Firewall' and provider='VirtualRouter' "); + rs = pstmt.executeQuery(); + while (rs.next()) { + long netId = rs.getLong(1); + //When upgraded from 2.2.14 to 3.0.6 guest_type is updated to Isolated in the 2214to30 clean up sql. clean up executes + //after this. So checking for Isolated OR Virtual + pstmt = conn.prepareStatement("select account_id, domain_id FROM `cloud`.`networks` where (guest_type='Isolated' OR guest_type='Virtual') and traffic_type='Guest' and vpc_id is NULL and (state='implemented' OR state='Shutdown') and id=? "); + pstmt.setLong(1, netId); + s_logger.debug("Getting account_id, domain_id from networks table: " + pstmt); + rsNw = pstmt.executeQuery(); + + if(rsNw.next()) { + long accountId = rsNw.getLong(1); + long domainId = rsNw.getLong(2); + + //Add new rule for the existing networks + s_logger.debug("Adding default egress firewall rule for network " + netId); + pstmt = conn.prepareStatement("INSERT INTO firewall_rules (uuid, state, protocol, purpose, account_id, domain_id, network_id, xid, created, traffic_type) VALUES (?, 'Active', 'all', 'Firewall', ?, ?, ?, ?, now(), 'Egress')"); + pstmt.setString(1, UUID.randomUUID().toString()); + pstmt.setLong(2, accountId); + pstmt.setLong(3, domainId); + pstmt.setLong(4, netId); + pstmt.setString(5, UUID.randomUUID().toString()); + s_logger.debug("Inserting default egress firewall rule " + pstmt); + pstmt.executeUpdate(); + + pstmt = conn.prepareStatement("select id from firewall_rules where protocol='all' and network_id=?"); + pstmt.setLong(1, netId); + rsId = pstmt.executeQuery(); + + long firewallRuleId; + if(rsId.next()) { + firewallRuleId = rsId.getLong(1); + pstmt = conn.prepareStatement("insert into firewall_rules_cidrs (firewall_rule_id,source_cidr) values (?, '0.0.0.0/0')"); + pstmt.setLong(1, firewallRuleId); + s_logger.debug("Inserting rule for cidr 0.0.0.0/0 for the new Firewall rule id=" + firewallRuleId + " with statement " + pstmt); + pstmt.executeUpdate(); + } + } + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to set egress firewall rules ", e); + } finally { + try { + if (rs != null) { + rs.close(); + } + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + } + } + } + } diff --git a/server/src/com/cloud/user/DomainManagerImpl.java b/server/src/com/cloud/user/DomainManagerImpl.java index 54ca2ac44bd..791f58174e2 100644 --- a/server/src/com/cloud/user/DomainManagerImpl.java +++ b/server/src/com/cloud/user/DomainManagerImpl.java @@ -368,7 +368,9 @@ public class DomainManagerImpl implements DomainManager, DomainService, Manager } _accountMgr.checkAccess(caller, domain); } else { - domainId = caller.getDomainId(); + if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { + domainId = caller.getDomainId(); + } if (listAll) { isRecursive = true; } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 68b2a29de6d..969539c0b15 100644 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -17,11 +17,16 @@ package com.cloud.vm; import com.cloud.agent.AgentManager; +import com.cloud.agent.AgentManager.OnError; import com.cloud.agent.api.*; import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; +import com.cloud.agent.api.PlugNicAnswer; +import com.cloud.agent.api.PlugNicCommand; +import com.cloud.agent.api.UnPlugNicAnswer; +import com.cloud.agent.api.UnPlugNicCommand; import com.cloud.agent.manager.Commands; import com.cloud.alert.AlertManager; import com.cloud.api.ApiDBUtils; @@ -401,6 +406,116 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager } } + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_RESETSSHKEY, eventDescription = "resetting Vm SSHKey", async = true) + public UserVm resetVMSSHKey(ResetVMSSHKeyCmd cmd) + throws ResourceUnavailableException, InsufficientCapacityException { + + Account caller = UserContext.current().getCaller(); + Account owner = _accountMgr.finalizeOwner(caller, cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId()); + Long vmId = cmd.getId(); + + UserVmVO userVm = _vmDao.findById(cmd.getId()); + _vmDao.loadDetails(userVm); + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(userVm.getTemplateId()); + + // Do parameters input validation + + if (userVm == null) { + throw new InvalidParameterValueException("unable to find a virtual machine by id" + cmd.getId()); + } + + if (userVm.getState() == State.Error || userVm.getState() == State.Expunging) { + s_logger.error("vm is not in the right state: " + vmId); + throw new InvalidParameterValueException("Vm with specified id is not in the right state"); + } + if (userVm.getState() != State.Stopped) { + s_logger.error("vm is not in the right state: " + vmId); + throw new InvalidParameterValueException("Vm " + userVm + " should be stopped to do SSH Key reset"); + } + + SSHKeyPairVO s = _sshKeyPairDao.findByName(owner.getAccountId(), owner.getDomainId(), cmd.getName()); + if (s == null) { + throw new InvalidParameterValueException("A key pair with name '" + cmd.getName() + "' does not exist for account " + owner.getAccountName() + " in specified domain id"); + } + + _accountMgr.checkAccess(caller, null, true, userVm); + String password = null; + String sshPublicKey = s.getPublicKey(); + if (template != null && template.getEnablePassword()) { + password = generateRandomPassword(); + } + + boolean result = resetVMSSHKeyInternal(vmId, sshPublicKey, password); + + if (result) { + userVm.setDetail("SSH.PublicKey", sshPublicKey); + if (template != null && template.getEnablePassword()) { + userVm.setPassword(password); + //update the encrypted password in vm_details table too + if (sshPublicKey != null && !sshPublicKey.equals("") && password != null && !password.equals("saved_password")) { + String encryptedPasswd = RSAHelper.encryptWithSSHPublicKey(sshPublicKey, password); + if (encryptedPasswd == null) { + throw new CloudRuntimeException("Error encrypting password"); + } + userVm.setDetail("Encrypted.Password", encryptedPasswd); + } + } + _vmDao.saveDetails(userVm); + } else { + throw new CloudRuntimeException("Failed to reset SSH Key for the virtual machine "); + } + return userVm; + } + + private boolean resetVMSSHKeyInternal(Long vmId, String SSHPublicKey, String password) throws ResourceUnavailableException, InsufficientCapacityException { + Long userId = UserContext.current().getCallerUserId(); + VMInstanceVO vmInstance = _vmDao.findById(vmId); + + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId()); + Nic defaultNic = _networkModel.getDefaultNic(vmId); + if (defaultNic == null) { + s_logger.error("Unable to reset SSH Key for vm " + vmInstance + " as the instance doesn't have default nic"); + return false; + } + + Network defaultNetwork = _networkDao.findById(defaultNic.getNetworkId()); + NicProfile defaultNicProfile = new NicProfile(defaultNic, defaultNetwork, null, null, null, + _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork), + _networkModel.getNetworkTag(template.getHypervisorType(), defaultNetwork)); + + VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vmInstance); + + if (template != null && template.getEnablePassword()) { + vmProfile.setParameter(VirtualMachineProfile.Param.VmPassword, password); + } + + UserDataServiceProvider element = _networkMgr.getSSHKeyResetProvider(defaultNetwork); + if (element == null) { + throw new CloudRuntimeException("Can't find network element for " + Service.UserData.getName() + " provider needed for SSH Key reset"); + } + boolean result = element.saveSSHKey(defaultNetwork, defaultNicProfile, vmProfile, SSHPublicKey); + + // Need to reboot the virtual machine so that the password gets redownloaded from the DomR, and reset on the VM + if (!result) { + s_logger.debug("Failed to reset SSH Key for the virutal machine; no need to reboot the vm"); + return false; + } else { + if (vmInstance.getState() == State.Stopped) { + s_logger.debug("Vm " + vmInstance + " is stopped, not rebooting it as a part of SSH Key reset"); + return true; + } + if (rebootVirtualMachine(userId, vmId) == null) { + s_logger.warn("Failed to reboot the vm " + vmInstance); + return false; + } else { + s_logger.debug("Vm " + vmInstance + " is rebooted successfully as a part of SSH Key reset"); + return true; + } + } + } + + @Override public boolean stopVirtualMachine(long userId, long vmId) { boolean status = false; @@ -916,6 +1031,237 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager return _vmDao.findById(vmInstance.getId()); } + @Override + public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, CloudRuntimeException { + Long vmId = cmd.getVmId(); + Long networkId = cmd.getNetworkId(); + String ipAddress = cmd.getIpAddress(); + Account caller = UserContext.current().getCaller(); + + UserVmVO vmInstance = _vmDao.findById(vmId); + if(vmInstance == null) { + throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); + } + NetworkVO network = _networkDao.findById(networkId); + if(network == null) { + throw new InvalidParameterValueException("unable to find a network with id " + networkId); + } + NicProfile profile = new NicProfile(null); + if(ipAddress != null) { + profile = new NicProfile(ipAddress); + } + + // Perform permission check on VM + _accountMgr.checkAccess(caller, null, true, vmInstance); + + // Verify that zone is not Basic + DataCenterVO dc = _dcDao.findById(vmInstance.getDataCenterIdToDeployIn()); + if (dc.getNetworkType() == DataCenter.NetworkType.Basic) { + throw new CloudRuntimeException("Zone " + vmInstance.getDataCenterIdToDeployIn() + ", has a NetworkType of Basic. Can't add a new NIC to a VM on a Basic Network"); + } + + // Perform account permission check on network + if (network.getGuestType() != Network.GuestType.Shared) { + // Check account permissions + List networkMap = _networkDao.listBy(caller.getId(), network.getId()); + if ((networkMap == null || networkMap.isEmpty() ) && caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { + throw new PermissionDeniedException("Unable to modify a vm using network with id " + network.getId() + ", permission denied"); + } + } + + //ensure network belongs in zone + if (network.getDataCenterId() != vmInstance.getDataCenterIdToDeployIn()) { + throw new CloudRuntimeException(vmInstance + " is in zone:" + vmInstance.getDataCenterIdToDeployIn() + " but " + network + " is in zone:" + network.getDataCenterId()); + } + + if(_networkModel.getNicInNetwork(vmInstance.getId(),network.getId()) != null){ + s_logger.debug(vmInstance + " already in " + network + " going to add another NIC"); + } else { + //* get all vms hostNames in the network + List hostNames = _vmInstanceDao.listDistinctHostNames(network.getId()); + //* verify that there are no duplicates + if (hostNames.contains(vmInstance.getHostName())) { + throw new CloudRuntimeException(network + " already has a vm with host name: '" + vmInstance.getHostName()); + } + } + + NicProfile guestNic = null; + + try { + guestNic = _itMgr.addVmToNetwork(vmInstance, network, profile); + } catch (ResourceUnavailableException e) { + throw new CloudRuntimeException("Unable to add NIC to " + vmInstance + ": " + e); + } catch (InsufficientCapacityException e) { + throw new CloudRuntimeException("Insufficient capacity when adding NIC to " + vmInstance + ": " + e); + } catch (ConcurrentOperationException e) { + throw new CloudRuntimeException("Concurrent operations on adding NIC to " + vmInstance + ": " +e); + } + if (guestNic == null) { + throw new CloudRuntimeException("Unable to add NIC to " + vmInstance); + } + + s_logger.debug("Successful addition of " + network + " from " + vmInstance); + return _vmDao.findById(vmInstance.getId()); + } + + @Override + public UserVm removeNicFromVirtualMachine(RemoveNicFromVMCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, CloudRuntimeException { + Long vmId = cmd.getVmId(); + Long nicId = cmd.getNicId(); + Account caller = UserContext.current().getCaller(); + + UserVmVO vmInstance = _vmDao.findById(vmId); + if(vmInstance == null) { + throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); + } + NicVO nic = _nicDao.findById(nicId); + if (nic == null){ + throw new InvalidParameterValueException("unable to find a nic with id " + nicId); + } + NetworkVO network = _networkDao.findById(nic.getNetworkId()); + if(network == null) { + throw new InvalidParameterValueException("unable to find a network with id " + nic.getNetworkId()); + } + + // Perform permission check on VM + _accountMgr.checkAccess(caller, null, true, vmInstance); + + // Verify that zone is not Basic + DataCenterVO dc = _dcDao.findById(vmInstance.getDataCenterIdToDeployIn()); + if (dc.getNetworkType() == DataCenter.NetworkType.Basic) { + throw new CloudRuntimeException("Zone " + vmInstance.getDataCenterIdToDeployIn() + ", has a NetworkType of Basic. Can't remove a NIC from a VM on a Basic Network"); + } + + //check to see if nic is attached to VM + if (nic.getInstanceId() != vmId) { + throw new InvalidParameterValueException(nic + " is not a nic on " + vmInstance); + } + + // Perform account permission check on network + if (network.getGuestType() != Network.GuestType.Shared) { + // Check account permissions + List networkMap = _networkDao.listBy(caller.getId(), network.getId()); + if ((networkMap == null || networkMap.isEmpty() ) && caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { + throw new PermissionDeniedException("Unable to modify a vm using network with id " + network.getId() + ", permission denied"); + } + } + + boolean nicremoved = false; + + try { + nicremoved = _itMgr.removeNicFromVm(vmInstance, nic); + } catch (ResourceUnavailableException e) { + throw new CloudRuntimeException("Unable to remove " + network + " from " + vmInstance +": " + e); + + } catch (ConcurrentOperationException e) { + throw new CloudRuntimeException("Concurrent operations on removing " + network + " from " + vmInstance + ": " + e); + } + + if (!nicremoved) { + throw new CloudRuntimeException("Unable to remove " + network + " from " + vmInstance ); + } + + s_logger.debug("Successful removal of " + network + " from " + vmInstance); + return _vmDao.findById(vmInstance.getId()); + + + } + + @Override + public UserVm updateDefaultNicForVirtualMachine(UpdateDefaultNicForVMCmd cmd) throws InvalidParameterValueException, CloudRuntimeException { + Long vmId = cmd.getVmId(); + Long nicId = cmd.getNicId(); + Account caller = UserContext.current().getCaller(); + + UserVmVO vmInstance = _vmDao.findById(vmId); + if (vmInstance == null){ + throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); + } + NicVO nic = _nicDao.findById(nicId); + if (nic == null){ + throw new InvalidParameterValueException("unable to find a nic with id " + nicId); + } + NetworkVO network = _networkDao.findById(nic.getNetworkId()); + if (network == null){ + throw new InvalidParameterValueException("unable to find a network with id " + nic.getNetworkId()); + } + + // Perform permission check on VM + _accountMgr.checkAccess(caller, null, true, vmInstance); + + // Verify that zone is not Basic + DataCenterVO dc = _dcDao.findById(vmInstance.getDataCenterIdToDeployIn()); + if (dc.getNetworkType() == DataCenter.NetworkType.Basic) { + throw new CloudRuntimeException("Zone " + vmInstance.getDataCenterIdToDeployIn() + ", has a NetworkType of Basic. Can't change default NIC on a Basic Network"); + } + + // no need to check permissions for network, we'll enumerate the ones they already have access to + Network existingdefaultnet = _networkModel.getDefaultNetworkForVm(vmId); + + //check to see if nic is attached to VM + if (nic.getInstanceId() != vmId) { + throw new InvalidParameterValueException(nic + " is not a nic on " + vmInstance); + } + // if current default equals chosen new default, Throw an exception + if (nic.isDefaultNic()){ + throw new CloudRuntimeException("refusing to set default nic because chosen nic is already the default"); + } + + //make sure the VM is Running or Stopped + if ((vmInstance.getState() != State.Running) && (vmInstance.getState() != State.Stopped)) { + throw new CloudRuntimeException("refusing to set default " + vmInstance + " is not Running or Stopped"); + } + + NicProfile existing = null; + List nicProfiles = _networkMgr.getNicProfiles(vmInstance); + for (NicProfile nicProfile : nicProfiles) { + if(nicProfile.isDefaultNic() && nicProfile.getNetworkId() == existingdefaultnet.getId()){ + existing = nicProfile; + continue; + } + } + + if (existing == null){ + s_logger.warn("Failed to update default nic, no nic profile found for existing default network"); + throw new CloudRuntimeException("Failed to find a nic profile for the existing default network. This is bad and probably means some sort of configuration corruption"); + } + + NicVO existingVO = _nicDao.findById(existing.id); + Integer chosenID = nic.getDeviceId(); + Integer existingID = existing.getDeviceId(); + + nic.setDefaultNic(true); + nic.setDeviceId(existingID); + existingVO.setDefaultNic(false); + existingVO.setDeviceId(chosenID); + + nic = _nicDao.persist(nic); + existingVO = _nicDao.persist(existingVO); + + Network newdefault = null; + newdefault = _networkModel.getDefaultNetworkForVm(vmId); + + if (newdefault == null){ + nic.setDefaultNic(false); + nic.setDeviceId(chosenID); + existingVO.setDefaultNic(true); + existingVO.setDeviceId(existingID); + + nic = _nicDao.persist(nic); + existingVO = _nicDao.persist(existingVO); + + newdefault = _networkModel.getDefaultNetworkForVm(vmId); + if (newdefault.getId() == existingdefaultnet.getId()) { + throw new CloudRuntimeException("Setting a default nic failed, and we had no default nic, but we were able to set it back to the original"); + } + throw new CloudRuntimeException("Failed to change default nic to " + nic + " and now we have no default"); + } else if (newdefault.getId() == nic.getNetworkId()) { + s_logger.debug("successfully set default network to " + network + " for " + vmInstance); + return _vmDao.findById(vmInstance.getId()); + } + + throw new CloudRuntimeException("something strange happened, new default network(" + newdefault.getId() + ") is not null, and is not equal to the network(" + nic.getNetworkId() + ") of the chosen nic"); + } @Override public HashMap getVirtualMachineStatistics(long hostId, String hostName, List vmIds) throws CloudRuntimeException { @@ -1069,14 +1415,10 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager String time = configs.get("expunge.interval"); _expungeInterval = NumbersUtil.parseInt(time, 86400); - if ( _expungeInterval < 600 ) { - _expungeInterval = 600; - } + time = configs.get("expunge.delay"); _expungeDelay = NumbersUtil.parseInt(time, _expungeInterval); - if ( _expungeDelay < 600 ) { - _expungeDelay = 600; - } + _executor = Executors.newScheduledThreadPool(wrks, new NamedThreadFactory("UserVm-Scavenger")); _itMgr.registerGuru(VirtualMachine.Type.User, this); @@ -1594,14 +1936,14 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager } // used for vm transitioning to error state - private void updateVmStateForFailedVmCreation(Long vmId) { + private void updateVmStateForFailedVmCreation(Long vmId, Long hostId) { UserVmVO vm = _vmDao.findById(vmId); if (vm != null) { if (vm.getState().equals(State.Stopped)) { - s_logger.debug("Destroying vm " + vm + " as it failed to create"); + s_logger.debug("Destroying vm " + vm + " as it failed to create on Host with Id:" + hostId); try { _itMgr.stateTransitTo(vm, VirtualMachine.Event.OperationFailedToError, null); } catch (NoTransitionException e1) { @@ -1619,7 +1961,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager s_logger.warn("Unable to delete volume:" + volume.getId() + " for vm:" + vmId + " whilst transitioning to error state"); } } - String msg = "Failed to deploy Vm with Id: " + vmId; + String msg = "Failed to deploy Vm with Id: " + vmId + ", on Host with Id: " + hostId; _alertMgr.sendAlert(AlertManager.ALERT_TYPE_USERVM, vm.getDataCenterIdToDeployIn(), vm.getPodIdToDeployIn(), msg, msg); _resourceLimitMgr.decrementResourceCount(vm.getAccountId(), ResourceType.user_vm); @@ -2552,9 +2894,9 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager Pair> vmParamPair = null; try { vmParamPair = startVirtualMachine(vmId, hostId, additonalParams); - vm = vmParamPair.first();; + vm = vmParamPair.first(); } finally { - updateVmStateForFailedVmCreation(vm.getId()); + updateVmStateForFailedVmCreation(vm.getId(), hostId); } // Check that the password was passed in and is valid @@ -3680,16 +4022,58 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager public boolean plugNic(Network network, NicTO nic, VirtualMachineTO vm, ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - //not supported - throw new UnsupportedOperationException("Plug nic is not supported for vm of type " + vm.getType()); + UserVmVO vmVO = _vmDao.findById(vm.getId()); + if (vmVO.getState() == State.Running) { + try { + PlugNicCommand plugNicCmd = new PlugNicCommand(nic,vm.getName()); + Commands cmds = new Commands(OnError.Stop); + cmds.addCommand("plugnic",plugNicCmd); + _agentMgr.send(dest.getHost().getId(),cmds); + PlugNicAnswer plugNicAnswer = cmds.getAnswer(PlugNicAnswer.class); + if (!(plugNicAnswer != null && plugNicAnswer.getResult())) { + s_logger.warn("Unable to plug nic for " + vmVO); + return false; + } + } catch (OperationTimedoutException e) { + throw new AgentUnavailableException("Unable to plug nic for " + vmVO + " in network " + network, dest.getHost().getId(), e); + } + } else if (vmVO.getState() == State.Stopped || vmVO.getState() == State.Stopping) { + s_logger.warn(vmVO + " is Stopped, not sending PlugNicCommand. Currently " + vmVO.getState()); + } else { + s_logger.warn("Unable to plug nic, " + vmVO + " is not in the right state " + vmVO.getState()); + throw new ResourceUnavailableException("Unable to plug nic on the backend," + + vmVO + " is not in the right state", DataCenter.class, vmVO.getDataCenterIdToDeployIn()); + } + return true; } @Override public boolean unplugNic(Network network, NicTO nic, VirtualMachineTO vm, ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException { - //not supported - throw new UnsupportedOperationException("Unplug nic is not supported for vm of type " + vm.getType()); + UserVmVO vmVO = _vmDao.findById(vm.getId()); + if (vmVO.getState() == State.Running) { + try { + UnPlugNicCommand unplugNicCmd = new UnPlugNicCommand(nic,vm.getName()); + Commands cmds = new Commands(OnError.Stop); + cmds.addCommand("unplugnic",unplugNicCmd); + _agentMgr.send(dest.getHost().getId(),cmds); + UnPlugNicAnswer unplugNicAnswer = cmds.getAnswer(UnPlugNicAnswer.class); + if (!(unplugNicAnswer != null && unplugNicAnswer.getResult())) { + s_logger.warn("Unable to unplug nic for " + vmVO); + return false; + } + } catch (OperationTimedoutException e) { + throw new AgentUnavailableException("Unable to unplug nic for " + vmVO + " in network " + network, dest.getHost().getId(), e); + } + } else if (vmVO.getState() == State.Stopped || vmVO.getState() == State.Stopping) { + s_logger.warn(vmVO + " is Stopped, not sending UnPlugNicCommand. Currently " + vmVO.getState()); + } else { + s_logger.warn("Unable to unplug nic, " + vmVO + " is not in the right state " + vmVO.getState()); + throw new ResourceUnavailableException("Unable to unplug nic on the backend," + + vmVO + " is not in the right state", DataCenter.class, vmVO.getDataCenterIdToDeployIn()); + } + return true; } @Override diff --git a/server/src/com/cloud/vm/VirtualMachineManager.java b/server/src/com/cloud/vm/VirtualMachineManager.java index 4f04617baba..53bd442f33b 100644 --- a/server/src/com/cloud/vm/VirtualMachineManager.java +++ b/server/src/com/cloud/vm/VirtualMachineManager.java @@ -152,6 +152,15 @@ public interface VirtualMachineManager extends Manager { NicProfile addVmToNetwork(VirtualMachine vm, Network network, NicProfile requested) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException; + /** + * @param vm + * @param nic + * @return + * @throws ResourceUnavailableException + * @throws ConcurrentOperationException + */ + boolean removeNicFromVm(VirtualMachine vm, NicVO nic) throws ConcurrentOperationException, ResourceUnavailableException; + /** * @param vm * @param network diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index e0647bbe67f..19756addd5f 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -2462,7 +2462,12 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene ResourceUnavailableException, InsufficientCapacityException { s_logger.debug("Adding vm " + vm + " to network " + network + "; requested nic profile " + requested); - VMInstanceVO vmVO = _vmDao.findById(vm.getId()); + VMInstanceVO vmVO; + if (vm.getType() == VirtualMachine.Type.User) { + vmVO = _userVmDao.findById(vm.getId()); + } else { + vmVO = _vmDao.findById(vm.getId()); + } ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM)); @@ -2506,7 +2511,6 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } } - @Override public NicTO toNicTO(NicProfile nic, HypervisorType hypervisorType) { HypervisorGuru hvGuru = _hvGuruMgr.getGuru(hypervisorType); @@ -2515,6 +2519,61 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene return nicTO; } + @Override + public boolean removeNicFromVm(VirtualMachine vm, NicVO nic) throws ConcurrentOperationException, ResourceUnavailableException { + VMInstanceVO vmVO = _vmDao.findById(vm.getId()); + NetworkVO network = _networkDao.findById(nic.getNetworkId()); + ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), + _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM)); + + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, + null, null, null); + + DataCenter dc = _configMgr.getZone(network.getDataCenterId()); + Host host = _hostDao.findById(vm.getHostId()); + DeployDestination dest = new DeployDestination(dc, null, null, host); + VirtualMachineGuru vmGuru = getVmGuru(vmVO); + HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vmProfile.getVirtualMachine().getHypervisorType()); + VirtualMachineTO vmTO = hvGuru.implement(vmProfile); + + // don't delete default NIC on a user VM + if (nic.isDefaultNic() && vm.getType() == VirtualMachine.Type.User ) { + s_logger.warn("Failed to remove nic from " + vm + " in " + network + ", nic is default."); + throw new CloudRuntimeException("Failed to remove nic from " + vm + " in " + network + ", nic is default."); + } + + NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), + _networkModel.getNetworkRate(network.getId(), vm.getId()), + _networkModel.isSecurityGroupSupportedInNetwork(network), + _networkModel.getNetworkTag(vmProfile.getVirtualMachine().getHypervisorType(), network)); + + //1) Unplug the nic + if (vm.getState() == State.Running) { + NicTO nicTO = toNicTO(nicProfile, vmProfile.getVirtualMachine().getHypervisorType()); + s_logger.debug("Un-plugging nic " + nic + " for vm " + vm + " from network " + network); + boolean result = vmGuru.unplugNic(network, nicTO, vmTO, context, dest); + if (result) { + s_logger.debug("Nic is unplugged successfully for vm " + vm + " in network " + network ); + } else { + s_logger.warn("Failed to unplug nic for the vm " + vm + " from network " + network); + return false; + } + } else if (vm.getState() != State.Stopped) { + s_logger.warn("Unable to remove vm " + vm + " from network " + network); + throw new ResourceUnavailableException("Unable to remove vm " + vm + " from network, is not in the right state", + DataCenter.class, vm.getDataCenterIdToDeployIn()); + } + + //2) Release the nic + _networkMgr.releaseNic(vmProfile, nic); + s_logger.debug("Successfully released nic " + nic + "for vm " + vm); + + //3) Remove the nic + _networkMgr.removeNic(vmProfile, nic); + _nicsDao.expunge(nic.getId()); + return true; + } + @Override public boolean removeVmFromNetwork(VirtualMachine vm, Network network, URI broadcastUri) throws ConcurrentOperationException, ResourceUnavailableException { VMInstanceVO vmVO = _vmDao.findById(vm.getId()); @@ -2538,21 +2597,38 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } else { nic = _networkModel.getNicInNetwork(vm.getId(), network.getId()); } + + if (nic == null){ + s_logger.warn("Could not get a nic with " + network); + return false; + } + // don't delete default NIC on a user VM + if (nic.isDefaultNic() && vm.getType() == VirtualMachine.Type.User ) { + s_logger.warn("Failed to remove nic from " + vm + " in " + network + ", nic is default."); + throw new CloudRuntimeException("Failed to remove nic from " + vm + " in " + network + ", nic is default."); + } + NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), _networkModel.getNetworkRate(network.getId(), vm.getId()), _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vmProfile.getVirtualMachine().getHypervisorType(), network)); //1) Unplug the nic - NicTO nicTO = toNicTO(nicProfile, vmProfile.getVirtualMachine().getHypervisorType()); - s_logger.debug("Un-plugging nic for vm " + vm + " from network " + network); - boolean result = vmGuru.unplugNic(network, nicTO, vmTO, context, dest); - if (result) { - s_logger.debug("Nic is unplugged successfully for vm " + vm + " in network " + network ); - } else { - s_logger.warn("Failed to unplug nic for the vm " + vm + " from network " + network); - return false; + if (vm.getState() == State.Running) { + NicTO nicTO = toNicTO(nicProfile, vmProfile.getVirtualMachine().getHypervisorType()); + s_logger.debug("Un-plugging nic for vm " + vm + " from network " + network); + boolean result = vmGuru.unplugNic(network, nicTO, vmTO, context, dest); + if (result) { + s_logger.debug("Nic is unplugged successfully for vm " + vm + " in network " + network ); + } else { + s_logger.warn("Failed to unplug nic for the vm " + vm + " from network " + network); + return false; + } + } else if (vm.getState() != State.Stopped) { + s_logger.warn("Unable to remove vm " + vm + " from network " + network); + throw new ResourceUnavailableException("Unable to remove vm " + vm + " from network, is not in the right state", + DataCenter.class, vm.getDataCenterIdToDeployIn()); } //2) Release the nic @@ -2561,7 +2637,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene //3) Remove the nic _networkMgr.removeNic(vmProfile, nic); - return result; + return true; } } diff --git a/server/test/com/cloud/network/MockFirewallManagerImpl.java b/server/test/com/cloud/network/MockFirewallManagerImpl.java index 1a79acf746b..7311d0e6744 100644 --- a/server/test/com/cloud/network/MockFirewallManagerImpl.java +++ b/server/test/com/cloud/network/MockFirewallManagerImpl.java @@ -32,6 +32,7 @@ import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.network.rules.FirewallRule.FirewallRuleType; import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.network.rules.FirewallRule.TrafficType; import com.cloud.user.Account; import com.cloud.utils.Pair; import com.cloud.utils.component.Manager; @@ -63,13 +64,6 @@ public class MockFirewallManagerImpl implements FirewallManager, return null; } - @Override - public FirewallRule createFirewallRule(FirewallRule rule) - throws NetworkRuleConflictException { - // TODO Auto-generated method stub - return null; - } - @Override public Pair, Integer> listFirewallRules( ListFirewallRulesCmd cmd) { @@ -83,13 +77,6 @@ public class MockFirewallManagerImpl implements FirewallManager, return false; } - @Override - public boolean applyFirewallRules(long ipId, Account caller) - throws ResourceUnavailableException { - // TODO Auto-generated method stub - return false; - } - @Override public FirewallRule getFirewallRule(long ruleId) { // TODO Auto-generated method stub @@ -109,14 +96,6 @@ public class MockFirewallManagerImpl implements FirewallManager, } - @Override - public void validateFirewallRule(Account caller, IPAddressVO ipAddress, - Integer portStart, Integer portEnd, String proto, Purpose purpose, - FirewallRuleType type) { - // TODO Auto-generated method stub - - } - @Override public boolean applyRules(List rules, boolean continueOnError, boolean updateRulesInDB) @@ -153,16 +132,6 @@ public class MockFirewallManagerImpl implements FirewallManager, return false; } - @Override - public FirewallRule createFirewallRule(long ipAddrId, Account caller, - String xId, Integer portStart, Integer portEnd, String protocol, - List sourceCidrList, Integer icmpCode, Integer icmpType, - Long relatedRuleId, FirewallRuleType type, long networkId) - throws NetworkRuleConflictException { - // TODO Auto-generated method stub - return null; - } - @Override public FirewallRule createRuleForAllCidrs(long ipAddrId, Account caller, Integer startPort, Integer endPort, String protocol, @@ -197,6 +166,52 @@ public class MockFirewallManagerImpl implements FirewallManager, } + @Override + public FirewallRule createFirewallRule(Long ipAddrId, Account caller, + String xId, Integer portStart, Integer portEnd, String protocol, + List sourceCidrList, Integer icmpCode, Integer icmpType, + Long relatedRuleId, FirewallRuleType type, Long networkId, + TrafficType traffictype) throws NetworkRuleConflictException { + // TODO Auto-generated method stub + return null; + } + + @Override + public void validateFirewallRule(Account caller, IPAddressVO ipAddress, + Integer portStart, Integer portEnd, String proto, Purpose purpose, + FirewallRuleType type, Long networkid, TrafficType trafficType) { + // TODO Auto-generated method stub + + } + + @Override + public boolean applyEgressFirewallRules(FirewallRule rule, Account caller) + throws ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean applyIngressFirewallRules(long Ipid, Account caller) + throws ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public FirewallRule createEgressFirewallRule(FirewallRule rule) + throws NetworkRuleConflictException { + // TODO Auto-generated method stub + return null; + } + + @Override + public FirewallRule createIngressFirewallRule(FirewallRule rule) + throws NetworkRuleConflictException { + // TODO Auto-generated method stub + return null; + } + } diff --git a/server/test/com/cloud/network/MockNetworkManagerImpl.java b/server/test/com/cloud/network/MockNetworkManagerImpl.java index ef5b9c9f9b7..e6280335c14 100755 --- a/server/test/com/cloud/network/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/network/MockNetworkManagerImpl.java @@ -417,17 +417,18 @@ public class MockNetworkManagerImpl implements NetworkManager, Manager, NetworkS return null; } - - - - @Override public UserDataServiceProvider getPasswordResetProvider(Network network) { // TODO Auto-generated method stub return null; } - + @Override + public UserDataServiceProvider getSSHKeyResetProvider(Network network) { + // TODO Auto-generated method stub + return null; + } + @Override public PhysicalNetworkServiceProvider updateNetworkServiceProvider(Long id, String state, List enabledServices) { // TODO Auto-generated method stub @@ -733,7 +734,7 @@ public class MockNetworkManagerImpl implements NetworkManager, Manager, NetworkS */ @Override public NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, - VirtualMachineProfileImpl vmProfile, boolean prepare) + VirtualMachineProfile vmProfile, boolean prepare) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { // TODO Auto-generated method stub diff --git a/server/test/com/cloud/vm/MockUserVmManagerImpl.java b/server/test/com/cloud/vm/MockUserVmManagerImpl.java index 27508b14c5b..684040cde03 100644 --- a/server/test/com/cloud/vm/MockUserVmManagerImpl.java +++ b/server/test/com/cloud/vm/MockUserVmManagerImpl.java @@ -55,6 +55,8 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.StorageUnavailableException; import com.cloud.exception.VirtualMachineMigrationException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; import com.cloud.host.Host; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.Network; @@ -69,6 +71,7 @@ import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; import com.cloud.utils.component.Manager; import com.cloud.utils.exception.ExecutionException; +import com.cloud.utils.exception.CloudRuntimeException; @Local(value = { UserVmManager.class, UserVmService.class }) public class MockUserVmManagerImpl implements UserVmManager, UserVmService, Manager { @@ -245,6 +248,12 @@ public class MockUserVmManagerImpl implements UserVmManager, UserVmService, Mana return null; } + @Override + public UserVm resetVMSSHKey(ResetVMSSHKeyCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException { + // TODO Auto-generated method stub + return null; + } + @Override public Volume attachVolumeToVM(AttachVolumeCmd cmd) { // TODO Auto-generated method stub @@ -276,6 +285,24 @@ public class MockUserVmManagerImpl implements UserVmManager, UserVmService, Mana return null; } + @Override + public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, CloudRuntimeException { + // TODO Auto-generated method stub + return null; + } + + @Override + public UserVm removeNicFromVirtualMachine(RemoveNicFromVMCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, CloudRuntimeException { + // TODO Auto-generated method stub + return null; + } + + @Override + public UserVm updateDefaultNicForVirtualMachine(UpdateDefaultNicForVMCmd cmd) throws InvalidParameterValueException, CloudRuntimeException { + // TODO Auto-generated method stub + return null; + } + @Override public UserVm recoverVirtualMachine(RecoverVMCmd cmd) throws ResourceAllocationException { // TODO Auto-generated method stub diff --git a/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java b/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java index 6723198b81a..862bb251cb0 100755 --- a/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java +++ b/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java @@ -270,6 +270,15 @@ public class MockVirtualMachineManagerImpl implements VirtualMachineManager { return null; } + /* (non-Javadoc) + * @see com.cloud.vm.VirtualMachineManager#removeVmFromNetwork(com.cloud.vm.VirtualMachine, com.cloud.network.Network, java.net.URI) + */ + @Override + public boolean removeNicFromVm(VirtualMachine vm, NicVO nic) throws ConcurrentOperationException, ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + /* (non-Javadoc) * @see com.cloud.vm.VirtualMachineManager#removeVmFromNetwork(com.cloud.vm.VirtualMachine, com.cloud.network.Network, java.net.URI) */ diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index b4e17948a43..a0f9d008f07 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -867,10 +867,6 @@ public class MockNetworkManagerImpl implements NetworkManager, NetworkService, M return false; } - - - - /* (non-Javadoc) * @see com.cloud.network.NetworkManager#getPasswordResetProvider(com.cloud.network.Network) */ @@ -880,8 +876,11 @@ public class MockNetworkManagerImpl implements NetworkManager, NetworkService, M return null; } - - + @Override + public UserDataServiceProvider getSSHKeyResetProvider(Network network) { + // TODO Auto-generated method stub + return null; + } /* (non-Javadoc) @@ -1123,11 +1122,11 @@ public class MockNetworkManagerImpl implements NetworkManager, NetworkService, M /* (non-Javadoc) - * @see com.cloud.network.NetworkManager#createNicForVm(com.cloud.network.Network, com.cloud.vm.NicProfile, com.cloud.vm.ReservationContext, com.cloud.vm.VirtualMachineProfileImpl, boolean) + * @see com.cloud.network.NetworkManager#createNicForVm(com.cloud.network.Network, com.cloud.vm.NicProfile, com.cloud.vm.ReservationContext, com.cloud.vm.VirtualMachineProfileImpl, boolean, boolean) */ @Override public NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, - VirtualMachineProfileImpl vmProfile, boolean prepare) + VirtualMachineProfile vmProfile, boolean prepare) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { // TODO Auto-generated method stub diff --git a/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java b/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java index 65ee33a65ab..29da6128084 100644 --- a/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java +++ b/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java @@ -74,6 +74,11 @@ public class MockVpcVirtualNetworkApplianceManager implements VpcVirtualNetworkA return false; } + @Override + public boolean saveSSHPublicKeyToRouter(Network network, NicProfile nic, VirtualMachineProfile profile, List routers, String SSHPublicKey) throws ResourceUnavailableException { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + /* (non-Javadoc) * @see com.cloud.network.router.VirtualNetworkApplianceManager#saveUserDataToRouter(com.cloud.network.Network, com.cloud.vm.NicProfile, com.cloud.vm.VirtualMachineProfile, java.util.List) */ diff --git a/test/integration/smoke/test_nic.py b/test/integration/smoke/test_nic.py new file mode 100644 index 00000000000..b9dfdde8fe5 --- /dev/null +++ b/test/integration/smoke/test_nic.py @@ -0,0 +1,351 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +""" NIC tests for VM """ +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.remoteSSHClient import remoteSSHClient +from marvin.integration.lib.utils import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * +from nose.plugins.attrib import attr + +import signal +import sys +import time + +class Services: + def __init__(self): + self.services = { + "disk_offering":{ + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended in create account to + # ensure unique username generated each time + "password": "password", + }, + # Create a small virtual machine instance with disk offering + "small": { + "displayname": "testserver", + "username": "root", # VM creds for SSH + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "service_offerings": { + "tiny": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 128, # In MBs + }, + }, + "network_offering": { + "name": 'Test Network offering', + "displaytext": 'Test Network offering', + "guestiptype": 'Isolated', + "supportedservices": 'Dhcp,Dns,SourceNat,PortForwarding', + "traffictype": 'GUEST', + "availability": 'Optional', + "serviceProviderList" : { + "Dhcp": 'VirtualRouter', + "Dns": 'VirtualRouter', + "SourceNat": 'VirtualRouter', + "PortForwarding": 'VirtualRouter', + }, + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + "acltype": "Account", + }, + # ISO settings for Attach/Detach ISO tests + "iso": { + "displaytext": "Test ISO", + "name": "testISO", + "url": "http://iso.linuxquestions.org/download/504/1819/http/gd4.tuwien.ac.at/dsl-4.4.10.iso", + # Source URL where ISO is located + "ostype": 'CentOS 5.3 (64-bit)', + "mode": 'HTTP_DOWNLOAD', # Downloading existing ISO + }, + "template": { + "displaytext": "Cent OS Template", + "name": "Cent OS Template", + "passwordenabled": True, + }, + "diskdevice": '/dev/xvdd', + # Disk device where ISO is attached to instance + "mount_dir": "/mnt/tmp", + "sleep": 60, + "timeout": 10, + #Migrate VM to hostid + "ostype": 'CentOS 5.3 (64-bit)', + # CentOS 5.3 (64-bit) + } + +class TestDeployVM(cloudstackTestCase): + + def setUp(self): + self.cleanup = [] + self.cleaning_up = 0 + + def signal_handler(signal, frame): + self.tearDown() + sys.exit(0) + + # assign the signal handler immediately + signal.signal(signal.SIGINT, signal_handler) + + try: + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.services = Services().services + + # Get Zone, Domain and templates + domain = get_domain(self.apiclient, self.services) + zone = get_zone(self.apiclient, self.services) + self.services['mode'] = zone.networktype + + if self.services['mode'] != 'Advanced': + self.debug("Cannot run this test with a basic zone, please use advanced!") + return + + #if local storage is enabled, alter the offerings to use localstorage + #this step is needed for devcloud + if zone.localstorageenabled == True: + self.services["service_offerings"]["tiny"]["storagetype"] = 'local' + + template = get_template( + self.apiclient, + zone.id, + self.services["ostype"] + ) + # Set Zones and disk offerings + self.services["small"]["zoneid"] = zone.id + self.services["small"]["template"] = template.id + + self.services["iso"]["zoneid"] = zone.id + self.services["network"]["zoneid"] = zone.id + + # Create Account, VMs, NAT Rules etc + self.account = Account.create( + self.apiclient, + self.services["account"], + domainid=domain.id + ) + self.cleanup.insert(0, self.account) + + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offerings"]["tiny"] + ) + self.cleanup.insert(0, self.service_offering) + + #################### + ### Network offering + self.network_offering = NetworkOffering.create( + self.apiclient, + self.services["network_offering"], + ) + self.cleanup.insert(0, self.network_offering) + self.network_offering.update(self.apiclient, state='Enabled') # Enable Network offering + self.services["network"]["networkoffering"] = self.network_offering.id + + ################ + ### Test Network + self.test_network = Network.create( + self.apiclient, + self.services["network"], + self.account.account.name, + self.account.account.domainid, + ) + self.cleanup.insert(0, self.test_network) + except Exception as ex: + self.debug("Exception during NIC test SETUP!: " + str(ex)) + self.assertEqual(True, False, "Exception during NIC test SETUP!: " + str(ex)) + + @attr(tags = ["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"]) + def test_01_nic(self): + if self.services['mode'] != 'Advanced': + self.debug("Cannot run this test with a basic zone, please use advanced!") + return + try: + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["small"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + mode=self.services['mode'] + ) + self.cleanup.insert(0, self.virtual_machine) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + + self.debug( + "Verify listVirtualMachines response for virtual machine: %s" \ + % self.virtual_machine.id + ) + + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_vm_response), + 0, + "Check VM available in List Virtual Machines" + ) + vm_response = list_vm_response[0] + + self.assertEqual( + + vm_response.id, + self.virtual_machine.id, + "Check virtual machine id in listVirtualMachines" + ) + + self.assertEqual( + vm_response.name, + self.virtual_machine.name, + "Check virtual machine name in listVirtualMachines" + ) + + self.assertEqual( + len(vm_response.nic), + 1, + "Verify we only start with one nic" + ) + + self.assertEqual( + vm_response.nic[0].isdefault, + True, + "Verify initial adapter is set to default" + ) + existing_nic_ip = vm_response.nic[0].ipaddress + existing_nic_id = vm_response.nic[0].id + + # 1. add a nic + add_response = self.virtual_machine.add_nic(self.apiclient, self.test_network.id) + + time.sleep(5) + # now go get the vm list? + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + + self.assertEqual( + len(list_vm_response[0].nic), + 2, + "Verify we have 2 NIC's now" + ) + + new_nic_id = "" + for nc in list_vm_response[0].nic: + if nc.ipaddress != existing_nic_ip: + new_nic_id = nc.id + + self.virtual_machine.update_default_nic(self.apiclient, new_nic_id) + + time.sleep(5) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + + # iterate as we don't know for sure what order our NIC's will be returned to us. + for nc in list_vm_response[0].nic: + if nc.ipaddress == existing_nic_ip: + self.assertEqual( + nc.isdefault, + False, + "Verify initial adapter is NOT set to default" + ) + else: + self.assertEqual( + nc.isdefault, + True, + "Verify second adapter is set to default" + ) + + sawException = False + try: + self.virtual_machine.remove_nic(self.apiclient, new_nic_id) + except Exception as ex: + sawException = True + + self.assertEqual(sawException, True, "Make sure we cannot delete the default NIC") + + self.virtual_machine.remove_nic(self.apiclient, existing_nic_id) + time.sleep(5) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + + self.assertEqual( + len(list_vm_response[0].nic), + 1, + "Verify we are back to a signle NIC" + ) + + return + except Exception as ex: + self.debug("Exception during NIC test!: " + str(ex)) + self.assertEqual(True, False, "Exception during NIC test!: " + str(ex)) + + def tearDown(self): + if self.services['mode'] != 'Advanced': + self.debug("Cannot run this test with a basic zone, please use advanced!") + return + + if self.cleaning_up == 1: + return + + self.cleaning_up = 1 + try: + for obj in self.cleanup: + try: + obj.delete(self.apiclient) + time.sleep(10) + except Exception as ex: + self.debug("Error deleting: " + str(obj) + ", exception: " + str(ex)) + + except Exception as e: + self.debug("Warning! Exception in tearDown: %s" % e) + self.cleaning_up = 0 + diff --git a/tools/cli/cloudmonkey/cachegen.py b/tools/cli/cloudmonkey/cachegen.py index e03b6fddf89..509c0c68587 100644 --- a/tools/cli/cloudmonkey/cachegen.py +++ b/tools/cli/cloudmonkey/cachegen.py @@ -16,11 +16,13 @@ # under the License. try: - from common import grammar + import re from marvin.cloudstackAPI import * from marvin import cloudstackAPI except ImportError, e: - pass + import sys + print "ImportError", e + sys.exit(1) completions = cloudstackAPI.__all__ @@ -43,9 +45,12 @@ def main(): completing commands and help docs. This reduces the overall search and cache_miss (computation) complexity from O(n) to O(1) for any valid cmd. """ + pattern = re.compile("[A-Z]") + verbs = list(set([x[:pattern.search(x).start()] for x in completions + if pattern.search(x) is not None]).difference(['cloudstack'])) # datastructure {'verb': {cmd': ['api', [params], doc, required=[]]}} cache_verbs = {} - for verb in grammar: + for verb in verbs: completions_found = filter(lambda x: x.startswith(verb), completions) cache_verbs[verb] = {} for api_name in completions_found: diff --git a/tools/cli/cloudmonkey/cloudmonkey.py b/tools/cli/cloudmonkey/cloudmonkey.py index eadf23ff173..646ad409993 100644 --- a/tools/cli/cloudmonkey/cloudmonkey.py +++ b/tools/cli/cloudmonkey/cloudmonkey.py @@ -20,25 +20,26 @@ try: import atexit import cmd - import clint import codecs import json import logging import os import pdb + import re import shlex import sys import time import types - from clint.textui import colored from ConfigParser import ConfigParser, SafeConfigParser from urllib2 import HTTPError, URLError from httplib import BadStatusLine from prettytable import PrettyTable - from common import __version__, config_file, config_fields - from common import grammar, precached_verbs + from common import __version__, config_dir, config_file, config_fields + from common import precached_verbs + from lexer import monkeyprint + from marvin.cloudstackConnection import cloudConnection from marvin.cloudstackException import cloudstackAPIException from marvin.cloudstackAPI import * @@ -69,44 +70,52 @@ class CloudMonkeyShell(cmd.Cmd, object): intro = ("☁ Apache CloudStack 🐵 cloudmonkey " + __version__ + ". Type help or ? to list commands.\n") ruler = "=" + config_dir = config_dir config_file = config_file - config_fields = config_fields - grammar = grammar # datastructure {'verb': {cmd': ['api', [params], doc, required=[]]}} cache_verbs = precached_verbs + config_options = [] - def __init__(self, pname): + def __init__(self, pname, verbs): self.program_name = pname + self.verbs = verbs + global config_fields + first_time = False + if not os.path.exists(self.config_dir): + os.makedirs(self.config_dir) if os.path.exists(self.config_file): config = self.read_config() else: - for key in self.config_fields.keys(): - setattr(self, key, self.config_fields[key]) - config = self.write_config() + first_time = True + config = self.write_config(first_time) + + for section in config_fields.keys(): + for key in config_fields[section].keys(): + try: + self.config_options.append(key) + setattr(self, key, config.get(section, key)) + except Exception: + print "Please fix `%s` in %s" % (key, self.config_file) + sys.exit() + + if first_time: print "Welcome! Using `set` configure the necessary settings:" - print " ".join(sorted(self.config_fields.keys())) + print " ".join(sorted(self.config_options)) print "Config file:", self.config_file print "For debugging, tail -f", self.log_file, "\n" - for key in self.config_fields.keys(): - try: - setattr(self, key, config.get('CLI', key)) - self.config_fields[key] = config.get('CLI', key) - except Exception: - print "Please fix `%s` config in %s" % (key, self.config_file) - sys.exit() - self.prompt = self.prompt.strip() + " " # Cosmetic fix for prompt + logging.basicConfig(filename=self.log_file, level=logging.DEBUG, format=log_fmt) - logger.debug("Loaded config fields:\n%s" % self.config_fields) + logger.debug("Loaded config fields:\n%s" % map(lambda x: "%s=%s" % + (x, getattr(self, x)), + self.config_options)) cmd.Cmd.__init__(self) - # Update config if config_file does not exist if not os.path.exists(self.config_file): config = self.write_config() - # Enable history support try: if os.path.exists(self.history_file): readline.read_history_file(self.history_file) @@ -123,11 +132,16 @@ class CloudMonkeyShell(cmd.Cmd, object): self.print_shell("Error: config_file not found", e) return config - def write_config(self): + def write_config(self, first_time=False): + global config_fields config = ConfigParser() - config.add_section('CLI') - for key in self.config_fields.keys(): - config.set('CLI', key, getattr(self, key)) + for section in config_fields.keys(): + config.add_section(section) + for key in config_fields[section].keys(): + if first_time: + config.set(section, key, config_fields[section][key]) + else: + config.set(section, key, getattr(self, key)) with open(self.config_file, 'w') as cfg: config.write(cfg) return config @@ -145,35 +159,19 @@ class CloudMonkeyShell(cmd.Cmd, object): print("^C") def print_shell(self, *args): + output = "" try: for arg in args: arg = str(arg) if isinstance(type(args), types.NoneType): continue - if self.color == 'true': - if str(arg).count(self.ruler) == len(str(arg)): - print colored.green(arg), - elif 'Error' in arg: - print colored.red(arg), - elif ":\n=" in arg: - print colored.red(arg), - elif ':' in arg: - print colored.blue(arg), - elif 'type' in arg: - print colored.green(arg), - elif 'state' in arg or 'count' in arg: - print colored.magenta(arg), - elif 'id =' in arg: - print colored.yellow(arg), - elif 'name =' in arg: - print colored.cyan(arg), - else: - print arg, - else: - print arg, - print + output += arg + if self.color == 'true': + monkeyprint(output) + else: + print output except Exception, e: - print colored.red("Error: "), e + self.print_shell("Error: " + e) def print_result(self, result, result_filter=None): if result is None or len(result) == 0: @@ -181,7 +179,7 @@ class CloudMonkeyShell(cmd.Cmd, object): def printer_helper(printer, toprow): if printer: - print printer + self.print_shell(printer) return PrettyTable(toprow) def print_result_tabular(result, result_filter=None): @@ -202,16 +200,16 @@ class CloudMonkeyShell(cmd.Cmd, object): if printer and row: printer.add_row(row) if printer: - print printer + self.print_shell(printer) def print_result_as_dict(result, result_filter=None): - for key in sorted(result.keys(), - key=lambda x: x != 'id' and x != 'count' and x): + for key in sorted(result.keys(), key=lambda x: + x not in ['id', 'count', 'name'] and x): if not (isinstance(result[key], list) or isinstance(result[key], dict)): self.print_shell("%s = %s" % (key, result[key])) else: - self.print_shell(key + ":\n" + len(key) * self.ruler) + self.print_shell(key + ":") self.print_result(result[key], result_filter) def print_result_as_list(result, result_filter=None): @@ -361,7 +359,7 @@ class CloudMonkeyShell(cmd.Cmd, object): command.required) if len(missing_args) > 0: - self.print_shell("Missing arguments:", ' '.join(missing_args)) + self.print_shell("Missing arguments: ", ' '.join(missing_args)) return isAsync = False @@ -381,13 +379,13 @@ class CloudMonkeyShell(cmd.Cmd, object): def completedefault(self, text, line, begidx, endidx): partitions = line.partition(" ") - verb = partitions[0] - rline = partitions[2].partition(" ") + verb = partitions[0].strip() + rline = partitions[2].lstrip().partition(" ") subject = rline[0] separator = rline[1] - params = rline[2] + params = rline[2].lstrip() - if verb not in self.grammar: + if verb not in self.verbs: return [] autocompletions = [] @@ -401,7 +399,7 @@ class CloudMonkeyShell(cmd.Cmd, object): self.cache_verbs[verb][subject][1]) search_string = text - if self.tabularize == "true": + if self.tabularize == "true" and subject != "": autocompletions.append("filter=") return [s for s in autocompletions if s.startswith(search_string)] @@ -436,13 +434,13 @@ class CloudMonkeyShell(cmd.Cmd, object): args = args.strip().partition(" ") key, value = (args[0], args[2]) setattr(self, key, value) # keys and attributes should have same names - self.prompt = self.prompt.strip() + " " # prompt fix + self.prompt = self.prompt.strip() + " " # prompt fix self.write_config() def complete_set(self, text, line, begidx, endidx): mline = line.partition(" ")[2] offs = len(mline) - len(text) - return [s[offs:] for s in self.config_fields.keys() + return [s[offs:] for s in self.config_options if s.startswith(mline)] def do_shell(self, args): @@ -513,22 +511,22 @@ class CloudMonkeyShell(cmd.Cmd, object): def main(): - # Create handlers on the fly using closures - self = CloudMonkeyShell - global grammar - for rule in grammar: - def add_grammar(rule): + pattern = re.compile("[A-Z]") + verbs = list(set([x[:pattern.search(x).start()] for x in completions + if pattern.search(x) is not None]).difference(['cloudstack'])) + for verb in verbs: + def add_grammar(verb): def grammar_closure(self, args): - if self.pipe_runner("%s %s" % (rule, args)): + if self.pipe_runner("%s %s" % (verb, args)): return try: args_partition = args.partition(" ") - res = self.cache_verbs[rule][args_partition[0]] + res = self.cache_verbs[verb][args_partition[0]] cmd = res[0] helpdoc = res[2] args = args_partition[2] except KeyError, e: - self.print_shell("Error: invalid %s api arg" % rule, e) + self.print_shell("Error: invalid %s api arg" % verb, e) return if ' --help' in args or ' -h' in args: self.print_shell(helpdoc) @@ -536,12 +534,12 @@ def main(): self.default("%s %s" % (cmd, args)) return grammar_closure - grammar_handler = add_grammar(rule) - grammar_handler.__doc__ = "%ss resources" % rule.capitalize() - grammar_handler.__name__ = 'do_' + rule - setattr(self, grammar_handler.__name__, grammar_handler) + grammar_handler = add_grammar(verb) + grammar_handler.__doc__ = "%ss resources" % verb.capitalize() + grammar_handler.__name__ = 'do_' + verb + setattr(CloudMonkeyShell, grammar_handler.__name__, grammar_handler) - shell = CloudMonkeyShell(sys.argv[0]) + shell = CloudMonkeyShell(sys.argv[0], verbs) if len(sys.argv) > 1: shell.onecmd(' '.join(sys.argv[1:])) else: diff --git a/tools/cli/cloudmonkey/common.py b/tools/cli/cloudmonkey/common.py index 0865a8ee8cc..05767a57cb8 100644 --- a/tools/cli/cloudmonkey/common.py +++ b/tools/cli/cloudmonkey/common.py @@ -17,34 +17,43 @@ # under the License. # Use following rules for versioning: -# .. -__version__ = "4.0.0" +# - +__version__ = "4.1.0-0" try: + from os.path import expanduser import os from precache import precached_verbs except ImportError, e: precached_verbs = {} -# Add config key:value -config_file = os.path.expanduser('~/.cloudmonkey_config') -config_fields = {'host': 'localhost', 'port': '8080', - 'protocol': 'http', 'path': '/client/api', - 'apikey': '', 'secretkey': '', - 'timeout': '3600', 'asyncblock': 'true', - 'prompt': '🐵 cloudmonkey>', 'color': 'true', - 'tabularize': 'false', - 'log_file': - os.path.expanduser('~/.cloudmonkey_log'), - 'history_file': - os.path.expanduser('~/.cloudmonkey_history')} +param_type = ['boolean', 'date', 'float', 'integer', 'short', 'list', + 'long', 'object', 'map', 'string', 'tzdate', 'uuid'] -# Add verbs in grammar -grammar = ['create', 'list', 'delete', 'update', 'lock', - 'enable', 'activate', 'disable', 'add', 'remove', - 'attach', 'detach', 'associate', 'disassociate', 'generate', 'ldap', - 'assign', 'authorize', 'change', 'register', 'configure', - 'start', 'restart', 'reboot', 'stop', 'reconnect', - 'cancel', 'destroy', 'revoke', 'mark', 'reset', - 'copy', 'extract', 'migrate', 'restore', 'suspend', - 'get', 'query', 'prepare', 'deploy', 'upload'] +config_dir = expanduser('~/.cloudmonkey') +config_file = expanduser(config_dir + '/config') + +# cloudmonkey config fields +config_fields = {'core': {}, 'ui': {}, 'server': {}, 'user': {}} + +# core +config_fields['core']['cache_file'] = expanduser(config_dir + '/cache') +config_fields['core']['history_file'] = expanduser(config_dir + '/history') +config_fields['core']['log_file'] = expanduser(config_dir + '/log') + +# ui +config_fields['ui']['color'] = 'true' +config_fields['ui']['prompt'] = '> ' +config_fields['ui']['tabularize'] = 'false' + +# server +config_fields['server']['asyncblock'] = 'true' +config_fields['server']['host'] = 'localhost' +config_fields['server']['path'] = '/client/api' +config_fields['server']['port'] = '8080' +config_fields['server']['protocol'] = 'http' +config_fields['server']['timeout'] = '3600' + +# user +config_fields['user']['apikey'] = '' +config_fields['user']['secretkey'] = '' diff --git a/tools/cli/cloudmonkey/lexer.py b/tools/cli/cloudmonkey/lexer.py new file mode 100644 index 00000000000..373c9f27469 --- /dev/null +++ b/tools/cli/cloudmonkey/lexer.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +try: + from pygments import highlight + from pygments.console import ansiformat + from pygments.formatter import Formatter + from pygments.formatters import Terminal256Formatter + from pygments.lexer import bygroups, include, RegexLexer + from pygments.token import * + + import sys +except ImportError, e: + print e + + +MONKEY_COLORS = { + Token: '', + Whitespace: 'reset', + Text: 'reset', + + Name: 'green', + Operator: 'teal', + Operator.Word: 'lightgray', + String: 'purple', + + Keyword: '_red_', + Error: 'red', + Literal: 'yellow', + Number: 'blue', +} + + +def get_colorscheme(): + return MONKEY_COLORS + + +class MonkeyLexer(RegexLexer): + keywords = ['[a-z]*id', '^[a-z A-Z]*:'] + attributes = ['[Tt]rue', '[Ff]alse'] + params = ['[a-z]*[Nn]ame', 'type', '[Ss]tate'] + + uuid_rgx = r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' + date_rgx = r'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9:]{8}-[0-9]{4}' + + def makelistre(lis): + return r'(' + r'|'.join(lis) + r')' + + tokens = { + 'root': [ + (r' ', Whitespace), + (date_rgx, Number), + (uuid_rgx, Literal), + (r'(?:\b\d+\b(?:-\b\d+|%)?)', Number), + (r'^[-=]*\n', Operator.Word), + (r'Error', Error), + (makelistre(keywords), Keyword), + (makelistre(attributes), Literal), + (makelistre(params) + r'( = )(.*)', bygroups(Name, Operator, + String)), + (makelistre(params), Name), + (r'(^[a-zA-Z]* )(=)', bygroups(Name, Operator)), + (r'\S+', Text), + ] + } + + def analyse_text(text): + npos = text.find('\n') + if npos < 3: + return False + return text[0] == '[' and text[npos - 1] == ']' + + +class MonkeyFormatter(Formatter): + def __init__(self, **options): + Formatter.__init__(self, **options) + self.colorscheme = get_colorscheme() + + def format(self, tokensource, outfile): + self.encoding = outfile.encoding + return Formatter.format(self, tokensource, outfile) + + def format_unencoded(self, tokensource, outfile): + for ttype, value in tokensource: + color = self.colorscheme.get(ttype) + while color is None: + ttype = ttype[:-1] + color = self.colorscheme.get(ttype) + if color: + spl = value.split('\n') + for line in spl[:-1]: + if line: + outfile.write(ansiformat(color, line)) + outfile.write('\n') + if spl[-1]: + outfile.write(ansiformat(color, spl[-1])) + else: + outfile.write(value) + + +def monkeyprint(text): + fmter = MonkeyFormatter() + lexer = MonkeyLexer() + lexer.encoding = 'utf-8' + fmter.encoding = 'utf-8' + highlight(text, lexer, fmter, sys.stdout) diff --git a/tools/cli/setup.py b/tools/cli/setup.py index f1efa741a02..9624115ed5f 100644 --- a/tools/cli/setup.py +++ b/tools/cli/setup.py @@ -26,7 +26,7 @@ from cloudmonkey import __version__ name = 'cloudmonkey' version = __version__ -requires = ['clint>=0.3.0', +requires = ['Pygments>=1.5', 'prettytable>=0.6', ] diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index 87b0bbb7bbc..3751d987785 100644 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -389,6 +389,27 @@ class VirtualMachine: cmd.id = volume.id return apiclient.detachVolume(cmd) + def add_nic(self, apiclient, networkId): + """Add a NIC to a VM""" + cmd = addNicToVirtualMachine.addNicToVirtualMachineCmd(); + cmd.virtualmachineid = self.id + cmd.networkid = networkId + return apiclient.addNicToVirtualMachine(cmd) + + def remove_nic(self, apiclient, nicId): + """Remove a NIC to a VM""" + cmd = removeNicFromVirtualMachine.removeNicFromVirtualMachineCmd() + cmd.nicid = nicId + cmd.virtualmachineid = self.id + return apiclient.removeNicFromVirtualMachine(cmd) + + def update_default_nic(self, apiclient, nicId): + """Set a NIC to be the default network adapter for a VM""" + cmd = updateDefaultNicForVirtualMachine.updateDefaultNicForVirtualMachineCmd() + cmd.nicid = nicId + cmd.virtualmachineid = self.id + return apiclient.updateDefaultNicForVirtualMachine(cmd) + @classmethod def list(cls, apiclient, **kwargs): """List all VMs matching criteria""" @@ -1115,7 +1136,7 @@ class DiskOffering: if domainid: cmd.domainid = domainid - if services["storagetype"]: + if "storagetype" in services: cmd.storagetype = services["storagetype"] return DiskOffering(apiclient.createDiskOffering(cmd).__dict__) diff --git a/tools/whisker/LICENSE b/tools/whisker/LICENSE index 14c9df84a44..68d851f9073 100644 --- a/tools/whisker/LICENSE +++ b/tools/whisker/LICENSE @@ -748,7 +748,7 @@ Within the deps/awsapi-lib directory The Covered Software is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial - computer software" (as that term is defined at 48 C.F.R. + computer software" (as that term is defined at 48 C.F.R. � 252.227-7014(a)(1)) and "commercial computer software documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 @@ -1138,7 +1138,7 @@ Within the deps/awsapi-lib directory The Covered Software is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial - computer software" (as that term is defined at 48 C.F.R. + computer software" (as that term is defined at 48 C.F.R. � 252.227-7014(a)(1)) and "commercial computer software documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 @@ -1526,7 +1526,7 @@ Within the deps/awsapi-lib directory The Covered Software is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial - computer software" (as that term is defined at 48 C.F.R. + computer software" (as that term is defined at 48 C.F.R. � 252.227-7014(a)(1)) and "commercial computer software documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 @@ -1867,7 +1867,9 @@ Within the deps/awsapi-lib directory slf4j-api-1.5.11.jar from https://github.com/qos-ch/slf4j slf4j-jdk14-1.5.11.jar from https://github.com/qos-ch/slf4j + licensed under the Mozilla Public License, Version 1.1 http://www.mozilla.org/MPL/1.1/ (as follows) + MOZILLA PUBLIC LICENSE @@ -2344,6 +2346,7 @@ Within the deps/awsapi-lib directory from Shigeru Chiba http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/ javassist-3.9.0.GA.jar from http://sourceforge.net/projects/jboss/files/Javassist/ + licensed under the Mozilla Public License, Version 1.1 http://www.mozilla.org/MPL/1.1/ (as follows) Copyright (c) 2007-2012 VMware, Inc. All Rights Reserved. @@ -2822,6 +2825,7 @@ Within the deps/awsapi-lib directory from VMware, Inc http://www.vmware.com/ rabbitmq-client.jar from http://www.rabbitmq.com/java-client.html + Within the patches/systemvm/debian/config/etc directory placed in the public domain by Adiscon GmbH http://www.adiscon.com/ @@ -3458,7 +3462,7 @@ Within the target/jar directory The Covered Software is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial - computer software" (as that term is defined at 48 C.F.R. + computer software" (as that term is defined at 48 C.F.R. � 252.227-7014(a)(1)) and "commercial computer software documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 @@ -3847,7 +3851,7 @@ Within the target/jar directory The Covered Software is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial - computer software" (as that term is defined at 48 C.F.R. + computer software" (as that term is defined at 48 C.F.R. � 252.227-7014(a)(1)) and "commercial computer software documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 @@ -4373,7 +4377,7 @@ Within the target/jar directory licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) - Copyright (C) 2008 Tth Istvn + Copyright (C) 2008 T�th Istv�n 2008-2012 Daniel Veillard 2009-2011 Bryan Kearney @@ -4504,7 +4508,7 @@ Within the ui/lib directory licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) - Copyright (c) 2006 - 2011 Jrn Zaefferer + Copyright (c) 2006 - 2011 J�rn Zaefferer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -4699,7 +4703,7 @@ Within the ui/lib/jquery-ui directory Within the ui/lib/qunit directory licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) - Copyright (c) 2012 John Resig, Jrn Zaefferer + Copyright (c) 2012 John Resig, J�rn Zaefferer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/tools/whisker/descriptor-for-packaging.xml b/tools/whisker/descriptor-for-packaging.xml index 095ebe90cba..29a8284686b 100644 --- a/tools/whisker/descriptor-for-packaging.xml +++ b/tools/whisker/descriptor-for-packaging.xml @@ -2424,9 +2424,9 @@ Innovation Centre, 2006 (http://www.it-innovation.soton.ac.uk). name='Adiscon GmbH' url='http://www.adiscon.com/' /> + id='vmware.com' + name='VMware, Inc' + url='http://www.vmware.com/' /> Copyright (c) 2013 The Apache Software Foundation diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 6519ed053e8..eb4284374de 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -441,15 +441,11 @@ body.login { div.list-view table tbody td span { display: block; float: left; - max-width: 160px; + max-width: 100%; word-wrap: break-word; text-indent: 0; margin-left: 12px; line-height: 15px; - overflow: hidden; - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap; } div.list-view div.toolbar div.section-switcher div.section-select label { diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp index 203c9daa52e..77dfdc9055f 100644 --- a/ui/dictionary.jsp +++ b/ui/dictionary.jsp @@ -884,6 +884,7 @@ dictionary = { 'label.network.offering.name': '', 'label.network.offering': '', 'label.network.rate': '', +'label.network.rate.megabytes': '', 'label.network.read': '', 'label.network.type': '', 'label.network.write': '', diff --git a/ui/scripts/instanceWizard.js b/ui/scripts/instanceWizard.js index 070f7e7b98d..e5e2fb7bcbb 100644 --- a/ui/scripts/instanceWizard.js +++ b/ui/scripts/instanceWizard.js @@ -18,7 +18,7 @@ (function($, cloudStack) { var zoneObjs, hypervisorObjs, featuredTemplateObjs, communityTemplateObjs, myTemplateObjs, featuredIsoObjs, community, networkObjs; var selectedZoneObj, selectedTemplateObj, selectedHypervisor, selectedDiskOfferingObj; - var step5ContainerType = 'nothing-to-select'; //'nothing-to-select', 'select-network', 'select-security-group' + var step5ContainerType = 'nothing-to-select'; //'nothing-to-select', 'select-network', 'select-security-group', 'select-advanced-sg'(advanced sg-enabled zone) cloudStack.instanceWizard = { maxDiskOfferingSize: function() { @@ -64,7 +64,7 @@ return $.grep(network.service, function(service) { return service.name == 'SecurityGroup'; }).length; - }).length; + }).length; //return total number of selected sg networks }, // Data providers for each wizard step @@ -520,7 +520,7 @@ } //step 5: select network - if (step5ContainerType == 'select-network') { + if (step5ContainerType == 'select-network' || step5ContainerType == 'select-advanced-sg') { var array2 = []; var defaultNetworkId = args.data.defaultNetwork; //args.data.defaultNetwork might be equal to string "new-network" or a network ID diff --git a/ui/scripts/system.js b/ui/scripts/system.js index def6d443026..2ce7672a60f 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -379,9 +379,7 @@ }); }; - //dataFns.zoneCount({}); - dataFns.podCount({}); //uncomment the line above and remove this line after "count" in listZones API is fixed. - + dataFns.zoneCount({}); } }, @@ -9736,7 +9734,7 @@ args.response.success({data:{}}); }, error: function(json) { - args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + args.response.error(parseXMLHttpResponse(json)); } }); }, diff --git a/ui/scripts/ui-custom/instanceWizard.js b/ui/scripts/ui-custom/instanceWizard.js index 6e993843562..a30b6194e51 100644 --- a/ui/scripts/ui-custom/instanceWizard.js +++ b/ui/scripts/ui-custom/instanceWizard.js @@ -827,9 +827,15 @@ data: cloudStack.serializeForm($form) }); - if (!advSGFilter) { + if (advSGFilter == 0) { //when total number of selected sg networks is 0, then 'Select Security Group' is skipped, go to step 6 directly showStep(6); } + else { //when total number of selected sg networks > 0 + if($activeStep.find('input[type=checkbox]:checked').size() > 1) { //when total number of selected networks > 1 + cloudStack.dialog.notice({ message: "Can't create a vm with multiple networks one of which is Security Group enabled" }); + return false; + } + } } } diff --git a/ui/scripts/ui-custom/recurringSnapshots.js b/ui/scripts/ui-custom/recurringSnapshots.js index e5302e9e1d5..985f36912cb 100644 --- a/ui/scripts/ui-custom/recurringSnapshots.js +++ b/ui/scripts/ui-custom/recurringSnapshots.js @@ -189,6 +189,7 @@ var $dialog = $snapshots.dialog({ title: _l('label.action.recurring.snapshot'), dialogClass: 'recurring-snapshots', + closeOnEscape:false, width: 600, buttons: [ { @@ -210,4 +211,4 @@ return $dialog; }; }; -}(cloudStack, jQuery)); \ No newline at end of file +}(cloudStack, jQuery)); diff --git a/usage/pom.xml b/usage/pom.xml index 252e293b4e0..9ecd6e139ff 100644 --- a/usage/pom.xml +++ b/usage/pom.xml @@ -36,5 +36,73 @@ install src test + + + org.apache.maven.plugins + maven-dependency-plugin + 2.5.1 + + + copy-dependencies + package + + copy-dependencies + + + target/dependencies + runtime + + + + + + maven-antrun-plugin + 1.7 + + + generate-resource + generate-resources + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +