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

com.vmware.photon.controller.model.adapters.azure.instance.AzureSubnetService Maven / Gradle / Ivy

/*
 * 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.azure.instance;

import java.net.URI;
import java.util.UUID;
import java.util.concurrent.ExecutorService;

import com.microsoft.azure.credentials.ApplicationTokenCredentials;
import com.microsoft.azure.management.network.implementation.NetworkManagementClientImpl;
import com.microsoft.azure.management.network.implementation.SubnetInner;
import com.microsoft.azure.management.network.implementation.SubnetsInner;
import com.microsoft.rest.RestClient;
import com.microsoft.rest.ServiceCallback;

import com.vmware.photon.controller.model.adapterapi.SubnetInstanceRequest;
import com.vmware.photon.controller.model.adapters.azure.AzureUriPaths;
import com.vmware.photon.controller.model.adapters.azure.utils.AzureDeferredResultServiceCallback;
import com.vmware.photon.controller.model.adapters.azure.utils.AzureProvisioningCallback;
import com.vmware.photon.controller.model.adapters.azure.utils.AzureUtils;
import com.vmware.photon.controller.model.adapters.util.AdapterUtils;
import com.vmware.photon.controller.model.adapters.util.TaskManager;
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.ResourceGroupService.ResourceGroupState;
import com.vmware.photon.controller.model.resources.SubnetService.SubnetState;
import com.vmware.photon.controller.model.support.LifecycleState;
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.StatelessService;
import com.vmware.xenon.services.common.AuthCredentialsService.AuthCredentialsServiceState;

/**
 * Adapter to create/delete a subnet on Azure.
 */
public class AzureSubnetService extends StatelessService {

    public static final String SELF_LINK = AzureUriPaths.AZURE_SUBNET_ADAPTER;

    /**
     * Subnet request context.
     */
    private static class AzureSubnetContext {

        final SubnetInstanceRequest request;

        EndpointState endpoint;
        AuthCredentialsServiceState authentication;
        RestClient restClient;
        SubnetsInner azureClient;

        SubnetState subnetState;
        NetworkState parentNetwork;
        String parentNetworkResourceGroupName;

        TaskManager taskManager;

        AzureSubnetContext(StatelessService service, SubnetInstanceRequest request) {
            this.request = request;
            this.taskManager = new TaskManager(service, request.taskReference,
                    request.resourceLink());
        }
    }

    private ExecutorService executorService;

    @Override
    public void handleStart(Operation op) {

        this.executorService = getHost().allocateExecutor(this);

        super.handleStart(op);
    }

    @Override
    public void handleStop(Operation op) {
        this.executorService.shutdown();
        AdapterUtils.awaitTermination(this.executorService);

        super.handleStop(op);
    }

    @Override
    public void handlePatch(Operation op) {

        if (!op.hasBody()) {
            op.fail(new IllegalArgumentException("body is required"));
            return;
        }

        // initialize context object
        AzureSubnetContext context = new AzureSubnetContext(this,
                op.getBody(SubnetInstanceRequest.class));

        // Immediately complete the Operation from calling task.
        op.complete();

        DeferredResult.completed(context)
                .thenCompose(this::populateContext)
                .thenCompose(this::handleSubnetInstanceRequest)
                .whenComplete((o, e) -> {
                    // Once done patch the calling task with correct stage.
                    if (e == null) {
                        context.taskManager.finishTask();
                    } else {
                        context.taskManager.patchTaskToFailure(e);
                    }
                });
    }

    private DeferredResult populateContext(AzureSubnetContext context) {
        return DeferredResult.completed(context)
                .thenCompose(this::getSubnetState)
                .thenCompose(this::getParentNetwork)
                .thenCompose(this::getParentNetworkRG)
                .thenCompose(this::getEndpointState)
                .thenCompose(this::getAuthentication)
                .thenCompose(this::getAzureClient);
    }

    private DeferredResult getSubnetState(AzureSubnetContext context) {
        return this.sendWithDeferredResult(
                Operation.createGet(context.request.resourceReference),
                SubnetState.class)
                .thenApply(subnetState -> {
                    context.subnetState = subnetState;
                    return context;
                });
    }

    private DeferredResult getParentNetworkRG(AzureSubnetContext context) {
        AssertUtil.assertNotNull(context.parentNetwork.groupLinks,
                "context.parentNetwork.groupLinks is null.");
        AssertUtil.assertTrue(context.parentNetwork.groupLinks.size() == 1,
                "context.parentNetwork.groupLinks doesn't contain exactly one element.");
        URI uri = context.request.buildUri(context.parentNetwork.groupLinks.iterator().next());
        return this.sendWithDeferredResult(
                Operation.createGet(uri),
                ResourceGroupState.class)
                .thenApply(resourceGroup -> {
                    context.parentNetworkResourceGroupName = resourceGroup.name;
                    return context;
                });
    }

    private DeferredResult getParentNetwork(AzureSubnetContext context) {
        URI uri = context.request.buildUri(context.subnetState.networkLink);
        return this.sendWithDeferredResult(
                Operation.createGet(uri),
                NetworkState.class)
                .thenApply(parentNetwork -> {
                    context.parentNetwork = parentNetwork;
                    return context;
                });
    }

    private DeferredResult getEndpointState(AzureSubnetContext context) {
        URI uri = context.request.buildUri(context.subnetState.endpointLink);
        return this.sendWithDeferredResult(
                Operation.createGet(uri),
                EndpointState.class)
                .thenApply(endpointState -> {
                    context.endpoint = endpointState;
                    return context;
                });
    }

    private DeferredResult getAuthentication(AzureSubnetContext context) {
        URI uri = context.request.buildUri(context.endpoint.authCredentialsLink);
        return this.sendWithDeferredResult(
                Operation.createGet(uri),
                AuthCredentialsServiceState.class)
                .thenApply(authCredentialsServiceState -> {
                    context.authentication = authCredentialsServiceState;
                    return context;
                });
    }

    private DeferredResult handleSubnetInstanceRequest(
            AzureSubnetContext context) {

        DeferredResult execution = DeferredResult.completed(context);

        switch (context.request.requestType) {
        case CREATE:
            if (context.request.isMockRequest) {
                // no need to go the end-point; just generate Azure Subnet Id.
                context.subnetState.id = UUID.randomUUID().toString();
            } else {
                execution = execution
                        //.thenCompose(this::checkSubnetCIDR)
                        .thenCompose(this::createSubnet);
            }

            return execution.thenCompose(this::updateSubnetState);

        case DELETE:
            if (context.request.isMockRequest) {
                // no need to go to the end-point
                this.logFine("Mock request to delete an Azure subnet ["
                        + context.subnetState.name + "] processed.");
            } else {
                execution = execution.thenCompose(this::deleteSubnet);
            }

            return execution.thenCompose(this::deleteSubnetState);
        default:
            IllegalStateException ex = new IllegalStateException("unsupported request type");
            return DeferredResult.failed(ex);
        }
    }

    private DeferredResult getAzureClient(AzureSubnetContext ctx) {

        // Creating a shared singleton Http client instance
        // Reference
        // https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.html
        // TODO: https://github.com/Azure/azure-sdk-for-java/issues/1000
        if (ctx.azureClient == null) {
            ctx.azureClient = getNetworkManagementClientImpl(ctx).subnets();
        }

        return DeferredResult.completed(ctx);
    }

    private DeferredResult createSubnet(AzureSubnetContext context) {
        SubnetInner subnet = new SubnetInner();
        subnet.withName(context.subnetState.name);
        subnet.withAddressPrefix(context.subnetState.subnetCIDR);

        String rgName = context.parentNetworkResourceGroupName;
        String vNetName = context.parentNetwork.name;

        final String msg = "Creating Azure Subnet [" + subnet.name()
                + "] in vNet [" + vNetName
                + "] in resource group [" + rgName + "].";
        AzureProvisioningCallback handler = new AzureProvisioningCallback(
                this, msg) {

            @Override
            protected DeferredResult consumeProvisioningSuccess(SubnetInner subnet) {
                // Populate the subnet id with Azure Subnet ID
                context.subnetState.id = subnet.id();
                return DeferredResult.completed(subnet);
            }

            @Override
            protected String getProvisioningState(SubnetInner subnet) {
                return subnet.provisioningState();
            }

            @Override
            protected Runnable checkProvisioningStateCall(
                    ServiceCallback checkProvisioningStateCallback) {
                return () -> context.azureClient.getAsync(
                        rgName,
                        vNetName,
                        subnet.name(),
                        null /* expand */,
                        checkProvisioningStateCallback);
            }
        };

        context.azureClient.createOrUpdateAsync(rgName, vNetName, subnet.name(),
                subnet, handler);

        return handler.toDeferredResult()
                .thenApply(ignore -> context);
    }

    private NetworkManagementClientImpl getNetworkManagementClientImpl(AzureSubnetContext ctx) {
        ApplicationTokenCredentials credentials =
                AzureUtils.getAzureConfig(ctx.authentication);
        ctx.restClient = AzureUtils.buildRestClient(credentials, this.executorService);
        NetworkManagementClientImpl client = new NetworkManagementClientImpl(ctx.restClient)
                .withSubscriptionId(ctx.authentication.userLink);
        return client;
    }

    private DeferredResult updateSubnetState(AzureSubnetContext context) {
        context.subnetState.lifecycleState = LifecycleState.READY;

        return this.sendWithDeferredResult(
                Operation.createPatch(this, context.subnetState.documentSelfLink)
                        .setBody(context.subnetState))
                .thenApply(op -> context);
    }

    private DeferredResult deleteSubnet(AzureSubnetContext context) {

        String rgName = context.parentNetworkResourceGroupName;
        String vNetName = context.parentNetwork.name;
        String subnetName = context.subnetState.name;

        final String msg = "Deleting Azure Subnet [" + subnetName
                + "] in vNet [" + vNetName
                + "] in resource group [" + rgName + "].";

        AzureDeferredResultServiceCallback handler =
                new AzureDeferredResultServiceCallback(this, msg) {
                    @Override
                    protected DeferredResult consumeSuccess(Void result) {
                        return DeferredResult.completed(null);
                    }
                };
        context.azureClient.deleteAsync(rgName, vNetName, subnetName, handler);
        return handler.toDeferredResult()
                .thenApply(ignore -> context);
    }

    private DeferredResult deleteSubnetState(AzureSubnetContext context) {
        return this.sendWithDeferredResult(
                Operation.createDelete(this, context.subnetState.documentSelfLink))
                .thenApply(operation -> context);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy