/**
* This file is part of CloudML [ http://cloudml.org ]
*
* Copyright (C) 2012 - SINTEF ICT
* Contact: Franck Chauvel <franck.chauvel@sintef.no>
*
* Module: root
*
* CloudML is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* CloudML is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with CloudML. If not, see
* <http://www.gnu.org/licenses/>.
*/
package org.cloudml.indicators;
import eu.diversify.trio.core.Component;
import org.cloudml.core.Deployment;
import eu.diversify.trio.core.System;
import eu.diversify.trio.core.Tag;
import java.util.*;
import org.cloudml.core.ComponentInstance;
import org.cloudml.core.Property;
import static org.cloudml.indicators.Selection.*;
/**
* Convert a CloudML model into a Trio System.
*
* The conversion is configured with the strategy to extract the conditions
* under which a component fails when failures occurs in its environment. Two
* strategies are possible:
*
* (i) only existing dependencies, which reflect the actual relationships
* explicitly defined in the given model,
*
* (ii) all possible dependencies, which reflects the possibility for a
* component to use alternative service/execution platform provider to overcome
* failures.
*/
public class TrioExporter {
private final DependencyExtractor extractRequirement;
/**
* Create a new TrioExporter, with a specific strategy for extracting the
* requirements that characterise each CloudML component.
*
* @param strategy the requirement extraction strategy to use.
*/
public TrioExporter(DependencyExtractor strategy) {
requireValidExtractionStrategy(strategy);
this.extractRequirement = strategy;
}
private void requireValidExtractionStrategy(DependencyExtractor strategy) throws IllegalArgumentException {
if (strategy == null) {
throw new IllegalArgumentException("'null' is not a valid requirement extraction strategy.");
}
}
/**
* Convert the given CloudML deployment model into a TRIO system
*
* @param model the CloudML model to be converted
*
* @return an equivalent Trio system
*/
public System asTrioSystem(Deployment model) {
requireValidDeployment(model);
final Map<String, List<String>> tagged = prepareTagMap();
final ArrayList<Component> trioComponents = new ArrayList<Component>();
for (ComponentInstance each: model.getComponentInstances()) {
extractTags(each, tagged);
trioComponents.add(new Component(each.getName(), extractRequirement.from(each)));
}
return new System(trioComponents, buildTags(tagged));
}
/**
* Abort if the given deployment model is not valid
*/
private void requireValidDeployment(Deployment model) throws IllegalArgumentException {
if (model == null) {
throw new IllegalArgumentException("Unable to build a TRIO system from 'null'");
}
}
/**
* Extract the tag that can be identified on the given component instance.
*
* @param componentInstance the component whose tags are needed
* @param tagged the already initialised maps of tags, to be filled in
*/
private void extractTags(ComponentInstance componentInstance, Map<String, List<String>> tagged) {
assert componentInstance != null: "Unable to extract tags from 'null'";
assert !tagged.isEmpty(): "The map of tag must have been initialized";
if (componentInstance.isExternal()) {
tagged.get(EXTERNAL.getLabel()).add(componentInstance.getName());
if (componentInstance.asExternal().isVM()) {
tagged.get(VM.getLabel()).add(componentInstance.getName());
}
}
if (componentInstance.isInternal()) {
tagged.get(INTERNAL.getLabel()).add(componentInstance.getName());
}
if (isService(componentInstance)) {
tagged.get(SERVICE.getLabel()).add(componentInstance.getName());
} else {
tagged.get(NOT_SERVICE.getLabel()).add(componentInstance.getName());
}
}
/**
* Check whether the given component is a marked as being a "service".
*
* @param instance the component instance of interest
* @return true if the instance is marked as being a service, false
* otherwise
*/
private boolean isService(ComponentInstance instance) {
assert instance != null: "Cannot check if 'null' is a service ";
assert instance.getType() != null: "The given component has no type!";
if (instance.getType().hasProperty(IS_SERVICE)) {
final Property isService = instance.getType().getProperties().get(IS_SERVICE);
final String escapedValue = isService.getValue().trim().toUpperCase();
if (YES_MARKERS.contains(escapedValue)) {
return true;
}
}
return false;
}
private static final List<String> YES_MARKERS
= Arrays.asList(new String[]{
"YES",
"TRUE",
"OK"}
);
public static final String IS_SERVICE = "is_service";
/**
* @return the map where each tag is associated with an empty collection
*/
private Map<String, List<String>> prepareTagMap() {
final Map<String, List<String>> tagged = new HashMap<String, List<String>>();
tagged.put(INTERNAL.getLabel(), new ArrayList<String>());
tagged.put(EXTERNAL.getLabel(), new ArrayList<String>());
tagged.put(VM.getLabel(), new ArrayList<String>());
tagged.put(SERVICE.getLabel(), new ArrayList<String>());
tagged.put(NOT_SERVICE.getLabel(), new ArrayList<String>());
assert tagged.size() == Selection.values().length - 1:
"Error some tags are properly initialized!";
return tagged;
}
/**
* @return a list of Trio::Tags, computed from the given maps of tags
* @param tagged the map of tags
*/
private List<Tag> buildTags(final Map<String, List<String>> tagged) {
assert tagged.size() == Selection.values().length - 1:
"tags were not properly collected/initialized.";
final List<Tag> tags = new ArrayList<Tag>();
for (String eachTag: tagged.keySet()) {
final List<String> taggedComponents = tagged.get(eachTag);
if (!taggedComponents.isEmpty()) {
tags.add(new Tag(eachTag, taggedComponents));
}
}
return tags;
}
}