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

com.vmware.photon.controller.model.adapters.registry.operations.ResourceOperationUtils Maven / Gradle / Ivy

There is a newer version: 0.6.60
Show newest version
/*
 * Copyright (c) 2017 VMware, Inc. All Rights Reserved.
 *
 * 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.vmware.photon.controller.model.adapters.registry.operations;

import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.stream.Collectors;

import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import com.vmware.photon.controller.model.adapters.registry.operations.ResourceOperationSpecService.ResourceOperationSpec;
import com.vmware.photon.controller.model.adapters.registry.operations.ResourceOperationSpecService.ResourceType;
import com.vmware.photon.controller.model.query.QueryUtils.QueryTop;
import com.vmware.photon.controller.model.resources.ComputeService.ComputeState;
import com.vmware.photon.controller.model.resources.EndpointService.EndpointState;
import com.vmware.photon.controller.model.resources.NetworkService.NetworkState;
import com.vmware.photon.controller.model.resources.ResourceState;
import com.vmware.photon.controller.model.util.AssertUtil;
import com.vmware.xenon.common.DeferredResult;
import com.vmware.xenon.common.Operation;
import com.vmware.xenon.common.Operation.AuthorizationContext;
import com.vmware.xenon.common.Operation.CompletionHandler;
import com.vmware.xenon.common.OperationJoin;
import com.vmware.xenon.common.OperationJoin.JoinedCompletionHandler;
import com.vmware.xenon.common.Service;
import com.vmware.xenon.common.ServiceHost;
import com.vmware.xenon.common.Utils;
import com.vmware.xenon.services.common.QueryTask.Query;

/**
 * Various {@link ResourceOperationSpec} related Utilities.
 */
public class ResourceOperationUtils {
    public static final String SCRIPT_ENGINE_NAME_JS = "js";
    public static final String SCRIPT_CONTEXT_RESOURCE = "resource";

    public static final String COMPUTE_KIND = Utils.buildKind(ComputeState.class);
    public static final String NETWORK_KIND = Utils.buildKind(NetworkState.class);

    public static enum TargetCriteria {
        RESOURCE_POWER_STATE_ON("resource.powerState == 'ON'"),

        // ComputeProperties.CUSTOM_PROP_COMPUTE_HAS_SNAPSHOTS = "__hasSnapshot". So "__hasSnapshot"
        // is used here. Any modification in that custom property requires manual changes here as well.
        RESOURCE_HAS_SNAPSHOTS("resource.customProperties != null && " +
                " (resource.customProperties.get('__hasSnapshot') != null) && " +
                "('true' == (resource.customProperties.get('__hasSnapshot')).toLowerCase())");

        private final String criteria;

        private TargetCriteria(String criteria) {
            this.criteria = criteria;
        }

        public String getCriteria() {
            return this.criteria;
        }
    }

    /**
     * Lookup for {@link ResourceOperationSpec} by given {@code endpointType},
     * {@code resourceType} and {@code operation}
     * @param host
     *         host to use to create operation
     * @param refererURI
     *         the referer to use when send the operation
     * @param endpointType
     *         the resource's endpoint type
     * @param resourceType
     *         the resource type
     * @param operation
     *         the operation
     * @param queryTaskTenantLinks
     *         tenant links used for the QueryTask
     * @param authorizationContext
     *         authorization context that will be used for operations (if set to null the context
     *         will not be changed)
     * @return
     */
    public static DeferredResult lookUpByEndpointType(
            ServiceHost host,
            URI refererURI,
            String endpointType,
            ResourceType resourceType,
            String operation,
            List queryTaskTenantLinks,
            AuthorizationContext authorizationContext) {

        return lookUp(host, refererURI, endpointType, resourceType, operation,
                queryTaskTenantLinks, authorizationContext)
                .thenApply(specs -> specs.isEmpty() ? null : specs.iterator().next());
    }

    /**
     * Lookup for {@link ResourceOperationSpec} by given {@code endpointLink},
     * {@code resourceType} and {@code operation}
     * @param host
     *         host to use to create operation
     * @param refererURI
     *         the referer to use when send the operation
     * @param endpointLink
     *         the endpoint link of the resource
     * @param resourceType
     *         the resource type
     * @param operation
     *         the operation
     * @param queryTaskTenantLinks
     *         tenant links used for the QueryTask
     * @param authorizationContext
     *         authorization context that will be used for operations (if set to null the context
     *         will not be changed)
     * @return
     */
    public static DeferredResult lookUpByEndpointLink(
            ServiceHost host,
            URI refererURI,
            String endpointLink,
            ResourceType resourceType,
            String operation,
            List queryTaskTenantLinks,
            AuthorizationContext authorizationContext) {

        return host.sendWithDeferredResult(Operation.createGet(host, endpointLink)
                .setReferer(refererURI))
                .thenCompose(o -> lookUpByEndpointType(host, refererURI,
                        (o.getBody(EndpointState.class)).endpointType,
                        resourceType, operation, queryTaskTenantLinks, authorizationContext)
                );
    }

    /**
     * Lookup for {@link ResourceOperationSpec} by given {@code resourceState} and {@code operation}
     * @param host
     *         host to use to create operation
     * @param refererURI
     *         the referer to use when send the operation
     * @param resourceState
     *         the resource state specialization for which to lookup the spec
     * @param operation
     *         the operation
     * @param authorizationContext
     *         authorization context that will be used for operations (if set to null the context
     *         will not be changed)
     * @return
     */
    public static  DeferredResult> lookupByResourceState(
            ServiceHost host,
            URI refererURI,
            T resourceState,
            String operation,
            AuthorizationContext authorizationContext) {
        AssertUtil.assertNotNull(resourceState, "'resourceState' must be set.");
        String endpointLink;
        ResourceType resourceType;
        if (resourceState instanceof ComputeState) {
            ComputeState compute = (ComputeState) resourceState;
            endpointLink = compute.endpointLink;
            resourceType = ResourceType.COMPUTE;
        } else if (resourceState instanceof NetworkState) {
            NetworkState network = (NetworkState) resourceState;
            endpointLink = network.endpointLink;
            resourceType = ResourceType.NETWORK;
        } else {
            throw new IllegalArgumentException("Unsupported resource state: "
                    + resourceState.getClass().getName());
        }
        AssertUtil.assertNotNull(endpointLink, "'endpointLink' must be set.");

        return host.sendWithDeferredResult(
                Operation.createGet(host, endpointLink).setReferer(refererURI),
                EndpointState.class)
                .thenCompose(ep -> lookUp(
                        host, refererURI, (ep).endpointType, resourceType, operation,
                        resourceState.tenantLinks, authorizationContext)
                );
    }

    /**
     * Evaluates provided {@code spec}'s target criteria against the specified {@code resourceState}
     * and returns if the  {@link ResourceOperationSpec} is applicable for the given {@link
     * ResourceState}
     * @param resourceState
     *         the resource state for which to check whether given {@code spec} is available
     * @param spec
     *         the {@link ResourceOperationSpec} which to check whether is available for the given
     *         {@code resourceState}
     * @return {@literal true} only in case there is targetCriteria, and the targetCriteria is
     * evaluated to {@literal true} for the given {@code resourceState}
     */
    public static boolean isAvailable(ResourceState resourceState, ResourceOperationSpec spec) {
        AssertUtil.assertNotNull(spec, "'spec' must be set.");
        if (spec.targetCriteria == null) {
            return true;
        }

        ScriptEngine engine = new ScriptEngineManager().getEngineByName(SCRIPT_ENGINE_NAME_JS);

        if (resourceState != null) {
            //Clone original object to avoid changing props of original object from vulnerable
            // targetCriteria
            ResourceState clone = Utils.cloneObject(resourceState);
            engine.getBindings(ScriptContext.ENGINE_SCOPE).put(SCRIPT_CONTEXT_RESOURCE, clone);
        }
        try {
            Object res = engine.eval(spec.targetCriteria);
            if (res instanceof Boolean) {
                return ((Boolean) res).booleanValue();
            } else {
                Utils.log(ResourceOperationUtils.class, "isAvailable",
                        Level.WARNING,
                        "Expect boolean result when evaluate targetCriteria \"%s\" of "
                                + "endpointType: %s, resourceType: %s, operation: %s, "
                                + "adapterReference: %s. Result: %s",
                        spec.targetCriteria,
                        spec.endpointType, spec.resourceType, spec.operation,
                        spec.adapterReference, res);
            }
        } catch (ScriptException e) {
            Utils.log(ResourceOperationUtils.class, "isAvailable",
                    Level.SEVERE,
                    "Cannot evaluate targetCriteria '%s' of "
                            + "endpointType: %s, resourceType: %s, operation: %s, "
                            + "adapterReference: %s. Cause: %s",
                    spec.targetCriteria,
                    spec.endpointType, spec.resourceType, spec.operation, spec.adapterReference,
                    Utils.toString(e));

        }
        return false;
    }

    /**
     * A generic utility method to register any Day 2 Operation service/adapter with the framework
     * as a ResourceOperationSpecService. It accepts a list of {@code specs} which a service can
     * handle as input and submits them to the ResourceOperationSpecService's Factory. This call
     * should generally be part of handleStart method of the adapter/service, preferably near the
     * end after any service specification configuration settings.
     * @param service
     *         the resourceOperation service/adapter
     * @param handler
     *         the operation completion handler for making the success/failure actions
     * @param specs
     *         list of intended the ResourceOperationSpec's to register with the service
     */
    public static void registerResourceOperation(Service service, CompletionHandler handler,
            ResourceOperationSpec... specs) {
        if (specs == null || specs.length == 0) {
            service.getHost().log(Level.FINE,
                    "No ResourceOperationSpec to register by %s",
                    service.getSelfLink());
            handler.handle(null, null);
            return;
        }

        service.getHost().registerForServiceAvailability((op, ex) -> {
            if (ex != null) {
                service.getHost().log(Level.SEVERE, "%s", Utils.toString(ex));
                handler.handle(op, ex);
            } else {
                List operations = Arrays.stream(specs)
                        .map(spec -> createOperation(service, spec))
                        .collect(Collectors.toList());

                JoinedCompletionHandler jh = (ops, err) -> {
                    if (err != null) {
                        service.getHost().log(Level.SEVERE, "Error: %s", Utils.toString(err));
                        handler.handle(ops.values().iterator().next(),
                                err.values().iterator().next());
                    } else {
                        service.getHost().log(Level.FINE,
                                "Successfully registered operations.");
                        handler.handle(null, null);
                    }
                };
                OperationJoin.create(operations).setCompletion(jh).sendWith(service);
            }
        },  true, ResourceOperationSpecService.FACTORY_LINK);
    }

    /**
     * Lookup for {@link ResourceOperationSpec}s by given {@code endpointType},
     * {@code resourceType} and optionally {@code operation}
     * 

* If operation not specified then return all resource operation specs for the given * {@code endpointType} and {@code resourceType} * @param host * host to use to create operation * @param refererURI * the referer to use when send the operation * @param endpointType * the resource's endpoint type * @param resourceType * the resource type * @param operation * optional operation id argument * @param queryTaskTenantLinks * tenant links used for the QueryTask * @param authorizationContext * authorization context that will be used for operations (if set to null the context * will not be changed) * @return */ private static DeferredResult> lookUp( ServiceHost host, URI refererURI, String endpointType, ResourceType resourceType, String operation, List queryTaskTenantLinks, AuthorizationContext authorizationContext) { Query.Builder builder = Query.Builder.create() .addKindFieldClause(ResourceOperationSpec.class) .addFieldClause( ResourceOperationSpec.FIELD_NAME_ENDPOINT_TYPE, endpointType) .addFieldClause( ResourceOperationSpec.FIELD_NAME_RESOURCE_TYPE, resourceType); if (operation != null) { builder.addFieldClause( ResourceOperationSpec.FIELD_NAME_OPERATION, operation); } Query query = builder.build(); QueryTop top = new QueryTop<>( host, query, ResourceOperationSpec.class, null) .setQueryTaskTenantLinks(queryTaskTenantLinks) .setAuthorizationContext(authorizationContext); if (operation != null) { //resource operation spec id and selfLink are built from the endpoint type, resource // type and operation id, so the query result is guaranteed to return at most 1 element top.setMaxResultsLimit(1); } top.setReferer(refererURI); return top.collectDocuments(Collectors.toList()); } private static Operation createOperation(Service service, ResourceOperationSpec spec) { service.getHost().log(Level.FINE, "Going to register Resource Operation name=%s, operation='%s'", spec.name, spec.operation); return Operation.createPost(service, ResourceOperationSpecService.FACTORY_LINK) .addPragmaDirective(Operation.PRAGMA_DIRECTIVE_QUEUE_FOR_SERVICE_AVAILABILITY) .setBody(spec); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy