com.gemini.provision.security.openstack.SecurityProviderOpenStackImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.gemini.provision.security.openstack.SecurityProviderOpenStackImpl.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.gemini.provision.security.openstack;

import com.gemini.domain.common.GeminiSecurityGroupRuleDirection;
import com.gemini.domain.common.IPAddressType;
import com.gemini.domain.common.Protocol;
import com.gemini.domain.model.GeminiEnvironment;
import com.gemini.domain.model.GeminiSecurityGroup;
import com.gemini.domain.model.GeminiSecurityGroupRule;
import com.gemini.domain.model.GeminiServer;
import com.gemini.domain.model.GeminiTenant;
import com.gemini.provision.base.ProvisioningProviderResponseType;
import com.gemini.provision.security.base.SecurityProvider;
import com.google.inject.Singleton;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.openstack4j.api.Builders;
import org.openstack4j.api.OSClient;
import org.openstack4j.api.exceptions.ClientResponseException;
import org.openstack4j.model.compute.ActionResponse;
import org.openstack4j.model.compute.SecGroupExtension;
import org.openstack4j.model.compute.SecGroupExtension.Rule;
import org.openstack4j.model.network.SecurityGroup;
import org.openstack4j.model.network.SecurityGroupRule;
import org.openstack4j.openstack.OSFactory;
import org.pmw.tinylog.Logger;

/**
 *
 * @author schari
 */
@Singleton
public class SecurityProviderOpenStackImpl implements SecurityProvider {

    /**
     *
     * @return
     *
     * A brief description of this Security Provider. Currently (2/5/15) returns
     * unsupported functions as part of the text
     */
    @Override
    public String provisioningDesc() {
        return "OpenStack Security Provider. Attach/Detach Interface functions not supported.";
    }

    /**
     *
     * @param tenant
     * @param env
     * @return
     *
     * List all the security groups.
     */
    @Override
    public List<GeminiSecurityGroup> listAllSecurityGroups(GeminiTenant tenant, GeminiEnvironment env) {
        List<GeminiSecurityGroup> listSecGrps = Collections.synchronizedList(new ArrayList());

        //authenticate the session with the OpenStack installation
        OSClient os = OSFactory.builder().endpoint(env.getEndPoint())
                .credentials(env.getAdminUserName(), env.getAdminPassword()).tenantName(tenant.getName())
                .authenticate();
        if (os == null) {
            Logger.error("Failed to authenticate Tenant: {}",
                    ToStringBuilder.reflectionToString(tenant, ToStringStyle.MULTI_LINE_STYLE));
            return null;
        }

        //get the list from OpenStack
        List<? extends SecurityGroup> osSecGrps = os.networking().securitygroup().list();
        osSecGrps.stream().forEach(osSecGrp -> {
            //see if this security group already exists in the environment
            GeminiSecurityGroup tmpSecGrp = null;
            GeminiSecurityGroup newGemSecGrp = null;
            try {
                tmpSecGrp = env.getSecurityGroups().stream().filter(s -> s.getName().equals(osSecGrp.getName()))
                        .findFirst().get();
                tmpSecGrp.setProvisioned(true);
                tmpSecGrp.setCloudID(osSecGrp.getId());
            } catch (NoSuchElementException | NullPointerException ex) {
                //The OpenStack security group hasn't been mapped to an object on the Gemini side, so create it and add to the environment
                newGemSecGrp = new GeminiSecurityGroup();
                newGemSecGrp.setCloudID(osSecGrp.getId());
                newGemSecGrp.setProvisioned(true);
                newGemSecGrp.setName(osSecGrp.getName());
                newGemSecGrp.setDescription(osSecGrp.getDescription());
                env.addSecurityGroup(newGemSecGrp);
            }

            //check to see if this group's rules are mapped on the Gemini side
            List<? extends SecurityGroupRule> osSecGrpRules = osSecGrp.getRules();
            final GeminiSecurityGroup gemSecGrp = tmpSecGrp == null ? newGemSecGrp : tmpSecGrp;
            osSecGrpRules.stream().filter(osSecGrpRule -> osSecGrpRule != null).forEach(osSecGrpRule -> {
                GeminiSecurityGroupRule gemSecGrpRule = null;
                try {
                    gemSecGrpRule = gemSecGrp.getSecurityRules().stream()
                            .filter(sr -> sr.getCloudID().equals(osSecGrpRule.getId())).findFirst().get();
                } catch (NoSuchElementException | NullPointerException ex) {
                    //not an error or even log worthy ... just signifies that the rule
                    //on Gemini Side needs to be created
                }
                if (gemSecGrpRule == null) {
                    //the rule has not been mapped on the Gemini side, so create it
                    gemSecGrpRule = new GeminiSecurityGroupRule();
                }
                gemSecGrpRule.setCloudID(osSecGrpRule.getId());
                gemSecGrpRule.setProvisioned(true);
                gemSecGrpRule.setPortRangeMin(osSecGrpRule.getPortRangeMin());
                gemSecGrpRule.setPortRangeMax(osSecGrpRule.getPortRangeMax());
                gemSecGrpRule.setProtocol(Protocol.fromString(osSecGrpRule.getProtocol()));
                gemSecGrpRule.setDirection(
                        GeminiSecurityGroupRuleDirection.valueOf(osSecGrpRule.getDirection().toUpperCase()));
                gemSecGrpRule.setRemoteGroupId(osSecGrpRule.getRemoteGroupId());
                gemSecGrpRule.setRemoteIpPrefix(osSecGrpRule.getRemoteIpPrefix());
                gemSecGrpRule.setIpAddressType(IPAddressType.valueOf(osSecGrpRule.getEtherType()));
                //gemSecGrpRule.setCidr(osSecGrpRule.getRange().getCidr());
                gemSecGrpRule.setParent(gemSecGrp);
                gemSecGrp.addSecurityRule(gemSecGrpRule);
            });
            listSecGrps.add(gemSecGrp);
        });

        Logger.debug("Successfully retrieved all security groups Tenant: {} Env: {}", tenant.getName(),
                env.getName());
        return listSecGrps;
    }

    @Override
    public List<GeminiSecurityGroup> listServerSecurityGroups(GeminiTenant tenant, GeminiEnvironment env,
            GeminiServer server) {
        List<GeminiSecurityGroup> listSecGrps = Collections.synchronizedList(new ArrayList());

        //authenticate the session with the OpenStack installation
        OSClient os = OSFactory.builder().endpoint(env.getEndPoint())
                .credentials(env.getAdminUserName(), env.getAdminPassword()).tenantName(tenant.getName())
                .authenticate();
        if (os == null) {
            Logger.error("Failed to authenticate Tenant: {}",
                    ToStringBuilder.reflectionToString(tenant, ToStringStyle.MULTI_LINE_STYLE));
            return null;
        }

        //get the list from OpenStack
        List<? extends SecGroupExtension> osSecGrps = os.compute().securityGroups()
                .listServerGroups(server.getCloudID());
        osSecGrps.stream().forEach(osSecGrp -> {
            //see if this security group already exists in the environment
            GeminiSecurityGroup tmpSecGrp = env.getSecurityGroups().stream()
                    .filter(s -> s.getName().equals(osSecGrp.getName())).findFirst().get();
            GeminiSecurityGroup newGemSecGrp = null;
            if (tmpSecGrp == null) {
                //The OpenStack security group hasn't been mapped to an object on the Gemini side, so create it and add to the environment
                newGemSecGrp = new GeminiSecurityGroup();
                newGemSecGrp.setCloudID(osSecGrp.getId());
                newGemSecGrp.setProvisioned(true);
                newGemSecGrp.setName(osSecGrp.getName());
                newGemSecGrp.setDescription(osSecGrp.getDescription());
                env.addSecurityGroup(newGemSecGrp);
            }

            //check to see if this group's rules are mapped on the Gemini side
            List<? extends Rule> osSecGrpRules = osSecGrp.getRules();
            final GeminiSecurityGroup gemSecGrp = tmpSecGrp == null ? newGemSecGrp : tmpSecGrp;
            osSecGrpRules.stream().filter(osSecGrpRule -> osSecGrpRule != null).forEach(osSecGrpRule -> {
                GeminiSecurityGroupRule gemSecGrpRule = gemSecGrp.getSecurityRules().stream()
                        .filter(sr -> sr.getName().equals(osSecGrpRule.getName())).findFirst().get();
                if (gemSecGrpRule == null) {
                    //the rule has not been mapped on the Gemini side, so create it
                    gemSecGrpRule = new GeminiSecurityGroupRule();
                }
                gemSecGrpRule.setCloudID(osSecGrpRule.getId());
                gemSecGrpRule.setProvisioned(true);
                gemSecGrpRule.setPortRangeMin(osSecGrpRule.getFromPort());
                gemSecGrpRule.setPortRangeMax(osSecGrpRule.getToPort());
                gemSecGrpRule.setProtocol(Protocol.fromString(osSecGrpRule.getIPProtocol().toString()));
                gemSecGrpRule.setCidr(osSecGrpRule.getRange().getCidr());
                gemSecGrp.addSecurityRule(gemSecGrpRule);
            });

            //check if this security group is attached to server on the gemini side
            if (server.getSecGroupNames().stream().noneMatch(s -> s.equals(osSecGrp.getName()))) {
                //it isn't so add it to the server
                server.addSecGroupName(osSecGrp.getName());
            }

            listSecGrps.add(gemSecGrp);
        });

        Logger.debug("Successfully retrieved security groups Tenant: {} Env: {} server {}{", tenant.getName(),
                env.getName(), server.getName());
        return listSecGrps;
    }

    /*
     * Assumes that the GeminiSecurityGroup passed is already mapped.
     */
    @Override
    public List<GeminiSecurityGroupRule> listSecurityGroupRules(GeminiTenant tenant, GeminiEnvironment env,
            GeminiSecurityGroup securityGroup) {
        List<GeminiSecurityGroupRule> listSecGrpRules = Collections.synchronizedList(new ArrayList());

        //authenticate the session with the OpenStack installation
        OSClient os = OSFactory.builder().endpoint(env.getEndPoint())
                .credentials(env.getAdminUserName(), env.getAdminPassword()).tenantName(tenant.getName())
                .authenticate();
        if (os == null) {
            Logger.error("Failed to authenticate Tenant: {}",
                    ToStringBuilder.reflectionToString(tenant, ToStringStyle.MULTI_LINE_STYLE));
            return null;
        }

        List<? extends SecurityGroupRule> osSecGrpRules = null;
        try {
            osSecGrpRules = os.networking().securityrule().list();
        } catch (NullPointerException | ClientResponseException ex) {
            Logger.error("Failed to get rules for tenant: {} env: {} and security Group {}", tenant.getName(),
                    env.getName(), securityGroup.getName());
            return null;
        }

        osSecGrpRules.stream().filter(osSecGrpRule -> osSecGrpRule != null).forEach(osSecGrpRule -> {
            //find the rule in the security group object and add it to list
            GeminiSecurityGroupRule gemSecGrpRule = null;
            try {
                gemSecGrpRule = securityGroup.getSecurityRules().stream()
                        .filter(sr -> sr.getCloudID().equals(osSecGrpRule.getId())) //check cloud id too to ensure the mapping on Gemini is complete
                        .findFirst().get();
            } catch (NoSuchElementException ex) {
                //not an error or even log worthy ... just signifies that the rule
                //on Gemini Side needs to be created
            }
            if (gemSecGrpRule == null) {
                //the rule has not been mapped on the Gemini side, so create it
                gemSecGrpRule = new GeminiSecurityGroupRule();
                gemSecGrpRule.setCloudID(osSecGrpRule.getId());
                gemSecGrpRule.setProvisioned(true);
                gemSecGrpRule.setPortRangeMin(osSecGrpRule.getPortRangeMin());
                gemSecGrpRule.setPortRangeMax(osSecGrpRule.getPortRangeMax());
                gemSecGrpRule.setProtocol(Protocol.fromString(osSecGrpRule.getProtocol()));
                //                gemSecGrpRule.setCidr(osSecGrpRule.getRange().getCidr());
                gemSecGrpRule.setDirection(
                        GeminiSecurityGroupRuleDirection.valueOf(osSecGrpRule.getDirection().toUpperCase()));
                gemSecGrpRule.setRemoteGroupId(osSecGrpRule.getRemoteGroupId());
                gemSecGrpRule.setRemoteIpPrefix(osSecGrpRule.getRemoteIpPrefix());
                gemSecGrpRule.setIpAddressType(IPAddressType.valueOf(osSecGrpRule.getEtherType()));
                gemSecGrpRule.setParent(securityGroup);
                securityGroup.addSecurityRule(gemSecGrpRule);
            }
            listSecGrpRules.add(gemSecGrpRule);
        });

        Logger.debug("Successfully retrieved security groups rules Tenant: {} Env: {} security group {}",
                tenant.getName(), env.getName(), securityGroup.getName());
        return listSecGrpRules;
    }

    /**
     *
     * @param tenant
     * @param env
     * @param securityGroup - assumes on the name is available in the object
     * @return
     *
     * Calls open stack to retrieve information about the security group.
     * Typically used when only a security group name is available and the
     * caller wants to get rest of the information
     */
    @Override
    public ProvisioningProviderResponseType getSecurityGroup(GeminiTenant tenant, GeminiEnvironment env,
            GeminiSecurityGroup securityGroup) {
        //authenticate the session with the OpenStack installation
        OSClient os = OSFactory.builder().endpoint(env.getEndPoint())
                .credentials(env.getAdminUserName(), env.getAdminPassword()).tenantName(tenant.getName())
                .authenticate();
        if (os == null) {
            Logger.error("Failed to authenticate Tenant: {}",
                    ToStringBuilder.reflectionToString(tenant, ToStringStyle.MULTI_LINE_STYLE));
            return ProvisioningProviderResponseType.CLOUD_AUTH_FAILURE;
        }

        SecurityGroup osSecGrp = null;
        try {
            List<? extends SecurityGroup> osSecGrps = os.networking().securitygroup().list();
            osSecGrp = osSecGrps.stream().filter(osg -> osg.getName().equals(securityGroup.getName())).findFirst()
                    .get();
        } catch (NullPointerException | ClientResponseException ex) {
            Logger.error(
                    "Cloud Exception: {}\nCould not retrieve security group information in OpenStack. tenant: {} env: {} security group: {}",
                    ex.toString(), tenant.getName(), env.getName(), securityGroup.getName());
            return ProvisioningProviderResponseType.CLOUD_EXCEPTION;
        } catch (NoSuchElementException ex) {
            //security group was not found, so return an error 
            Logger.error(
                    "Security Group {} not found for tenant: {} env: {} Could not retrieve security group information in OpenStack. tenant: {} env: {} security group: {}",
                    securityGroup.getName(), tenant.getName(), env.getName());
            return ProvisioningProviderResponseType.OBJECT_NOT_FOUND;
        }

        //copy the basic stuff
        securityGroup.setCloudID(osSecGrp.getId());
        securityGroup.setProvisioned(true);
        securityGroup.setName(osSecGrp.getName());
        securityGroup.setDescription(osSecGrp.getDescription());

        //now the rules
        List<? extends SecurityGroupRule> osSecGrpRules = osSecGrp.getRules();
        osSecGrpRules.stream().forEach(osSecGrpRule -> {
            //check to see if this already exists
            GeminiSecurityGroupRule gemSecGrpRule = null;
            try {
                securityGroup.getSecurityRules().stream()
                        //.filter(gsr -> gsr.getCloudID().equals(osSecGrpRule.getId())) // don't compare the cloud id, because this could be a new Gemini object
                        .filter(gsr -> gsr.getDirection().toString().toLowerCase()
                                .equals(osSecGrpRule.getDirection()))
                        .filter(gsr -> gsr.getIpAddressType().toString()
                                .contains(osSecGrpRule.getEtherType().toUpperCase()))
                        .filter(gsr -> gsr.getPortRangeMax().equals(osSecGrpRule.getPortRangeMax()))
                        .filter(gsr -> gsr.getPortRangeMin().equals(osSecGrpRule.getPortRangeMin()))
                        .filter(gsr -> gsr.getProtocol().toString().contains(osSecGrpRule.getProtocol()))
                        .filter(gsr -> gsr.getRemoteGroupId().equals(osSecGrpRule.getRemoteGroupId()))
                        .filter(gsr -> gsr.getRemoteIpPrefix().equals(osSecGrpRule.getRemoteIpPrefix())).findAny()
                        .get();
            } catch (NoSuchElementException | NullPointerException ex) {
                gemSecGrpRule = new GeminiSecurityGroupRule();
                securityGroup.addSecurityRule(gemSecGrpRule);
                gemSecGrpRule.setPortRangeMin(osSecGrpRule.getPortRangeMin());
                gemSecGrpRule.setPortRangeMax(osSecGrpRule.getPortRangeMax());
                gemSecGrpRule.setProtocol(Protocol.fromString(osSecGrpRule.getProtocol()));
                //                gemSecGrpRule.setCidr(osSecGrpRule.getRange().getCidr());
                gemSecGrpRule.setDirection(
                        GeminiSecurityGroupRuleDirection.valueOf(osSecGrpRule.getDirection().toUpperCase()));
                gemSecGrpRule.setRemoteGroupId(osSecGrpRule.getRemoteGroupId());
                gemSecGrpRule.setRemoteIpPrefix(osSecGrpRule.getRemoteIpPrefix());
                gemSecGrpRule.setIpAddressType(IPAddressType.valueOf(osSecGrpRule.getEtherType()));
                gemSecGrpRule.setParent(securityGroup);
            }
            //rule already existed or new one created - just copy the cloud ID
            gemSecGrpRule.setCloudID(osSecGrpRule.getId());
            gemSecGrpRule.setProvisioned(true);
        });
        Logger.debug("Successfully retrieved security group information Tenant: {} Env: {} security group {}",
                tenant.getName(), env.getName(), securityGroup.getName());
        return ProvisioningProviderResponseType.SUCCESS;
    }

    /**
     *
     * @param tenant
     * @param env
     * @param securityGroup - the security group to be created
     * @return SUCCESS, AUTH_FAILURE, CLOUD_FAILURE, OBJECT_EXISTS
     *
     * Creates the Security group in OpenStack if it doesn't exist NOTE: it
     * creates all security rules in the group. A separate call to create rules
     * IS NOT required
     */
    @Override
    public ProvisioningProviderResponseType createSecurityGroup(GeminiTenant tenant, GeminiEnvironment env,
            GeminiSecurityGroup securityGroup) {
        //authenticate the session with the OpenStack installation
        OSClient os = OSFactory.builder().endpoint(env.getEndPoint())
                .credentials(env.getAdminUserName(), env.getAdminPassword()).tenantName(tenant.getName())
                .authenticate();
        if (os == null) {
            Logger.error("Failed to authenticate Tenant: {}",
                    ToStringBuilder.reflectionToString(tenant, ToStringStyle.MULTI_LINE_STYLE));
            return ProvisioningProviderResponseType.CLOUD_AUTH_FAILURE;
        }

        SecurityGroup osSecGrp = null;
        try {
            List<? extends SecurityGroup> osSecGrps = os.networking().securitygroup().list();
            osSecGrp = osSecGrps.stream().filter(osg -> osg.getName().equals(securityGroup.getName())).findFirst()
                    .get();
        } catch (NullPointerException | ClientResponseException ex) {
            Logger.error(
                    "Cloud Exception: {}\nCould not retrieve security group information in OpenStack. tenant: {} env: {} security group: {}",
                    ex.toString(), tenant.getName(), env.getName(), securityGroup.getName());
            return ProvisioningProviderResponseType.CLOUD_EXCEPTION;
        } catch (NoSuchElementException ex) {
            //security group not found, so ok to move forward with the creation
        }

        if (osSecGrp != null) {
            Logger.error("Security Group exists: tenant: {} env: {} security group: {}", tenant.getName(),
                    env.getName(), securityGroup.getName());
            return ProvisioningProviderResponseType.OBJECT_EXISTS;
        }

        //create the security group
        osSecGrp = os.networking().securitygroup().create(Builders.securityGroup().tenantId(tenant.getTenantID())
                .name(securityGroup.getName()).description(securityGroup.getDescription()).build());
        if (osSecGrp == null) {
            Logger.error("Security Group creation failure tenant: {} env: {} security group: {}", tenant.getName(),
                    env.getName(), securityGroup.getName());
            return ProvisioningProviderResponseType.CLOUD_FAILURE;
        }

        //map the object and then create the security group rules in the cloud
        securityGroup.setCloudID(osSecGrp.getId());
        securityGroup.setProvisioned(true);
        securityGroup.getSecurityRules().stream().forEach(s -> {
            SecurityGroupRule r = null;
            try {
                r = os.networking().securityrule()
                        .create(Builders.securityGroupRule().tenantId(tenant.getTenantID())
                                .securityGroupId(securityGroup.getCloudID())
                                .direction(s.getDirection().toString().toLowerCase())
                                .ethertype(s.getIpAddressType().toString()).portRangeMin(s.getPortRangeMin())
                                .portRangeMax(s.getPortRangeMax()).protocol(s.getProtocol().toString())
                                //                                .remoteGroupId(s.getRemoteGroupId())
                                .remoteIpPrefix(s.getRemoteIpPrefix()).build());
                //copy the id
                s.setCloudID(r.getId());
                s.setProvisioned(true);
            } catch (ClientResponseException | NullPointerException ex) {
                Logger.error(
                        "Cloud Exception {} - Security Group rule creation failure tenant: {} env: {} security group: {} rule: {}",
                        ex.toString(), tenant.getName(), env.getName(), securityGroup.getName(), s.getName());
            }
        });

        Logger.debug("Successfully created security group Tenant: {} Env: {} security group {}", tenant.getName(),
                env.getName(), securityGroup.getName());
        return ProvisioningProviderResponseType.SUCCESS;
    }

    @Override
    public ProvisioningProviderResponseType updateSecurityGroup(GeminiTenant tenant, GeminiEnvironment env,
            GeminiSecurityGroup securityGroup) {
        //authenticate the session with the OpenStack installation
        OSClient os = OSFactory.builder().endpoint(env.getEndPoint())
                .credentials(env.getAdminUserName(), env.getAdminPassword()).tenantName(tenant.getName())
                .authenticate();
        if (os == null) {
            Logger.error("Failed to authenticate Tenant: {}",
                    ToStringBuilder.reflectionToString(tenant, ToStringStyle.MULTI_LINE_STYLE));
            return ProvisioningProviderResponseType.CLOUD_AUTH_FAILURE;
        }

        SecurityGroup osSecGrp;
        try {
            osSecGrp = os.networking().securitygroup().get(securityGroup.getCloudID());
        } catch (NullPointerException | ClientResponseException ex) {
            Logger.error(
                    "Could not retrieve security group information in OpenStack. tenant: {} env: {} security group: {}",
                    tenant.getName(), env.getName(), securityGroup.getName());
            return ProvisioningProviderResponseType.CLOUD_EXCEPTION;
        }

        if (osSecGrp == null) {
            Logger.error("Security Group does not exist: tenant: {} env: {} security group: {}", tenant.getName(),
                    env.getName(), securityGroup.getName());
            return ProvisioningProviderResponseType.OBJECT_NOT_FOUND;
        }

        //looks like there no way to update a security group, we have to delete and re-create
        ActionResponse a = os.networking().securitygroup().delete(osSecGrp.getId());
        if (!a.isSuccess()) {
            Logger.error(
                    "Security Group update - could not delete the security group: tenant: {} env: {} security group: {} Error Message: {}",
                    tenant.getName(), env.getName(), securityGroup.getName(), a.getFault());
            return ProvisioningProviderResponseType.CLOUD_FAILURE;
        }

        //now re-create the group with the updated info
        //create the security group
        osSecGrp = os.networking().securitygroup().create(Builders.securityGroup().tenantId(tenant.getTenantID())
                .name(securityGroup.getName()).description(securityGroup.getDescription()).build());
        if (osSecGrp == null) {
            Logger.error("Security Group update, creation failure. tenant: {} env: {} security group: {}",
                    tenant.getName(), env.getName(), securityGroup.getName());
            return ProvisioningProviderResponseType.CLOUD_FAILURE;
        }

        //update the cloud ID since it has most likely changed and then create the security group rules in the cloud
        securityGroup.setCloudID(osSecGrp.getId());
        securityGroup.setProvisioned(true);
        securityGroup.getSecurityRules().stream().forEach(s -> {
            SecurityGroupRule r = os.networking().securityrule()
                    .create(Builders.securityGroupRule().tenantId(tenant.getTenantID())
                            .securityGroupId(securityGroup.getCloudID())
                            .direction(s.getDirection().toString().toLowerCase())
                            .ethertype(s.getIpAddressType().toString()).portRangeMin(s.getPortRangeMin())
                            .portRangeMax(s.getPortRangeMax()).protocol(s.getProtocol().toString())
                            //                            .remoteGroupId(s.getRemoteGroupId())
                            .remoteIpPrefix(s.getRemoteIpPrefix()).build());
            if (r == null) {
                Logger.error("Security Group rule creation failure. tenant: {} env: {} security group: {} rule {}",
                        tenant.getName(), env.getName(), securityGroup.getName(), s.getName());
            }
        });

        Logger.debug("Successfully updated security group Tenant: {} Env: {} security group {}", tenant.getName(),
                env.getName(), securityGroup.getName());
        return ProvisioningProviderResponseType.SUCCESS;
    }

    @Override
    public ProvisioningProviderResponseType deleteSecurityGroup(GeminiTenant tenant, GeminiEnvironment env,
            GeminiSecurityGroup securityGroup) {
        //authenticate the session with the OpenStack installation
        OSClient os = OSFactory.builder().endpoint(env.getEndPoint())
                .credentials(env.getAdminUserName(), env.getAdminPassword()).tenantName(tenant.getName())
                .authenticate();
        if (os == null) {
            Logger.error("Failed to authenticate Tenant: {}",
                    ToStringBuilder.reflectionToString(tenant, ToStringStyle.MULTI_LINE_STYLE));
            return ProvisioningProviderResponseType.CLOUD_AUTH_FAILURE;
        }

        SecurityGroup osSecGrp;
        try {
            osSecGrp = os.networking().securitygroup().get(securityGroup.getCloudID());
        } catch (NullPointerException | ClientResponseException ex) {
            Logger.error(
                    "Could not retrieve security group information in OpenStack. tenant: {} env: {} security group: {}",
                    tenant.getName(), env.getName(), securityGroup.getName());
            return ProvisioningProviderResponseType.CLOUD_EXCEPTION;
        }

        if (osSecGrp == null) {
            Logger.error("Security Group does not exist: tenant: {} env: {} security group: {}", tenant.getName(),
                    env.getName(), securityGroup.getName());
            return ProvisioningProviderResponseType.OBJECT_NOT_FOUND;
        }

        //looks like there no way to update a security group, we have to delete and re-create
        ActionResponse a = os.networking().securitygroup().delete(osSecGrp.getId());
        if (!a.isSuccess()) {
            Logger.error(
                    "Security Group update - could not delete the security group: tenant: {} env: {} security group: {} Error Message: {}",
                    tenant.getName(), env.getName(), securityGroup.getName(), a.getFault());
            return ProvisioningProviderResponseType.CLOUD_FAILURE;
        }

        //remove this security group from the environment
        env.deleteSecurityGroup(securityGroup);

        //now remove the deleted security group's name from the servers that use the deleted security group
        //        env.getApplications()
        //                .stream()
        //                .map(GeminiApplication::getNetworks)
        //                .flatMap(List::stream)
        //                .map(GeminiNetwork::getSubnets)
        //                .flatMap(List::stream)
        //                .map(GeminiSubnet::getAllocationPools)
        //                .flatMap(List::stream)
        //                .map(GeminiSubnetAllocationPool::getServers)
        //                .flatMap(List::stream)
        //                .forEach(s -> s.deleteSecGroupName(securityGroup.getName()));

        Logger.debug("Successfully deleted security group Tenant: {} Env: {} security group {}", tenant.getName(),
                env.getName(), securityGroup.getName());
        return ProvisioningProviderResponseType.SUCCESS;
    }

    /**
     * Get the security group rule details based on the cloud id provided in the
     * securityRule object.
     *
     * @param tenant
     * @param env
     * @param securityGroup - the parent security group
     * @param securityRule - the rule whose information needs to be retrieved,
     * the cloud ID must be provided.
     * @return SUCCESS - if all goes well AUTH_FAILURE - if the authentication
     * failed with the information in the tenant object EXCEPTION - if the cloud
     * raised an exception OBJECT_NOT_FOUND - if the rule with ID provided is
     * not found
     */
    @Override
    public ProvisioningProviderResponseType getSecurityGroupRule(GeminiTenant tenant, GeminiEnvironment env,
            GeminiSecurityGroup securityGroup, GeminiSecurityGroupRule securityRule) {
        //authenticate the session with the OpenStack installation
        OSClient os = OSFactory.builder().endpoint(env.getEndPoint())
                .credentials(env.getAdminUserName(), env.getAdminPassword()).tenantName(tenant.getName())
                .authenticate();
        if (os == null) {
            Logger.error("Failed to authenticate Tenant: {}",
                    ToStringBuilder.reflectionToString(tenant, ToStringStyle.MULTI_LINE_STYLE));
            return ProvisioningProviderResponseType.CLOUD_AUTH_FAILURE;
        }

        SecurityGroupRule osSecGrpRule;
        try {
            osSecGrpRule = os.networking().securityrule().get(securityRule.getCloudID());
        } catch (NullPointerException | ClientResponseException ex) {
            Logger.error(
                    "Cloud Exception: Could not retrieve security group rule information in OpenStack. tenant: {} env: {} security group: {} security rule ID: {}",
                    tenant.getName(), env.getName(), securityGroup.getName(), securityRule.getCloudID());
            return ProvisioningProviderResponseType.CLOUD_EXCEPTION;
        }

        if (osSecGrpRule == null) {
            Logger.error(
                    "Security rule not found: Could not find security group rule information in OpenStack. tenant: {} env: {} security group: {} security rule ID: {}",
                    tenant.getName(), env.getName(), securityGroup.getName(), securityRule.getCloudID());
            return ProvisioningProviderResponseType.OBJECT_NOT_FOUND;
        }

        //now copy the information into the gemini security rule object
        securityRule.setProvisioned(true);
        securityRule
                .setDirection(GeminiSecurityGroupRuleDirection.valueOf(osSecGrpRule.getDirection().toUpperCase()));
        securityRule.setIpAddressType(IPAddressType.valueOf(osSecGrpRule.getEtherType()));
        securityRule.setParent(securityGroup);
        securityRule.setPortRangeMin(osSecGrpRule.getPortRangeMin());
        securityRule.setPortRangeMax(osSecGrpRule.getPortRangeMax());
        securityRule.setProtocol(Protocol.valueOf(osSecGrpRule.getProtocol()));
        securityRule.setRemoteGroupId(osSecGrpRule.getRemoteGroupId());
        securityRule.setRemoteIpPrefix(osSecGrpRule.getRemoteIpPrefix());

        Logger.debug(
                "Successfully retrieved security groups rule Tenant: {} Env: {} security group {} security rule {}",
                tenant.getName(), env.getName(), securityGroup.getName(), securityRule.getName());
        return ProvisioningProviderResponseType.SUCCESS;
    }

    /**
     * createSecurityGroupRule. Creates the security rule in the cloud. It is
     * expected that the security rule object has all the relevant required for
     * the creation.
     *
     * @param tenant - contains the auth information
     * @param env - contains the security group
     * @param securityGroup - the parent security group
     * @param securityRule - represents the rule to be created in the cloud. All
     * values must be available in the object. The cloud provider may not create
     * it with partial information
     * @return SUCCESS - if all goes well, AUTH_FAILURE - if the authentication
     * failed with the information in the tenant object, EXCEPTION - if the
     * cloud raised an exception CLOUD_FAILURE - if the creation did not succeed
     */
    @Override
    public ProvisioningProviderResponseType createSecurityGroupRule(GeminiTenant tenant, GeminiEnvironment env,
            GeminiSecurityGroup securityGroup, GeminiSecurityGroupRule securityRule) {
        //authenticate the session with the OpenStack installation
        OSClient os = OSFactory.builder().endpoint(env.getEndPoint())
                .credentials(env.getAdminUserName(), env.getAdminPassword()).tenantName(tenant.getName())
                .authenticate();
        if (os == null) {
            Logger.error("Failed to authenticate Tenant: {}",
                    ToStringBuilder.reflectionToString(tenant, ToStringStyle.MULTI_LINE_STYLE));
            return ProvisioningProviderResponseType.CLOUD_AUTH_FAILURE;
        }

        SecurityGroupRule r;
        try {
            r = os.networking().securityrule().create(Builders.securityGroupRule().tenantId(tenant.getTenantID())
                    .securityGroupId(securityGroup.getCloudID())
                    .direction(securityRule.getDirection().toString().toLowerCase())
                    .ethertype(securityRule.getIpAddressType().toString())
                    .portRangeMin(securityRule.getPortRangeMin()).portRangeMax(securityRule.getPortRangeMax())
                    .protocol(securityRule.getProtocol().toString())
                    //.remoteGroupId(securityRule.getRemoteGroupId())
                    .remoteIpPrefix(securityRule.getRemoteIpPrefix()).build());
            securityRule.setCloudID(r.getId());
            securityRule.setProvisioned(true);
        } catch (NullPointerException | ClientResponseException ex) {
            Logger.error(
                    "Cloud Exception {}: Could not create the security group rule in OpenStack. tenant: {} env: {} security group: {} security rule ID: {}",
                    ex.toString(), tenant.getName(), env.getName(), securityGroup.getName(),
                    securityRule.getCloudID());
            return ProvisioningProviderResponseType.CLOUD_EXCEPTION;
        }
        Logger.debug(
                "Successfully created security group rule Tenant: {} Env: {} security group {} security rule {}",
                tenant.getName(), env.getName(), securityGroup.getName(), securityRule.getName());
        return ProvisioningProviderResponseType.SUCCESS;
    }

    /**
     * updateSecurityGroupRule - updates security rule information in the cloud.
     *
     * Currently OpenStack DOES NOT allow update of a security rule. This
     * function deletes the rule and recreates it with the updated information.
     *
     * @param tenant
     * @param env
     * @param securityGroup
     * @param securityRule
     * @return
     */
    @Override
    public ProvisioningProviderResponseType updateSecurityGroupRule(GeminiTenant tenant, GeminiEnvironment env,
            GeminiSecurityGroup securityGroup, GeminiSecurityGroupRule securityRule) {
        //authenticate the session with the OpenStack installation
        OSClient os = OSFactory.builder().endpoint(env.getEndPoint())
                .credentials(env.getAdminUserName(), env.getAdminPassword()).tenantName(tenant.getName())
                .authenticate();
        if (os == null) {
            Logger.error("Failed to authenticate Tenant: {}",
                    ToStringBuilder.reflectionToString(tenant, ToStringStyle.MULTI_LINE_STYLE));
            return ProvisioningProviderResponseType.CLOUD_AUTH_FAILURE;
        }

        //since openstack doesn't allow update, we need to delete and re-create
        try {
            os.networking().securityrule().delete(securityRule.getCloudID());
        } catch (NullPointerException | ClientResponseException ex) {
            Logger.error(
                    "Failed to delete security group rule Tenant: {} Env: {}, Security Group: {} security rule {}",
                    tenant.getName(), env.getName(), securityGroup.getName(), securityRule.getName());
            return ProvisioningProviderResponseType.CLOUD_EXCEPTION;
        }
        SecurityGroupRule r;
        try {
            r = os.networking().securityrule().create(Builders.securityGroupRule().tenantId(tenant.getTenantID())
                    .securityGroupId(securityGroup.getCloudID())
                    .direction(securityRule.getDirection().toString().toLowerCase())
                    .ethertype(securityRule.getIpAddressType().toString())
                    .portRangeMin(securityRule.getPortRangeMin()).portRangeMax(securityRule.getPortRangeMax())
                    .protocol(securityRule.getProtocol().toString())
                    //.remoteGroupId(securityRule.getRemoteGroupId())
                    .remoteIpPrefix(securityRule.getRemoteIpPrefix()).build());
            securityRule.setCloudID(r.getId());
            securityRule.setProvisioned(true);
        } catch (NullPointerException | ClientResponseException ex) {
            Logger.error(
                    "Cloud Exception {}: Could not create the security group rule in OpenStack. tenant: {} env: {} security group: {} security rule ID: {}",
                    ex.toString(), tenant.getName(), env.getName(), securityGroup.getName(),
                    securityRule.getCloudID());
            return ProvisioningProviderResponseType.CLOUD_EXCEPTION;
        }

        Logger.debug(
                "Successfully updated security group rule Tenant: {} Env: {} security group {} security rule {}",
                tenant.getName(), env.getName(), securityGroup.getName(), securityRule.getName());
        return ProvisioningProviderResponseType.SUCCESS;
    }

    @Override
    public ProvisioningProviderResponseType deleteSecurityGroupRule(GeminiTenant tenant, GeminiEnvironment env,
            GeminiSecurityGroup securityGroup, GeminiSecurityGroupRule securityRule) {
        //authenticate the session with the OpenStack installation
        OSClient os = OSFactory.builder().endpoint(env.getEndPoint())
                .credentials(env.getAdminUserName(), env.getAdminPassword()).tenantName(tenant.getName())
                .authenticate();
        if (os == null) {
            Logger.error("Failed to authenticate Tenant: {}",
                    ToStringBuilder.reflectionToString(tenant, ToStringStyle.MULTI_LINE_STYLE));
            return ProvisioningProviderResponseType.CLOUD_AUTH_FAILURE;
        }

        //since openstack doesn't allow update, we need to delete and re-create
        try {
            os.networking().securityrule().delete(securityRule.getCloudID());
        } catch (NullPointerException | ClientResponseException ex) {
            Logger.error(
                    "Failed to delete security group rule Exception {} Tenant: {} Env: {}, Security Group: {} security rule {}",
                    ex.toString(), tenant.getName(), env.getName(), securityGroup.getName(),
                    securityRule.getName());
            return ProvisioningProviderResponseType.CLOUD_EXCEPTION;
        }
        Logger.debug(
                "Successfully deleted security group rule Tenant: {} Env: {} security group {} security rule {}",
                tenant.getName(), env.getName(), securityGroup.getName(), securityRule.getName());
        return ProvisioningProviderResponseType.SUCCESS;
    }

    @Override
    public ProvisioningProviderResponseType attachInterface(GeminiTenant tenant, GeminiEnvironment env,
            GeminiSecurityGroup securityGroup) {
        Logger.error("attachInterface feature not supported in OpenStack. Tenant: {} Env: {} security group {}",
                tenant.getName(), env.getName(), securityGroup.getName());
        throw new UnsupportedOperationException("attachInterface feature not supported in OpenStack."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public ProvisioningProviderResponseType detachInterface(GeminiTenant tenant, GeminiEnvironment env,
            GeminiSecurityGroup securityGroup) {
        Logger.error("detachInterface feature not supported in OpenStack. Tenant: {} Env: {} security group {}",
                tenant.getName(), env.getName(), securityGroup.getName());
        throw new UnsupportedOperationException("detachInterface feature not supported in OpenStack."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public ProvisioningProviderResponseType attachIpRange(GeminiTenant tenant, GeminiEnvironment env,
            GeminiSecurityGroup securityGroup) {
        Logger.error(
                "attachIpRange feature not supported in OpenStack. Use the createSecurityGroupRule function instead"
                        + "Tenant: {} Env: {} security group {}",
                tenant.getName(), env.getName(), securityGroup.getName());
        throw new UnsupportedOperationException(
                "attachIpRange feature not supported in OpenStack. Create a new GeminiSecurityRule and add it to the security group"); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public ProvisioningProviderResponseType detachIpRange(GeminiTenant tenant, GeminiEnvironment env,
            GeminiSecurityGroup securityGroup) {
        Logger.error(
                "detachIpRange feature not supported in OpenStack. Use deleteSecurityGroupRule instead. "
                        + "Tenant: {} Env: {} security group {}",
                tenant.getName(), env.getName(), securityGroup.getName());
        throw new UnsupportedOperationException(
                "detachIpRange feature not supported in OpenStack. Use deleteSecurityGroupRule instead. "); //To change body of generated methods, choose Tools | Templates.
    }
}