/**
* 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.ambari.server.state.stack;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.inject.Singleton;
/**
* Class that encapsulates OS family logic
*/
@Singleton
public class OsFamily {
private final static String OS_FAMILY_UBUNTU = "ubuntu";
private final static String OS_FAMILY_SUSE = "suse";
private final static String OS_FAMILY_REDHAT = "redhat";
private final String os_pattern = "([\\D]+|(?:[\\D]+[\\d]+[\\D]+))([\\d]*)";
private final String OS_DISTRO = "distro";
private final String OS_VERSION = "versions";
private final String LOAD_CONFIG_MSG = "Could not load OS family definition from %s file";
private final String FILE_NAME = "os_family.json";
private final Logger LOG = LoggerFactory.getLogger(OsFamily.class);
private Map<String, JsonOsFamilyEntry> osMap = null;
private JsonOsFamilyRoot jsonOsFamily = null;
/**
* Initialize object
* @param conf Configuration instance
*/
public OsFamily(Configuration conf){
init(conf.getSharedResourcesDirPath());
}
/**
* Initialize object
* @param properties list of properties
*/
public OsFamily(Properties properties){
init(properties.getProperty(Configuration.SHARED_RESOURCES_DIR.getKey()));
}
private void init(String SharedResourcesPath){
FileInputStream inputStream = null;
try {
File f = new File(SharedResourcesPath, FILE_NAME);
if (!f.exists()) {
throw new Exception();
}
inputStream = new FileInputStream(f);
Type type = new TypeToken<JsonOsFamilyRoot>() {}.getType();
Gson gson = new Gson();
jsonOsFamily = gson.fromJson(new InputStreamReader(inputStream), type);
osMap = jsonOsFamily.getMapping();
} catch (Exception e) {
LOG.error(String.format(LOAD_CONFIG_MSG, new File(SharedResourcesPath, FILE_NAME).toString()));
throw new RuntimeException(e);
} finally {
IOUtils.closeQuietly(inputStream);
}
}
/**
* Separate os name from os major version
* @param os the os
* @return separated os name and os version
*/
private Map<String,String> parse_os(String os){
Map<String,String> pos = new HashMap<>();
Pattern r = Pattern.compile(os_pattern);
Matcher m = r.matcher(os);
if (m.matches()) {
pos.put(OS_DISTRO, m.group(1));
pos.put(OS_VERSION, m.group(2));
} else {
pos.put(OS_DISTRO, os);
pos.put(OS_VERSION, "");
}
return pos;
}
/**
* Gets the array of compatible OS types
* @param os the os
* @return all types that are compatible with the supplied type
*/
public Set<String> findTypes(String os) {
Map<String,String> pos = parse_os(os);
for ( String family : osMap.keySet()) {
JsonOsFamilyEntry fam = osMap.get(family);
if (fam.getDistro().contains(pos.get(OS_DISTRO)) && fam.getVersions().contains(pos.get(OS_VERSION))){
Set<String> data= new HashSet<>();
for (String item: fam.getDistro()) {
data.add(item + pos.get(OS_VERSION));
}
return Collections.unmodifiableSet(data);
}
}
return Collections.emptySet();
}
/**
* Finds the family for the specific OS + it's version number
* @param os the OS
* @return the family, or <code>null</code> if not defined
*/
public String find(String os) {
Map<String,String> pos = parse_os(os);
for ( String family : osMap.keySet()) {
JsonOsFamilyEntry fam = osMap.get(family);
if (fam.getDistro().contains(pos.get(OS_DISTRO)) && fam.getVersions().contains(pos.get(OS_VERSION))){
return family + pos.get(OS_VERSION);
}
}
return null;
}
/**
* Finds the family for the specific OS
* @param os the OS
* @return the family, or <code>null</code> if not defined
*/
public String find_family(String os) {
Map<String,String> pos = parse_os(os);
for ( String family : osMap.keySet()) {
JsonOsFamilyEntry fam = osMap.get(family);
if (fam.getDistro().contains(pos.get(OS_DISTRO)) && fam.getVersions().contains(pos.get(OS_VERSION))){
return family;
}
}
return null;
}
/**
* Form list of all supported os types
* @return one dimension list with os types
*/
public Set<String> os_list(){
Set<String> r= new HashSet<>();
for ( String family : osMap.keySet()) {
JsonOsFamilyEntry fam = osMap.get(family);
for (String version: fam.getVersions()){
Set<String> data= new HashSet<>();
for (String item: fam.getDistro()) {
data.add(item + version);
}
r.addAll(data);
}
}
return r;
}
public boolean isUbuntuFamily(String osType) {
return isOsInFamily(osType, OS_FAMILY_UBUNTU);
}
public boolean isSuseFamily(String osType) {
return isOsInFamily(osType, OS_FAMILY_SUSE);
}
public boolean isRedhatFamily(String osType) {
return isOsInFamily(osType, OS_FAMILY_REDHAT);
}
public boolean isOsInFamily(String osType, String osFamily) {
String familyOfOsType = find_family(osType);
return (familyOfOsType != null && isFamilyExtendedByFamily(familyOfOsType, osFamily));
}
private boolean isFamilyExtendedByFamily(String currentFamily, String family) {
return (currentFamily.equals(family) || getOsFamilyParent(currentFamily)!=null && isFamilyExtendedByFamily(getOsFamilyParent(currentFamily), family));
}
private String getOsFamilyParent(String osFamily) {
return osMap.get(osFamily).getExtendsFamily();
}
/**
* @return the map of aliases
*/
public Map<String, String> getAliases() {
return (null == jsonOsFamily || null == jsonOsFamily.getAliases()) ?
Collections.<String, String>emptyMap() : jsonOsFamily.getAliases();
}
}