/**
* Copyright (c) 2009--2014 Red Hat, Inc.
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* Red Hat trademarks are not licensed under GPLv2. No permission is
* granted to use or replicate Red Hat trademarks that are incorporated
* in this software or its documentation.
*/
package com.redhat.rhn.manager.kickstart.cobbler;
import com.redhat.rhn.common.conf.ConfigDefaults;
import com.redhat.rhn.common.validator.ValidatorError;
import com.redhat.rhn.domain.action.Action;
import com.redhat.rhn.domain.kickstart.KickstartData;
import com.redhat.rhn.domain.server.NetworkInterface;
import com.redhat.rhn.domain.server.Server;
import com.redhat.rhn.domain.token.ActivationKey;
import com.redhat.rhn.domain.token.ActivationKeyFactory;
import com.redhat.rhn.domain.token.Token;
import com.redhat.rhn.domain.user.User;
import com.redhat.rhn.domain.user.UserFactory;
import com.redhat.rhn.manager.kickstart.KickstartFormatter;
import com.redhat.rhn.manager.kickstart.KickstartUrlHelper;
import com.redhat.rhn.manager.token.ActivationKeyManager;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.cobbler.Network;
import org.cobbler.Profile;
import org.cobbler.SystemRecord;
import org.cobbler.XmlRpcException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
* Login to Cobbler's XMLRPC API and get a token
* @version $Rev$
*/
public class CobblerSystemCreateCommand extends CobblerCommand {
private static Logger log = Logger.getLogger(CobblerSystemCreateCommand.class);
private Action scheduledAction;
private final Server server;
private String mediaPath;
private String profileName;
private String activationKeys;
private String kickstartHost;
private String kernelOptions;
private String postKernelOptions;
private String networkInterface;
private boolean isDhcp;
private boolean useIpv6Gateway;
private String ksDistro;
private boolean setupBridge;
private String bridgeName;
private List<String> bridgeSlaves;
private String bridgeOptions;
private String bridgeAddress;
private String bridgeNetmask;
private String bridgeGateway;
private boolean isBridgeDhcp;
/**
* @param dhcp true if the network type is dhcp
* @param networkInterfaceIn The name of the network interface
* @param useIpv6GatewayIn whether to use ipv6 gateway
* @param ksDistroIn distro to be provisioned
*/
public void setNetworkInfo(boolean dhcp, String networkInterfaceIn,
boolean useIpv6GatewayIn, String ksDistroIn) {
isDhcp = dhcp;
networkInterface = networkInterfaceIn;
useIpv6Gateway = useIpv6GatewayIn;
ksDistro = ksDistroIn;
}
/**
* @param doBridge boolean, whether or not to set up a bridge post-install
* @param name string, name of the bridge
* @param slaves string array, nics to use as slaves
* @param options string, bridge options
* @param isBridgeDhcpIn boolean, if the bridge will use dhcp to obtain an ip address
* @param address string, ip address for the bridge (if isDhcp is false)
* @param netmask string, netmask for the bridge (if isDhcp is false)
* @param gateway string, gateway for the bridge (if isDhcp is false)
*/
public void setBridgeInfo(boolean doBridge, String name,
List<String> slaves, String options, boolean isBridgeDhcpIn,
String address, String netmask, String gateway) {
setupBridge = doBridge;
bridgeName = name;
bridgeSlaves = slaves;
bridgeOptions = options;
isBridgeDhcp = isBridgeDhcpIn;
bridgeAddress = address;
bridgeNetmask = netmask;
bridgeGateway = gateway;
}
/**
* Constructor
* @param userIn who is requesting the sync
* @param serverIn profile we want to create in cobbler
* @param ksDataIn profile to associate with with server.
* @param mediaPathIn mediaPath to override in the server profile.
* @param activationKeysIn to add to the system record. Used when the system
* re-registers to Spacewalk
*/
public CobblerSystemCreateCommand(User userIn, Server serverIn,
KickstartData ksDataIn, String mediaPathIn, String activationKeysIn) {
super(userIn);
this.server = serverIn;
this.mediaPath = mediaPathIn;
if (ksDataIn != null) {
profileName = ksDataIn.getCobblerObject(user).getName();
}
else {
throw new NullPointerException("ksDataIn cant be null");
}
this.activationKeys = activationKeysIn;
}
/**
* Constructor to be used for a system outside tthe context
* of actually kickstarting it to a specific profile.
* @param serverIn profile we want to create in cobbler
* @param cobblerProfileName the name of the cobbler profile
* to associate with system
* @param ksData the kickstart data to associate the system with
*/
public CobblerSystemCreateCommand(Server serverIn, String cobblerProfileName,
KickstartData ksData) {
super(serverIn.getCreator());
this.server = serverIn;
this.mediaPath = null;
this.profileName = cobblerProfileName;
String note = "Reactivation key for " + server.getName() + ".";
ActivationKey key = ActivationKeyManager.getInstance().
createNewReActivationKey(UserFactory.findRandomOrgAdmin(
server.getOrg()), server, note);
log.debug("created reactivation key: " + key.getKey());
String keys = key.getKey();
if (ksData != null) {
for (Token token : ksData.getDefaultRegTokens()) {
ActivationKey keyTmp = ActivationKeyFactory.lookupByToken(token);
if (keyTmp != null) {
keys += "," + keyTmp.getKey();
}
}
}
this.activationKeys = keys;
}
/**
* Constructor
* @param userIn who is requesting the sync
* @param serverIn profile we want to create in cobbler
* @param nameIn profile nameIn to associate with with server.
*/
public CobblerSystemCreateCommand(User userIn, Server serverIn,
String nameIn) {
super(userIn);
this.server = serverIn;
profileName = nameIn;
}
protected SystemRecord lookupExisting() {
if (server.getCobblerId() != null) {
SystemRecord rec;
rec = SystemRecord.lookupById(CobblerXMLRPCHelper.getConnection(user),
server.getCobblerId());
if (rec != null) {
return rec;
}
}
//lookup by ID failed, so lets try by mac
Map sysmap = getSystemMapByMac();
if (sysmap != null) {
log.debug("getSystemHandleByMAC.found match.");
String uid = (String) sysmap.get("uid");
SystemRecord rec;
rec = SystemRecord.lookupById(CobblerXMLRPCHelper.getConnection(user),
uid);
if (rec != null) {
return rec;
}
}
return null;
}
private Map getSystemMapByMac() {
// Build up list of mac addrs
List macs = new LinkedList();
for (NetworkInterface n : server.getNetworkInterfaces()) {
// Skip localhost and non real interfaces
if (!n.isValid()) {
log.debug("Skipping. not a real interface");
}
else {
macs.add(n.getHwaddr().toLowerCase());
}
}
List <String> args = new ArrayList();
args.add(xmlRpcToken);
List<Map> systems = (List) invokeXMLRPC("get_systems", args);
for (Map row : systems) {
Set ifacenames = ((Map) row.get("interfaces")).keySet();
log.debug("Ifacenames: " + ifacenames);
Map ifaces = (Map) row.get("interfaces");
log.debug("ifaces: " + ifaces);
Iterator names = ifacenames.iterator();
while (names.hasNext()) {
String name = (String) names.next();
log.debug("Name: " + name);
Map iface = (Map) ifaces.get(name);
log.debug("iface: " + iface);
String mac = (String) iface.get("mac_address");
log.debug("getSystemMapByMac.ROW: " + row +
" looking for: " + macs);
if (mac != null &&
macs.contains(mac.toLowerCase())) {
log.debug("getSystemMapByMac.found match.");
return row;
}
}
}
return null;
}
/**
* Store the System to cobbler
* @return ValidatorError if the store failed.
*/
@Override
public ValidatorError store() {
return store(true);
}
/**
* Store the System to cobbler
* @param saveCobblerId false if CobblerVirtualSystemCommand is calling, true otherwise
* @return ValidatorError if the store failed.
*/
public ValidatorError store(boolean saveCobblerId) {
Profile profile = Profile.lookupByName(getCobblerConnection(), profileName);
// First lookup by MAC addr
SystemRecord rec = lookupExisting();
if (rec == null) {
// Next try by name
rec = SystemRecord.lookupByName(getCobblerConnection(user),
getCobblerSystemRecordName());
}
// Else, lets make a new system
if (rec == null) {
rec = SystemRecord.create(getCobblerConnection(),
getCobblerSystemRecordName(), profile);
}
try {
processNetworkInterfaces(rec, server);
}
catch (XmlRpcException e) {
if (e.getCause() != null && e.getCause().getMessage() != null &&
e.getCause().getMessage().contains("IP address duplicated")) {
return new ValidatorError(
"frontend.actions.systems.virt.duplicateipaddressvalue");
}
throw e;
}
rec.enableNetboot(true);
rec.setProfile(profile);
if (isDhcp) {
rec.setIpv6Autoconfiguration(true);
}
else {
rec.setIpv6Autoconfiguration(false);
}
if (this.activationKeys == null || this.activationKeys.length() == 0) {
log.error("This cobbler profile does not " +
"have a redhat_management_key set ");
}
else {
rec.setRedHatManagementKey(activationKeys);
}
if (!StringUtils.isBlank(getKickstartHost())) {
rec.setServer(getKickstartHost());
}
else {
rec.setServer("");
}
// Setup the kickstart metadata so the URLs and activation key are setup
Map<String, Object> ksmeta = rec.getKsMeta();
if (ksmeta == null) {
ksmeta = new HashMap<String, Object>();
}
if (!StringUtils.isBlank(mediaPath)) {
ksmeta.put(KickstartUrlHelper.COBBLER_MEDIA_VARIABLE,
this.mediaPath);
}
if (!StringUtils.isBlank(getKickstartHost())) {
ksmeta.put(SystemRecord.REDHAT_MGMT_SERVER,
getKickstartHost());
}
ksmeta.remove(KickstartFormatter.STATIC_NETWORK_VAR);
ksmeta.put(KickstartFormatter.USE_IPV6_GATEWAY,
this.useIpv6Gateway ? "true" : "false");
if (this.ksDistro != null) {
ksmeta.put(KickstartFormatter.KS_DISTRO, this.ksDistro);
}
rec.setKsMeta(ksmeta);
if (getServer().getHostname() != null) {
rec.setHostName(getServer().getHostname());
}
else if (getServer().getName() != null) {
rec.setHostName(getServer().getName());
}
rec.setKernelOptions(kernelOptions);
rec.setKernelPostOptions(postKernelOptions);
try {
rec.save();
}
catch (XmlRpcException e) {
if (e.getCause() != null && e.getCause().getMessage() != null &&
e.getCause().getMessage().contains("IP address duplicated")) {
return new ValidatorError(
"frontend.actions.systems.virt.duplicateipaddressvalue");
}
throw e;
}
/*
* This is a band-aid for the problem revealed in bug 846221. However
* the real fix involves creating a new System for the virtual guest
* instead of re-using the host System object, and I am unsure of what
* effects that would have. The System object is used when creating
* reActivation keys and setting up the cobbler SystemRecord network
* info among other things. No bugs have been reported in those areas
* yet, so I don't want to change something that has the potential to
* break a lot of things.
*/
if (saveCobblerId) {
server.setCobblerId(rec.getId());
}
return null;
}
/**
* Get the cobbler system record name for a system
* @return String name of cobbler system record
*/
public String getCobblerSystemRecordName() {
return CobblerSystemCreateCommand.getCobblerSystemRecordName(this.server);
}
/**
* Get the cobbler system record name for a system
* @param serverIn the server to get the name from
* @return String name of cobbler system record
*/
public static String getCobblerSystemRecordName(Server serverIn) {
String sep = ConfigDefaults.get().getCobblerNameSeparator();
String name = serverIn.getName().replace(' ', '_');
name = name.replace(' ', '_').replaceAll("[^a-zA-Z0-9_\\-\\.]", "");
return name + sep + serverIn.getOrg().getId();
}
protected void processNetworkInterfaces(SystemRecord rec,
Server serverIn) {
List <Network> nics = new LinkedList<Network>();
if (serverIn.getNetworkInterfaces() != null) {
for (NetworkInterface n : serverIn.getNetworkInterfaces()) {
// don't create a physical network device for a bond
if (n.isPublic() && !n.isVirtBridge() && !n.isBond()) {
Network net = new Network(getCobblerConnection(),
n.getName());
net.setIpAddress(n.getIpaddr());
net.setMacAddress(n.getHwaddr());
net.setNetmask(n.getNetmask());
if (!StringUtils.isBlank(networkInterface) &&
n.getName().equals(networkInterface)) {
net.setStaticNetwork(!isDhcp);
}
ArrayList<String> ipv6Addresses = n.getGlobalIpv6Addresses();
if (ipv6Addresses.size() > 0) {
net.setIpv6Address(ipv6Addresses.get(0));
ipv6Addresses.remove(0);
}
if (ipv6Addresses.size() > 0) {
net.setIpv6Secondaries(ipv6Addresses);
}
if (setupBridge && bridgeSlaves.contains(n.getName())) {
net.makeBondingSlave();
net.setBondingMaster(bridgeName);
}
nics.add(net);
}
else if (setupBridge && bridgeSlaves.contains(n.getName())) {
Network net = new Network(getCobblerConnection(),
n.getName());
net.setMacAddress(n.getHwaddr());
net.makeBondingSlave();
net.setBondingMaster(bridgeName);
nics.add(net);
}
}
if (setupBridge) {
Network net = new Network(getCobblerConnection(), bridgeName);
net.makeBondingMaster();
net.setBondingOptions(bridgeOptions);
net.setStaticNetwork(!isBridgeDhcp);
if (!isBridgeDhcp) {
net.setNetmask(bridgeNetmask);
net.setIpAddress(bridgeAddress);
rec.setGateway(bridgeGateway);
}
nics.add(net);
}
}
rec.setNetworkInterfaces(nics);
}
/**
* @return the system
*/
public Server getServer() {
return server;
}
/**
* @return Returns the kickstartHost.
*/
public String getKickstartHost() {
return kickstartHost;
}
/**
* @param kickstartHostIn The kickstartHost to set.
*/
public void setKickstartHost(String kickstartHostIn) {
this.kickstartHost = kickstartHostIn;
}
/**
* @param kernelOptionsIn The kernelOptions to set.
*/
public void setKernelOptions(String kernelOptionsIn) {
this.kernelOptions = kernelOptionsIn;
}
/**
* @param postKernelOptionsIn The postKernelOptions to set.
*/
public void setPostKernelOptions(String postKernelOptionsIn) {
this.postKernelOptions = postKernelOptionsIn;
}
/**
* Set the scheduled action associated to this command.
* @param kickstartAction ks action associated to this command
*/
public void setScheduledAction(Action kickstartAction) {
scheduledAction = kickstartAction;
}
protected Action getScheduledAction() {
return scheduledAction;
}
}