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

dev.galasa.linux.internal.shared.LinuxSharedProvisioner Maven / Gradle / Ivy

The newest version!
/*
 * Copyright contributors to the Galasa project
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package dev.galasa.linux.internal.shared;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import dev.galasa.framework.spi.DssAdd;
import dev.galasa.framework.spi.DssSwap;
import dev.galasa.framework.spi.DynamicStatusStoreMatchException;
import dev.galasa.framework.spi.IDssAction;
import dev.galasa.framework.spi.IDynamicStatusStoreService;
import dev.galasa.framework.spi.IResourcePoolingService;
import dev.galasa.framework.spi.ResourceUnavailableException;
import dev.galasa.linux.LinuxManagerException;
import dev.galasa.linux.OperatingSystem;
import dev.galasa.linux.internal.LinuxManagerImpl;
import dev.galasa.linux.internal.properties.LinuxCapabilities;
import dev.galasa.linux.internal.properties.LinuxOperatingSystem;
import dev.galasa.linux.internal.properties.MaximumSlots;
import dev.galasa.linux.internal.properties.SharedLinuxImages;
import dev.galasa.linux.internal.properties.SharedLinuxPriority;
import dev.galasa.linux.internal.properties.UsernamePool;
import dev.galasa.linux.spi.ILinuxProvisionedImage;
import dev.galasa.linux.spi.ILinuxProvisioner;

public class LinuxSharedProvisioner implements ILinuxProvisioner {

    private final Log                                logger = LogFactory.getLog(getClass());

    private final LinuxManagerImpl                   manager;
    private final IDynamicStatusStoreService         dss;

    public LinuxSharedProvisioner(LinuxManagerImpl manager) {

        this.manager = manager;
        this.dss = this.manager.getDss();
    }

    @Override
    public ILinuxProvisionedImage provisionLinux(String tag, OperatingSystem operatingSystem, List capabilities)
            throws LinuxManagerException, ResourceUnavailableException {
        return provisionLinux(tag, operatingSystem, capabilities, 1);
    }

    private ILinuxProvisionedImage provisionLinux(String tag, OperatingSystem operatingSystem, List capabilities, int retryCount)
                throws LinuxManagerException, ResourceUnavailableException {
        
        if (retryCount > 10) {
            // tried too many times,  allow another provisioner to attempt
            throw new ResourceUnavailableException("Failed to reserve shared linux image");
        }
        

        try {
            List images = SharedLinuxImages.get();
            if (images.isEmpty()) {
                return null;
            }

            List  availableImages = new ArrayList(images.size());
            for(String image : images) {
                if (image == null || image.isEmpty()) {
                    continue;
                }

                availableImages.add(new AvailableImage(image));
            }

            // Remove all those servers that do not match the operating system or doesn't have the capabilities
            String choosenOperatingSystem = null;
            if (operatingSystem != OperatingSystem.any) {
                choosenOperatingSystem = operatingSystem.name();
            }
            Iterator availableImagesIterator = availableImages.iterator();
            nextImage:
            while(availableImagesIterator.hasNext()) {
                AvailableImage image = availableImagesIterator.next();

                if (choosenOperatingSystem != null) {
                    String availableOperatingSystem = LinuxOperatingSystem.get(image.getHostId());
                    if (availableOperatingSystem == null) {
                        availableImagesIterator.remove();
                        continue;
                    }
                    if (!availableOperatingSystem.equalsIgnoreCase(choosenOperatingSystem)) {
                        availableImagesIterator.remove();
                        continue;
                    }
                }
                
                // First check to see the the tests MUST request a capability this server provides
                List availableCapabilities = LinuxCapabilities.get(image.getHostId());
                if (!availableCapabilities.isEmpty()) {
                    for(String availableCapability : availableCapabilities) {
                        if (availableCapability.startsWith("+")) {
                            String actualAvailableCapability = availableCapability.substring(1);
                            boolean requestedCapability = false;
                            for(String choosenCapability : capabilities) {
                                if (choosenCapability.equalsIgnoreCase(actualAvailableCapability)) {
                                    requestedCapability = true;
                                    break;
                                }
                            }
                            if (!requestedCapability) {
                                availableImagesIterator.remove();
                                continue nextImage;
                            }
                        }
                    }
                }

                // Now check the server provides the capabilities requested
                if (!capabilities.isEmpty()) {
                    for(String choosenCapability : capabilities) {
                        if (choosenCapability == null || choosenCapability.isEmpty()) {
                            continue;
                        }

                        boolean found = false;
                        for (String availableCapability : availableCapabilities) {
                            if (availableCapability.startsWith("+")) {
                                availableCapability = availableCapability.substring(1);
                            }
                            if (availableCapability.equalsIgnoreCase(choosenCapability)) {
                                found = true;
                                break;
                            }
                        }

                        if (!found) {
                            availableImagesIterator.remove();
                            continue nextImage;
                        }
                    }
                }
            }

            // Are there any suitable images left?
            if (availableImages.isEmpty()) {
                logger.trace("There are no suitable shared Linux images available");
                return null;
            }

            // Find the image with the greatest capacity

            availableImagesIterator = availableImages.iterator();
            while(availableImagesIterator.hasNext()) {
                AvailableImage image = availableImagesIterator.next();
                String hostId = image.getHostId();

                int maxSlots = MaximumSlots.get(hostId);
                if (maxSlots < 1) {
                    availableImagesIterator.remove();
                    continue;
                }

                String sUsedSlots = this.dss.get(hostId + ".used.slots");
                if (sUsedSlots == null || sUsedSlots.isEmpty()) {
                    sUsedSlots = "0";
                }

                int usedSlots = Integer.parseInt(sUsedSlots);
                if (usedSlots >= maxSlots) {
                    availableImagesIterator.remove();
                    continue;
                } else {
                    float freePercentage = ((float)maxSlots - (float)usedSlots) / (float)maxSlots * 100.0f;
                    image.setSpareCapacityPercentage(Math.round(freePercentage));
                }
            }
            
            if (availableImages.isEmpty()) {
                logger.trace("There are no suitable shared Linux images with spare capacity");
                throw new ResourceUnavailableException("Failed to reserve shared linux image");
            }

            Collections.sort(availableImages);
            
            AvailableImage selectedImage = availableImages.get(0);
            
            String hostId = selectedImage.hostId;
            // Reserve a slot
            int maxSlots = MaximumSlots.get(hostId);
            if (maxSlots < 1) {
                // must have just changed,  redrive the method again
                return provisionLinux(tag, operatingSystem, capabilities, retryCount++);
            }

            int usedSlots = 0;
            String sUsedSlots = this.dss.get(hostId + ".used.slots");
            if (sUsedSlots != null) {
                usedSlots = Integer.parseInt(sUsedSlots);
            }
            
            usedSlots++;
            
            if (usedSlots > maxSlots) {
                // must have just changed,  redrive the method again
                return provisionLinux(tag, operatingSystem, capabilities, retryCount++);
            }
            
            String runName = this.manager.getFramework().getTestRunName();
            
            IDssAction slotsUpdate = null;
            if (sUsedSlots == null) {
                slotsUpdate = new DssAdd(hostId + ".used.slots", Integer.toString(usedSlots));
            } else {
                slotsUpdate = new DssSwap(hostId + ".used.slots", sUsedSlots, Integer.toString(usedSlots));
            }
            DssAdd runImage = new DssAdd("run." + runName + ".image." + hostId, "active");

            // We needs a unique userid to use on the shared linux box, so reserve that at the sametime
            
            List instanceNamePool = UsernamePool.get(hostId);
            IResourcePoolingService poolingService = this.manager.getFramework().getResourcePoolingService();
            
            // *** Get a list of potential usernames
            ArrayList exclude = new ArrayList<>();
            List possibleNames = poolingService.obtainResources(instanceNamePool, exclude, 10, 1, this.dss,
                    "image." + hostId + ".username.");

            if (possibleNames.isEmpty()) {
                // no available usernames on this image,   there should always be more usernames than slots
                return provisionLinux(tag, operatingSystem, capabilities, retryCount++);
            }

            String possibleUsername = possibleNames.get(0);
            
            // add active and ownership
            DssAdd username = new DssAdd("image." + hostId + ".username." + possibleUsername, runName);
            DssAdd runUsername = new DssAdd("run." + runName + ".image." + hostId + ".username." + possibleUsername, "active");
            
            try {
                this.dss.performActions(slotsUpdate, runImage, username, runUsername);
            } catch(DynamicStatusStoreMatchException e) {
                //*** collision on either the slot increment or the username,  so simply retry
                Thread.sleep(200 + new SecureRandom().nextInt(200)); // *** To avoid race conditions
                return provisionLinux(tag, operatingSystem, capabilities, retryCount++);
            }
            
            logger.info("Selected shared Linux image for " + tag + " host id is " + hostId + ", with username " + possibleUsername);

            return new LinuxSharedImage(manager, tag, hostId, possibleUsername);
        } catch (Exception e) {
            throw new LinuxManagerException("Unable to provision the Linux DSE", e);
        }
    }

    @Override
    public int getLinuxPriority() {
        return SharedLinuxPriority.get();
    }


    private static class AvailableImage implements Comparable {

        private final String hostId;
        private int spareCapacityPercentage = 0;

        private AvailableImage(String hostId) {
            this.hostId = hostId;
        }

        private void setSpareCapacityPercentage(int spareCapacityPercentage) {
            this.spareCapacityPercentage = spareCapacityPercentage;
        }

        private String getHostId() {
            return hostId;
        }

        @Override
        public int compareTo(AvailableImage o) {
            return o.spareCapacityPercentage - this.spareCapacityPercentage;
        }

    }
    
    

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy