Java tutorial
/** * 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.frontend.action.kickstart; import com.redhat.rhn.common.conf.ConfigDefaults; import com.redhat.rhn.common.localization.LocalizationService; import com.redhat.rhn.common.security.SessionSwap; import com.redhat.rhn.domain.channel.Channel; import com.redhat.rhn.domain.kickstart.KickstartData; import com.redhat.rhn.domain.kickstart.KickstartFactory; import com.redhat.rhn.domain.org.Org; import com.redhat.rhn.domain.org.OrgFactory; import com.redhat.rhn.domain.user.User; import com.redhat.rhn.manager.channel.ChannelManager; import com.redhat.rhn.manager.kickstart.IpAddress; import com.redhat.rhn.manager.kickstart.KickstartFormatter; import com.redhat.rhn.manager.kickstart.KickstartManager; import com.redhat.rhn.manager.kickstart.KickstartSessionUpdateCommand; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionMessages; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; /** * KickstartHelper - helper class for KS UI processing * @version $Rev$ */ public class KickstartHelper { private static Logger log = Logger.getLogger(KickstartHelper.class); private HttpServletRequest request; private static final String VIEW_LABEL = "view_label"; private static final String LABEL = "label"; private static final String ORG_DEFAULT = "org_default"; private static final String IP_RANGE = "ip_range"; private static final String SESSION = "session"; private static final String SESSION_ID = "session_id"; private static final String ORG = "org"; private static final String HOST = "host"; private static final String ORG_ID = "org_id"; private static final String XFORWARD_HOST = "X-Forwarded-Host"; private static final String XFORWARD = "X-Forwarded-For"; private static final String XFORWARD_REGEX = "(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}.\\d{1,3})"; private static final String KSDATA = "ksdata"; public static final String XRHNPROXYAUTH = "X-RHN-Proxy-Auth"; /** * Constructor * @param reqIn associated with this helper. */ public KickstartHelper(HttpServletRequest reqIn) { this.request = reqIn; } /** * Parse a ks url and return a Map of options * Example: * "ks/org/3756992x3d9f6e2d5717e264c89b5ac18eb0c59e/label/kslabelfoo"; * * NOTE: This method also updates the KickstartSession.state field * to "configuration_accessed" * * @param url to parse * @return Map of options. Usually containing host, ksdata, label and org_id */ public Map<String, Object> parseKickstartUrl(String url) { Map<String, Object> retval = new HashMap<String, Object>(); KickstartData ksdata = null; Map<String, String> options = new HashMap<String, String>(); log.debug("url: " + url); List<String> rawopts = Arrays.asList(StringUtils.split(url, '/')); for (Iterator<String> iter = rawopts.iterator(); iter.hasNext();) { String name = iter.next(); if (iter.hasNext()) { String value = iter.next(); options.put(name, value); } } log.debug("Options: " + options); String remoteAddr = getClientIp(); // Process the org if (options.containsKey(ORG)) { String id = options.get(ORG); retval.put(ORG_ID, id); } else { retval.put(ORG_ID, OrgFactory.getSatelliteOrg().getId().toString()); } String mode = ORG_DEFAULT; // go ahead and make this the default profile // Process the session // /kickstart/ks/session/2xb7d56e8958b0425e762cc74e8705d8e7 if (options.containsKey(SESSION)) { // update session String hashed = options.get(SESSION); String[] ids = SessionSwap.extractData(hashed); retval.put(SESSION_ID, ids[0]); Long kssid = new Long(ids[0]); log.debug("sessionid: " + kssid); KickstartSessionUpdateCommand cmd = new KickstartSessionUpdateCommand(kssid); ksdata = cmd.getKsdata(); retval.put(SESSION, cmd.getKickstartSession()); log.debug("session: " + retval.get(SESSION)); cmd.setSessionState(KickstartFactory.SESSION_STATE_CONFIG_ACCESSED); cmd.store(); mode = SESSION; } log.debug("org_id: " + retval.get(ORG_ID)); //TODO: reconsider/cleanup this logic flow if (retval.get(ORG_ID) != null) { // Process label if (options.containsKey(LABEL)) { retval.put(LABEL, options.get(LABEL)); mode = LABEL; } else if (options.containsKey(VIEW_LABEL)) { retval.put(VIEW_LABEL, options.get(VIEW_LABEL)); retval.put(LABEL, options.get(VIEW_LABEL)); mode = LABEL; } else if (options.containsValue(IP_RANGE)) { mode = IP_RANGE; } Org org = OrgFactory.lookupById(new Long((String) retval.get(ORG_ID))); if (mode.equals(LABEL)) { String label = (String) retval.get(LABEL); ksdata = KickstartFactory.lookupKickstartDataByLabelAndOrgId(label, org.getId()); } else if (mode.equals(IP_RANGE)) { log.debug("Ip_range mode"); IpAddress clientIp = new IpAddress(remoteAddr); ksdata = KickstartManager.getInstance().findProfileForIpAddress(clientIp, org); } else if (mode.equals(ORG_DEFAULT)) { //process org_default log.debug("Org_default mode."); ksdata = getOrgDefaultProfile(org); } if (log.isDebugEnabled()) { log.debug("session : " + retval.get(SESSION)); log.debug("options.containsKey(VIEW_LABEL): " + options.containsKey(VIEW_LABEL)); log.debug("ksdata : " + ksdata); } } // Add the host. retval.put(HOST, getKickstartHost()); // Add ksdata retval.put(KSDATA, ksdata); if (retval.size() == 0) { retval = null; } return retval; } /** * Check to see if this request came through a proxy * @return boolean if proxied or not. */ public boolean isProxyRequest() { return request.getHeader(XFORWARD) != null; } private String getClientIp() { String remoteAddr = request.getRemoteAddr(); String proxyHeader = request.getHeader(XFORWARD); // check if we are going through a proxy, grab real IP if so if (proxyHeader != null) { log.debug("proxy header in: " + proxyHeader); Matcher matcher = Pattern.compile(XFORWARD_REGEX).matcher(proxyHeader); if (matcher.lookingAt()) { remoteAddr = matcher.group(1); log.debug("origination ip from pchain: " + remoteAddr); } } return remoteAddr; } /** * Check to see if this request came through a proxy * @return String of hostname of first proxy in chain or null otherwise. */ public String getForwardedHost() { String host = null; String forwardHosts = request.getHeader(XFORWARD_HOST); if (forwardHosts != null) { host = forwardHosts.split(",")[0]; } return host; } /** * * @param orgIdIn Org Id * @return Default Kickstart Data for Org */ private KickstartData getOrgDefaultProfile(Org orgIn) { return KickstartFactory.lookupOrgDefault(orgIn); } /** * Get the kickstart host to use. Will use the host of the proxy if the header is * present. If not the code then resorts to getting the cobbler hostname from our * rhn.conf Config. * * @return String representing the Kickstart Host */ public String getKickstartHost() { log.debug("KickstartHelper.getKickstartHost()"); // Example proxy header: // X-RHN-Proxy-Auth : 1006681409::1151513167.96:21600.0:VV/xF // NEmCYOuHxEBAs7BEw==:fjs-0-08.rhndev.redhat.com,1006681408 // ::1151513034.3:21600.0:w2lm+XWSFJMVCGBK1dZXXQ==:fjs-0-11. // rhndev.redhat.com,1006678487::1152567362.02:21600.0:t15l // gsaTRKpX6AxkUFQ11A==:fjs-0-12.rhndev.redhat.com String proxyHeader = request.getHeader(XRHNPROXYAUTH); log.debug("X-RHN-Proxy-Auth : " + proxyHeader); if (!StringUtils.isEmpty(proxyHeader)) { String[] proxies = StringUtils.split(proxyHeader, ","); String firstProxy = proxies[0]; // Now we have: 1006681409::1151513167.96:21600.0:VV/xF // NEmCYOuHxEBAs7BEw==:fjs-0-08.rhndev.redhat.com log.debug("first1: " + firstProxy); String[] chunks = StringUtils.split(firstProxy, ":"); firstProxy = chunks[chunks.length - 1]; log.debug("first2: " + firstProxy); log.debug("Kickstart host from proxy header: " + firstProxy); return firstProxy; } return ConfigDefaults.get().getCobblerHost(); } /** * @return String representing the kickstart protocol. */ public String getKickstartProtocol() { String protocol = null; try { URL url = new URL(request.getRequestURL().toString()); protocol = url.getProtocol(); } catch (MalformedURLException e) { throw new IllegalArgumentException("Bad argument when determining kickstart protocol."); } return protocol; } /** * Get the protocol plus the host: * * http://host1.rhndev.redhat.com * * @return proto plus host url */ public String getKickstartProtocolAndHost() { String retval = getKickstartProtocol(); retval = retval + "://" + getKickstartHost(); return retval; } /** * @param org The Org to generate the token for. * @return A session-specific token for the given Org. */ public String generateOrgToken(Org org) { String orgStr = org.getId().toString(); return orgStr + "x" + SessionSwap.generateSwapKey(orgStr); } /** * Verify that the kickstart channel is valid. * Valid kickstart channels must have a set list of packages described * by KickstartFormatter.UPDATE_PKG_NAMES and KickstartFormatter.FRESH_PKG_NAMES_RHEL34 * * Also checks for auto-kickstart packages. * @param ksdata kickstart data containing the kickstart channel. * @param user The logged in user. * @return Whether the kickstart channel is a valid one. */ public boolean verifyKickstartChannel(KickstartData ksdata, User user) { return verifyKickstartChannel(ksdata, user, true); } /** * Verify that the kickstart channel is valid. * Valid kickstart channels must have a set list of packages described * by KickstartFormatter.UPDATE_PKG_NAMES and KickstartFormatter.FRESH_PKG_NAMES_RHEL34 * * Non bare metal kickstarts also must have auto kickstart packages. * @param ksdata kickstart data containing the kickstart channel. * @param user The logged in user. * @param checkAutoKickstart Whether or not to verify the existence of * auto-kickstart files. These are needed for many tasks, but are * not necessary for generating kickstart files. * @return Whether the kickstart channel is a valid one. */ public boolean verifyKickstartChannel(KickstartData ksdata, User user, boolean checkAutoKickstart) { if (ksdata.isRawData()) { //well this is Rawdata I am going to assume // its fine and dandy // In the future if we instead decide // that we need to do a channel // check on a rawdata this is the place to fix that return true; } //I tried to make this readable while still maintaining all the boolean //shortcutting. Here is the one liner boolean: if (hasUpdates(ksdata) && hasFresh(ksdata) && (!checkAutoKickstart || hasKickstartPackage(ksdata, user))) { return true; } return false; } private boolean hasUpdates(KickstartData ksdata) { if (ksdata.isRhel4() || ksdata.isRhel3() || ksdata.isRhel2()) { return hasPackages(ksdata.getChannel(), KickstartFormatter.UPDATE_PKG_NAMES); } return true; } private boolean hasFresh(KickstartData ksdata) { //There are different 'fresh packages' for different RHEL releases. //TODO: right now we do this a pretty ugly way -> we have a static // list of fresh packages for the different releases and we // check which one to use based on the install type suffix number. // If we need to support more than two lists, we should probably // make this a little more data driven. if (ksdata.isRhel2()) { return hasPackages(ksdata.getChannel(), KickstartFormatter.FRESH_PKG_NAMES_RHEL2); } if (ksdata.isRhel3() || ksdata.isRhel4()) { return hasPackages(ksdata.getChannel(), KickstartFormatter.FRESH_PKG_NAMES_RHEL34); } return true; } private boolean hasPackages(Channel c, String[] packageNames) { log.debug("HasPackages: " + c.getId()); //Go through every package name. for (int i = 0; i < packageNames.length; i++) { log.debug("hasPackages : Checking for package: " + packageNames[i]); Long pid = ChannelManager.getLatestPackageEqual(c.getId(), packageNames[i]); //No package by this name exists in this package. if (pid == null) { log.debug("hasPackages : not found"); return false; } } //We have a pid from every package. return true; } private boolean hasKickstartPackage(KickstartData ksdata, User user) { //We expect this to be in the RHN Tools channel. //Check in the channel and all of its children channels Channel channel = ksdata.getChannel(); log.debug("Checking on auto-ks in channel : " + channel.getId()); List<Channel> channelsToCheck = ChannelManager.userAccessibleChildChannels(user.getOrg().getId(), channel.getId()); channelsToCheck.add(channel); Iterator<Channel> i = channelsToCheck.iterator(); while (i.hasNext()) { Channel current = i.next(); log.debug("Current.channel : " + current.getId()); //Look for the auto-kickstart package. List<Map<String, Object>> kspackages = ChannelManager.listLatestPackagesLike(current.getId(), ksdata.getKickstartPackageName()); //found it, this channel is good. if (kspackages.size() > 0) { return true; } log.debug("package not found"); } //We have checked every channel without luck. return false; } /** * Create a message to the user about having a kickstart channel that is missing * required packages. * @param ksdata The kickstart data that contains the kickstart channel. * @return Messages to add to the request. */ public ActionMessages createInvalidChannelMsg(KickstartData ksdata) { ActionMessages msg = new ActionMessages(); Object[] args = new Object[] { createPackageNameList(ksdata) }; msg.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("kickstart.invalidchannel.message", args)); if (ksdata.getChannel().getOrg() == null) { //if not a custom channel //Tell them that they should sync the RHN Tools channel. msg.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("kickstart.invalidchannel.satmessage")); } return msg; } private String createPackageNameList(KickstartData ksdata) { //First create a list of all the packages needed List<String> packages = new ArrayList<String>(); packages.addAll(Arrays.asList(KickstartFormatter.UPDATE_PKG_NAMES)); //different 'fresh' packages for RHEL2 if (ksdata.isRhel2()) { packages.addAll(Arrays.asList(KickstartFormatter.FRESH_PKG_NAMES_RHEL2)); } if (ksdata.isRhel3() || ksdata.isRhel4()) { packages.addAll(Arrays.asList(KickstartFormatter.FRESH_PKG_NAMES_RHEL34)); } //add a '*' at the end because the auto kickstart is a prefix packages.add(ksdata.getKickstartPackageName() + "*"); //Now convert the list to a delimited string. String delimiter = LocalizationService.getInstance().getMessage("list delimiter"); return StringUtils.join(packages.toArray(), delimiter); } }