package org.ovirt.engine.core.vdsbroker.vdsbroker; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.ovirt.engine.core.common.action.SysPrepParams; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigUtil; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.osinfo.OsRepository; import org.ovirt.engine.core.common.utils.SimpleDependencyInjector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class SysprepHandler { private static final Map<String, Integer> timeZoneIndex = new HashMap<>(); private static OsRepository osRepository = SimpleDependencyInjector.getInstance().get(OsRepository.class); private static final Logger log = LoggerFactory.getLogger(SysprepHandler.class); static { initTimeZones(); } public static String getSysPrep(VM vm, SysPrepParams sysPrepParams) { String sysPrepContent = ""; boolean useCustomScript = vm.getVmInit() != null && !StringUtils.isEmpty(vm.getVmInit().getCustomScript()); if (useCustomScript) { sysPrepContent = vm.getVmInit().getCustomScript(); } else { sysPrepContent = loadFile(osRepository.getSysprepPath(vm.getVmOsId(), null)); } String productKey = osRepository.getProductKey(vm.getVmOsId(), null); sysPrepContent = replaceProductKey(sysPrepContent, productKey, useCustomScript); String domain = (vm.getVmInit() != null && vm.getVmInit().getDomain() != null) ? vm.getVmInit().getDomain() : ""; String hostName = (vm.getVmInit() != null && vm.getVmInit().getHostname() != null) ? vm.getVmInit().getHostname() : vm.getName(); Integer nameLength = Config.getValue(ConfigValues.MaxVmNameLengthSysprep); if (hostName != null && hostName.length() > nameLength) { hostName = hostName.substring(0, nameLength); } if (sysPrepContent.length() > 0) { sysPrepContent = populateSysPrepDomainProperties(sysPrepContent, domain, sysPrepParams); sysPrepContent = replace(sysPrepContent, "$ComputerName$", hostName != null ? hostName : ""); String timeZone = getTimeZone(vm); sysPrepContent = replace(sysPrepContent, "$TimeZone$", timeZone); String inputLocale = Config.getValue(ConfigValues.DefaultSysprepLocale); String uiLanguage = Config.getValue(ConfigValues.DefaultSysprepLocale); String systemLocale = Config.getValue(ConfigValues.DefaultSysprepLocale); String userLocale = Config.getValue(ConfigValues.DefaultSysprepLocale); String activeDirectoryOU = ""; String adminPassword = ""; String orgName = Config.getValue(ConfigValues.OrganizationName); if (vm.getVmInit() != null) { if (!StringUtils.isEmpty(vm.getVmInit().getInputLocale())) { inputLocale = vm.getVmInit().getInputLocale(); } if (!StringUtils.isEmpty(vm.getVmInit().getUiLanguage())) { uiLanguage = vm.getVmInit().getUiLanguage(); } if (!StringUtils.isEmpty(vm.getVmInit().getSystemLocale())) { systemLocale = vm.getVmInit().getSystemLocale(); } if (!StringUtils.isEmpty(vm.getVmInit().getUserLocale())) { userLocale = vm.getVmInit().getUserLocale(); } if (!StringUtils.isEmpty(vm.getVmInit().getActiveDirectoryOU())) { activeDirectoryOU = vm.getVmInit().getActiveDirectoryOU(); } if (!StringUtils.isEmpty(vm.getVmInit().getRootPassword())) { adminPassword = vm.getVmInit().getRootPassword(); } if (!StringUtils.isEmpty(vm.getVmInit().getOrgName())) { orgName = vm.getVmInit().getOrgName(); } } sysPrepContent = replace(sysPrepContent, "$SetupUiLanguageUiLanguage$", uiLanguage); sysPrepContent = replace(sysPrepContent, "$InputLocale$", inputLocale); sysPrepContent = replace(sysPrepContent, "$UILanguage$", uiLanguage); sysPrepContent = replace(sysPrepContent, "$SystemLocale$", systemLocale); sysPrepContent = replace(sysPrepContent, "$UserLocale$", userLocale); sysPrepContent = replace(sysPrepContent, "$MachineObjectOU$", activeDirectoryOU); sysPrepContent = replace(sysPrepContent, "$OrgName$", orgName); sysPrepContent = replace(sysPrepContent, "$AdminPassword$", adminPassword); } return sysPrepContent; } /** * Replaces the ProductKey in the sysprep file if the product key is provided in osinfo. * If the product key is not provided, the whole ProductKey section is removed from the * sysprep file. The reason is that the sysprep operation fails if the sysprep file contains * an empty product key. * * In case the custom script is provided, the product key section is not removed even * the product key itself is not provided. * The reason is that the custom sysprep file can already contain the product key, so the * product key does not need to be provided from osinfo and we don't want to remove it from * the sysprep file. */ static String replaceProductKey(String sysPrepContent, String productKey, boolean useCustomScript) { if (StringUtils.isNotEmpty(productKey)) { return replace(sysPrepContent, "$ProductKey$", productKey); } if (!useCustomScript) { return replaceMultiline(sysPrepContent, "<ProductKey>.*?</ProductKey>", ""); } return sysPrepContent; } private static String populateSysPrepDomainProperties(String sysPrepContent, String domain, SysPrepParams sysPrepParams) { String domainName; String adminUserName; String adminPassword; if (sysPrepParams == null || StringUtils.isEmpty(sysPrepParams .getSysPrepDomainName())) { domainName = useDefaultIfNull("domain", domain, "", true); } else { domainName = sysPrepParams.getSysPrepDomainName(); } if (sysPrepParams == null || sysPrepParams.getSysPrepUserName() == null || sysPrepParams.getSysPrepPassword() == null) { adminUserName = Config.getValue(ConfigValues.SysPrepDefaultUser); adminPassword = Config.getValue(ConfigValues.SysPrepDefaultPassword); } else { adminUserName = sysPrepParams.getSysPrepUserName(); adminPassword = sysPrepParams.getSysPrepPassword(); } // Get values from SysPrepParams - alternative for username,password and domain. sysPrepContent = replace(sysPrepContent, "$JoinDomain$", domainName); sysPrepContent = replace(sysPrepContent, "$DomainAdmin$", adminUserName); sysPrepContent = replace(sysPrepContent, "$DomainAdminPassword$", adminPassword); return sysPrepContent; } static String replaceMultiline(String sysPrepContent, String pattern, String value) { Pattern p = Pattern.compile(pattern, Pattern.DOTALL); return p.matcher(sysPrepContent).replaceAll(value); } static String replace(String sysPrepContent, String pattern, String value) { return sysPrepContent.replaceAll(Pattern.quote(pattern), Matcher.quoteReplacement(value)); } private static String useDefaultIfNull(String key, String value, String defaultValue, boolean printDefaultValue) { if (value == null && printDefaultValue) { log.warn("Could not find value for key '{}'. Going to use default value of: '{}'", key, defaultValue); } return value != null ? value : defaultValue; } private static String getTimeZone(VM vm) { String timeZone = null; // Can be empty if the VM was imported. if (vm.getVmInit() != null && StringUtils.isNotEmpty(vm.getVmInit().getTimeZone())) { timeZone = vm.getVmInit().getTimeZone(); } else { timeZone = Config.getValue(ConfigValues.DefaultWindowsTimeZone); } if (osRepository.isTimezoneValueInteger(vm.getStaticData(). getOsId(), null)) { // send correct time zone as sysprep expect to get it (a wierd number) return getTimezoneIndexByKey(timeZone); } return timeZone; } private static String getSysprepDir() { return Config.getValue(ConfigValues.DataDir) + File.separator + "sysprep"; } private static String loadFile(String fileName) { String content = ""; fileName = ConfigUtil.resolvePath(getSysprepDir(), fileName); Path path = Paths.get(fileName); if (path.toFile().exists()) { try { content = new String(Files.readAllBytes(path)); } catch (Exception e) { log.error("Failed to read sysprep template '{}': {}", fileName, e.getMessage()); log.debug("Exception", e); } } else { log.error("Sysprep template: '{}' not found", fileName); } return content; } // exclude 13 and 158 - not in the sysprep documentation! // {"Arabic Standard Time", 158}, // {"Jerusalem Standard Time", 135}, // {"Mexico Standard Time 2", 13}, // {"Malay Peninsula Standard Time", 215}, // TimeZone reference from Microsoft: // http://msdn.microsoft.com/en-us/library/ms912391(v=winembedded.11).aspx private static void initTimeZones() { timeZoneIndex.put("(GMT+04:30) Afghanistan Standard Time", 175); timeZoneIndex.put("(GMT-09:00) Alaskan Standard Time", 3); timeZoneIndex.put("(GMT+03:00) Arab Standard Time", 150); timeZoneIndex.put("(GMT+04:00) Arabian Standard Time", 165); timeZoneIndex.put("(GMT+03:00) Arabic Standard Time", 158); timeZoneIndex.put("(GMT-04:00) Atlantic Standard Time", 50); /** Before September 2007 Venezuela observed AST */ timeZoneIndex.put("(GMT-04:30) Venezuelan Standard Time", 50); // timeZoneIndex.put("(GMT+04:00) Azerbaijan Standard Time", xxx); timeZoneIndex.put("(GMT-10:00) Azores Standard Time", 80); timeZoneIndex.put("(GMT-06:00) Canada Central Standard Time", 25); timeZoneIndex.put("(GMT-01:00) Cape Verde Standard Time", 83); timeZoneIndex.put("(GMT+04:00) Caucasus Standard Time", 170); timeZoneIndex.put("(GMT+09:30) Cen. Australia Standard Time", 250); timeZoneIndex.put("(GMT-06:00) Central America Standard Time", 33); timeZoneIndex.put("(GMT+06:00) Central Asia Standard Time", 195); // timeZoneIndex.put("(GMT-04:00) Central Brazilian Standard Time ", xxx); timeZoneIndex.put("(GMT+01:00) Central Europe Standard Time", 95); timeZoneIndex.put("(GMT+01:00) Central European Standard Time", 100); timeZoneIndex.put("(GMT+11:00) Central Pacific Standard Time", 280); timeZoneIndex.put("(GMT-06:00) Central Standard Time", 20); timeZoneIndex.put("(GMT-06:00) Central Standard Time (Mexico)", 30); timeZoneIndex.put("(GMT+08:00) China Standard Time", 210); timeZoneIndex.put("(GMT-12:00) Dateline Standard Time", 0); timeZoneIndex.put("(GMT+03:00) E. Africa Standard Time", 155); timeZoneIndex.put("(GMT+10:00) E. Australia Standard Time", 260); timeZoneIndex.put("(GMT+02:00) E. Europe Standard Time", 115); timeZoneIndex.put("(GMT-03:00) E. South America Standard Time", 65); timeZoneIndex.put("(GMT-05:00) Eastern Standard Time", 35); timeZoneIndex.put("(GMT+02:00) Egypt Standard Time", 120); timeZoneIndex.put("(GMT+05:00) Ekaterinburg Standard Time", 180); timeZoneIndex.put("(GMT+12:00) Fiji Standard Time", 285); timeZoneIndex.put("(GMT+02:00) FLE Standard Time", 125); timeZoneIndex.put("(GMT+04:00) Georgian Standard Time", 70); timeZoneIndex.put("(GMT) GMT Standard Time", 85); timeZoneIndex.put("(GMT-03:00) Greenland Standard Time", 73); timeZoneIndex.put("(GMT) Greenwich Standard Time", 90); timeZoneIndex.put("(GMT+02:00) GTB Standard Time", 130); timeZoneIndex.put("(GMT-10:00) Hawaiian Standard Time", 2); timeZoneIndex.put("(GMT+05:30) India Standard Time", 190); timeZoneIndex.put("(GMT+03:00) Iran Standard Time", 160); timeZoneIndex.put("(GMT+02:00) Israel Standard Time", 135); timeZoneIndex.put("(GMT+09:00) Korea Standard Time", 230); timeZoneIndex.put("(GMT-02:00) Mid-Atlantic Standard Time", 75); timeZoneIndex.put("(GMT-07:00) Mountain Standard Time", 10); timeZoneIndex.put("(GMT+06:30) Myanmar Standard Time", 203); timeZoneIndex.put("(GMT+06:00) N. Central Asia Standard Time", 201); timeZoneIndex.put("(GMT+05:45) Nepal Standard Time", 193); timeZoneIndex.put("(GMT+12:00) New Zealand Standard Time", 290); timeZoneIndex.put("(GMT-03:30) Newfoundland Standard Time", 60); timeZoneIndex.put("(GMT+08:00) North Asia East Standard Time", 227); timeZoneIndex.put("(GMT+07:00) North Asia Standard Time", 207); timeZoneIndex.put("(GMT+04:00) Pacific SA Standard Time", 56); timeZoneIndex.put("(GMT-08:00) Pacific Standard Time", 4); timeZoneIndex.put("(GMT+01:00) Romance Standard Time", 105); timeZoneIndex.put("(GMT+03:00) Russian Standard Time", 145); timeZoneIndex.put("(GMT-03:00) SA Eastern Standard Time", 70); timeZoneIndex.put("(GMT-05:00) SA Pacific Standard Time", 45); timeZoneIndex.put("(GMT-04:00) SA Western Standard Time", 55); timeZoneIndex.put("(GMT-11:00) Samoa Standard Time", 1); timeZoneIndex.put("(GMT+07:00) SE Asia Standard Time", 205); timeZoneIndex.put("(GMT+08:00) Singapore Standard Time", 215); timeZoneIndex.put("(GMT+02:00) South Africa Standard Time", 140); timeZoneIndex.put("(GMT+06:00) Sri Lanka Standard Time", 200); timeZoneIndex.put("(GMT+08:00) Taipei Standard Time", 220); timeZoneIndex.put("(GMT+10:00) Tasmania Standard Time", 265); timeZoneIndex.put("(GMT+09:00) Tokyo Standard Time", 235); timeZoneIndex.put("(GMT+13:00) Tonga Standard Time", 300); timeZoneIndex.put("(GMT-05:00) US Eastern Standard Time", 40); timeZoneIndex.put("(GMT-07:00) US Mountain Standard Time", 15); timeZoneIndex.put("(GMT+10:00) Vladivostok Standard Time", 270); timeZoneIndex.put("(GMT+08:00) W. Australia Standard Time", 225); timeZoneIndex.put("(GMT+01:00) W. Central Africa Standard Time", 113); timeZoneIndex.put("(GMT+01:00) W. Europe Standard Time", 110); timeZoneIndex.put("(GMT+05:00) West Asia Standard Time", 185); timeZoneIndex.put("(GMT+10:00) West Pacific Standard Time", 275); timeZoneIndex.put("(GMT+09:00) Yakutsk Standard Time", 240); } // we use: // key = "Afghanistan Standard Time" // value = "(GMT+04:30) Afghanistan Standard Time" public static String getTimezoneKey(String value) { return value.substring(value.indexOf(' ') + 1); } // we get "Afghanistan Standard Time" we return "175" // the "Afghanistan Standard Time" is the vm Key that we get from the method getTimezoneKey() // "175" is the timezone keys that xp/2003 excpect to get, vista/7/2008 gets "Afghanistan Standard Time" public static String getTimezoneIndexByKey(String key) { for (Map.Entry<String, Integer> timeZoneEntry : timeZoneIndex.entrySet()) { if (getTimezoneKey(timeZoneEntry.getKey()).equals(key)) { return timeZoneEntry.getValue().toString(); } } log.error("getTimezoneIndexByKey: cannot find timezone key '{}'", key); return key; } }