All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.fenzo.VMCollection Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * Copyright 2015 Netflix, Inc.
 *
 * Licensed 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 com.netflix.fenzo;

import com.netflix.fenzo.functions.Func1;
import org.apache.mesos.Protos;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class VMCollection {
    private static final String defaultGroupName = "DEFAULT";
    private static final Logger logger = LoggerFactory.getLogger(VMCollection.class);
    private final ConcurrentMap> vms;
    private final Func1 newVmCreator;
    private final String groupingAttrName;

    VMCollection(Func1 func1, String groupingAttrName) {
        vms = new ConcurrentHashMap<>();
        this.newVmCreator = func1;
        this.groupingAttrName = groupingAttrName;
    }

    Collection getAllVMs() {
        List result = new LinkedList<>();
        vms.values().forEach(m -> result.addAll(m.values()));
        return result;
    }

    Collection getGroups() {
        return Collections.unmodifiableCollection(vms.keySet());
    }

    /**
     * Create n pseudo VMs for each group by cloning a VM in each group.
     * @param groupCounts Map with keys contain group names and values containing number of agents to clone
     * @param ruleGetter Getter function for autoscale rules
     * @return Collection of pseudo host names added.
     */
    Map> clonePseudoVMsForGroups(Map groupCounts,
                                                      Func1 ruleGetter,
                                                      Predicate vmFilter
    ) {
        if (groupCounts == null || groupCounts.isEmpty())
            return Collections.emptyMap();
        InternalVMCloner vmCloner = new InternalVMCloner();
        Map> result = new HashMap<>();
        long now = System.currentTimeMillis();
        for (String g: groupCounts.keySet()) {
            List hostnames = new LinkedList<>();
            result.put(g, hostnames);
            final ConcurrentMap avmsMap = vms.get(g);
            if (avmsMap != null) {
                final List vmsList = avmsMap.values()
                        .stream()
                        .filter(avm -> vmFilter.test(avm.getCurrTotalLease()))
                        .collect(Collectors.toList());
                if (vmsList != null && !vmsList.isEmpty()) {
                    // NOTE: a shortcoming here is that the attributes of VMs across a group may not be homogeneous.
                    // By creating one lease object and cloning from it, we pick one combination of the attributes
                    // and replicate across all the newly created pseudo VMs. It may be possible to capture the
                    // unique set of attributes across all existing VMs in the group and replicate that mix within
                    // the new pseudo VMs. However, we will not do that at this time. So, it is possible that some
                    // task constraints that depend on the variety of such attributes may fail the task placement.
                    // We will live with that limitation at this time.
                    VirtualMachineLease lease = vmCloner.getClonedMaxResourcesLease(vmsList);
                    if(logger.isDebugEnabled()) {
                        logger.debug("Cloned lease cpu={}, mem={}, disk={}, network={}", lease.cpuCores(),
                                lease.memoryMB(), lease.diskMB(), lease.networkMbps());
                        final Map attributeMap = lease.getAttributeMap();
                        if (attributeMap == null || attributeMap.isEmpty())
                            logger.debug("Cloned maxRes lease has empty attributeMap");
                        else
                            for (Map.Entry entry : attributeMap.entrySet())
                                logger.debug("Cloned maxRes lease attribute: " + entry.getKey() + ": " +
                                        (entry.getValue() == null ? "null" : entry.getValue().getText().getValue())
                                );
                    }
                    int n = groupCounts.get(g);
                    final AutoScaleRule rule = ruleGetter.call(g);
                    if (rule != null) {
                        int max = rule.getMaxSize();
                        if (max < Integer.MAX_VALUE && n > (max - vmsList.size()))
                            n = max - vmsList.size();
                    }
                    for (int i = 0; i < n; i++) {
                        final String hostname = createHostname(g, i);
                        addLease(vmCloner.cloneLease(lease, hostname, now));
                        if(logger.isDebugEnabled())
                            logger.debug("Added cloned lease for " + hostname);
                        hostnames.add(hostname);
                        // update total lease on the newly added VMs so they are available for use
                        getVmByName(hostname).ifPresent(AssignableVirtualMachine::updateCurrTotalLease);
                    }
                }
            }
        }
        return result;
    }

    private String createHostname(String g, int i) {
        return AssignableVirtualMachine.PseuoHostNamePrefix + g + "-" + i;
    }

    /**
     * Remove VM of given name from the given group. This is generally unsafe and intended only to be used by whoever
     * uses {@link VMCollection#clonePseudoVMsForGroups(Map, Func1, Predicate)}.
     * @param name
     * @param group
     */
    /* package */ AssignableVirtualMachine unsafeRemoveVm(String name, String group) {
        final ConcurrentMap vmsMap = vms.get(group);
        if (vmsMap != null) {
            return vmsMap.remove(name);
        }
        return null;
    }

    Optional getVmByName(String name) {
        return vms.values().stream()
                .flatMap(
                        (Function, Stream>) m
                                -> m.values().stream()
                )
                .filter(avm -> name.equals(avm.getHostname()))
                .findFirst();
    }

    AssignableVirtualMachine create(String host) {
        return create(host, defaultGroupName);
    }

    AssignableVirtualMachine create(String host, String group) {
        vms.putIfAbsent(group, new ConcurrentHashMap<>());
        AssignableVirtualMachine prev = null;
        if (!defaultGroupName.equals(group)) {
            if (vms.get(defaultGroupName) != null)
                prev = vms.get(defaultGroupName).remove(host);
        }
        vms.get(group).putIfAbsent(host, prev == null? newVmCreator.call(host) : prev);
        return vms.get(group).get(host);
    }

    AssignableVirtualMachine getOrCreate(String host) {
        final Optional vmByName = getVmByName(host);
        if (vmByName.isPresent())
            return vmByName.get();
        return create(host, defaultGroupName);
    }

    private AssignableVirtualMachine getOrCreate(String host, String group) {
        vms.putIfAbsent(group, new ConcurrentHashMap<>());
        final AssignableVirtualMachine avm = vms.get(group).get(host);
        if (avm != null)
            return avm;
        if (logger.isDebugEnabled())
            logger.debug("Creating new host " + host);
        return create(host, group);
    }

    boolean addLease(VirtualMachineLease l) {
        String group = l.getAttributeMap() == null? null :
                l.getAttributeMap().get(groupingAttrName) == null?
                        null :
                        l.getAttributeMap().get(groupingAttrName).getText().getValue();
        if (group == null)
            group = defaultGroupName;
        final AssignableVirtualMachine avm = getOrCreate(l.hostname(), group);
        return avm.addLease(l);
    }

    public int size() {
        final Optional size = vms.values().stream().map(Map::size).reduce((i1, i2) -> i1 + i2);
        return size.orElse(0);
    }

    public int size(String group) {
        final ConcurrentMap m = vms.get(group);
        return m == null? 0 : m.size();
    }

    public AssignableVirtualMachine remove(AssignableVirtualMachine avm) {
        final String group = avm.getAttrValue(groupingAttrName);
        AssignableVirtualMachine removed = null;
        if (group != null) {
            final ConcurrentMap m = vms.get(group);
            if (m != null) {
                removed = m.remove(avm.getHostname());
            }
        }
        if (removed != null)
            return removed;
        final ConcurrentMap m = vms.get(defaultGroupName);
        if (m != null)
            m.remove(avm.getHostname());
        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy