net.roboconf.agent.internal.misc.UserDataUtils.java Source code

Java tutorial

Introduction

Here is the source code for net.roboconf.agent.internal.misc.UserDataUtils.java

Source

/**
 * Copyright 2014-2017 Linagora, Universit Joseph Fourier, Floralis
 *
 * The present code is developed in the scope of the joint LINAGORA -
 * Universit Joseph Fourier - Floralis research program and is designated
 * as a "Result" pursuant to the terms and conditions of the LINAGORA
 * - Universit Joseph Fourier - Floralis research program. Each copyright
 * holder of Results enumerated here above fully & independently holds complete
 * ownership of the complete Intellectual Property rights applicable to the whole
 * of said Results, and may freely exploit it in any manner which does not infringe
 * the moral rights of the other copyright holders.
 *
 * 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 net.roboconf.agent.internal.misc;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.codec.binary.Base64;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import net.roboconf.agent.internal.AgentProperties;
import net.roboconf.core.Constants;
import net.roboconf.core.utils.Utils;
import net.roboconf.messaging.api.MessagingConstants;

/**
 * @author Nol - LIG
 * @author Pierre-Yves Gibello - Linagora
 */
public final class UserDataUtils {

    public static final String CONF_FILE_AGENT = "net.roboconf.agent.configuration.cfg";

    /**
     * Private empty constructor.
     */
    private UserDataUtils() {
        // nothing
    }

    /**
     * Configures the agent from a IaaS registry.
     * @param logger a logger
     * @return the agent's data, or null if they could not be parsed
     */
    public static AgentProperties findParametersForAmazonOrOpenStack(Logger logger) {

        // Copy the user data
        String userData = "";
        InputStream in = null;
        try {
            URL userDataUrl = new URL("http://169.254.169.254/latest/user-data");
            in = userDataUrl.openStream();
            ByteArrayOutputStream os = new ByteArrayOutputStream();

            Utils.copyStreamSafely(in, os);
            userData = os.toString("UTF-8");

        } catch (IOException e) {
            logger.severe("The agent properties could not be read. " + e.getMessage());
            Utils.logException(logger, e);
        }

        AgentProperties result = null;
        in = null;
        try {
            // Parse the user data
            result = AgentProperties.readIaasProperties(userData, logger);

            // We need to ask our IP address because we may have several network interfaces.
            URL userDataUrl = new URL("http://169.254.169.254/latest/meta-data/public-ipv4");
            in = userDataUrl.openStream();
            ByteArrayOutputStream os = new ByteArrayOutputStream();

            Utils.copyStreamSafely(in, os);
            String ip = os.toString("UTF-8");
            if (!AgentUtils.isValidIP(ip)) {
                // Failed retrieving public IP: try private one instead
                Utils.closeQuietly(in);
                userDataUrl = new URL("http://169.254.169.254/latest/meta-data/local-ipv4");
                in = userDataUrl.openStream();
                os = new ByteArrayOutputStream();

                Utils.copyStreamSafely(in, os);
                ip = os.toString("UTF-8");
            }

            if (!AgentUtils.isValidIP(ip))
                throw new IOException("No IP address could be retrieved (either public-ipv4 or local-ipv4)");

            result.setIpAddress(os.toString("UTF-8"));

        } catch (IOException e) {
            logger.severe("The network properties could not be read. " + e.getMessage());
            Utils.logException(logger, e);

        } finally {
            Utils.closeQuietly(in);
        }

        return result;
    }

    /**
     * Configures the agent from Azure.
     * @param logger a logger
     * @return the agent's data, or null if they could not be parsed
     */
    public static AgentProperties findParametersForAzure(Logger logger) {

        String userData = "";
        try {
            // Get the user data from /var/lib/waagent/ovf-env.xml and decode it
            String userDataEncoded = getValueOfTagInXMLFile("/var/lib/waagent/ovf-env.xml", "CustomData");
            userData = new String(Base64.decodeBase64(userDataEncoded.getBytes("UTF-8")), "UTF-8");

        } catch (IOException | ParserConfigurationException | SAXException e) {
            logger.severe("The agent properties could not be read. " + e.getMessage());
            Utils.logException(logger, e);
        }

        // Get the public IP Address from /var/lib/waagent/SharedConfig.xml
        AgentProperties result = null;
        String publicIPAddress;
        try {
            result = AgentProperties.readIaasProperties(userData, logger);
            publicIPAddress = getSpecificAttributeOfTagInXMLFile("/var/lib/waagent/SharedConfig.xml", "Endpoint",
                    "loadBalancedPublicAddress");
            result.setIpAddress(publicIPAddress);

        } catch (ParserConfigurationException | SAXException e) {
            logger.severe("The agent could not retrieve a public IP address. " + e.getMessage());
            Utils.logException(logger, e);

        } catch (IOException e) {
            logger.severe("The agent could not retrieve its configuration. " + e.getMessage());
            Utils.logException(logger, e);
        }

        return result;
    }

    /**
     * Configures the agent from VMWare.
     * @param logger a logger
     * @return the agent's data, or null if they could not be parsed
     */
    public static AgentProperties findParametersForVmware(Logger logger) {

        File propertiesFile = new File("/tmp/roboconf.properties");
        try {
            int retries = 30;
            while ((!propertiesFile.exists() || !propertiesFile.canRead()) && retries-- > 0) {
                logger.fine("Agent tries to read properties file " + propertiesFile + ": trial #" + (30 - retries));
                try {
                    Thread.sleep(2000);

                } catch (InterruptedException e) {
                    throw new IOException("Can't read properties file: " + e);
                }
            }

            AgentProperties result = AgentProperties.readIaasProperties(Utils.readPropertiesFile(propertiesFile));

            /*
             * HACK for specific IaaS configurations (using properties file in a VMWare-like manner)
             * Try to pick IP address... in the case we are on OpenStack or any IaaS with amazon-compatible API
             * Some configurations (with floating IPs) do not provide network interfaces exposing public IPs !
             */
            InputStream in = null;
            try {
                URL userDataUrl = new URL("http://169.254.169.254/latest/meta-data/public-ipv4");
                in = userDataUrl.openStream();
                ByteArrayOutputStream os = new ByteArrayOutputStream();

                Utils.copyStreamSafely(in, os);
                String ip = os.toString("UTF-8");
                if (!AgentUtils.isValidIP(ip)) {
                    // Failed retrieving public IP: try private one instead
                    Utils.closeQuietly(in);
                    userDataUrl = new URL("http://169.254.169.254/latest/meta-data/local-ipv4");
                    in = userDataUrl.openStream();
                    os = new ByteArrayOutputStream();

                    Utils.copyStreamSafely(in, os);
                    ip = os.toString("UTF-8");
                }

                if (AgentUtils.isValidIP(ip))
                    result.setIpAddress(os.toString("UTF-8"));

            } catch (IOException e) {
                Utils.logException(logger, e);
            } finally {
                Utils.closeQuietly(in);
            }
            /* HACK ends here (see comment above). Removing it is harmless on classical VMWare configurations. */

            return result;

        } catch (IOException e) {
            logger.fine("Agent failed to read properties file " + propertiesFile);
            return null;
        }
    }

    /**
     * Reconfigures the messaging.
     * @param etcDir the KARAF_ETC directory
     * @param msgData the messaging configuration parameters
     */
    public static void reconfigureMessaging(String etcDir, Map<String, String> msgData) throws IOException {

        String messagingType = msgData.get(MessagingConstants.MESSAGING_TYPE_PROPERTY);
        Logger.getLogger(UserDataUtils.class.getName())
                .fine("Messaging type for reconfiguration: " + messagingType);
        if (!Utils.isEmptyOrWhitespaces(etcDir)) {

            // Write the messaging configuration
            Properties props = new Properties();
            props.putAll(msgData);
            props.remove(Constants.MESSAGING_TYPE);

            File f = new File(etcDir, "net.roboconf.messaging." + messagingType + ".cfg");
            Utils.writePropertiesFile(props, f);

            // Set the messaging type
            f = new File(etcDir, CONF_FILE_AGENT);

            props = Utils.readPropertiesFileQuietly(f, Logger.getLogger(UserDataUtils.class.getName()));
            props.put(Constants.MESSAGING_TYPE, messagingType);
            Utils.writePropertiesFile(props, f);
        }
    }

    // FIXME: there must be a shorter way with XPath...
    private static String getValueOfTagInXMLFile(String filePath, String tagName)
            throws ParserConfigurationException, SAXException, IOException {

        File fXmlFile = new File(filePath);
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        Document doc = dBuilder.parse(fXmlFile);

        // Optional, but recommended
        // Read this: http://stackoverflow.com/questions/13786607/normalization-in-dom-parsing-with-java-how-does-it-work
        doc.getDocumentElement().normalize();

        NodeList nList = doc.getElementsByTagName(tagName);
        String valueOfTagName = "";

        for (int temp = 0; temp < nList.getLength(); temp++) {
            Node nNode = nList.item(temp);
            valueOfTagName = nNode.getTextContent();
        }

        return valueOfTagName;
    }

    private static String getSpecificAttributeOfTagInXMLFile(String filePath, String tagName, String attrName)
            throws ParserConfigurationException, SAXException, IOException {

        File fXmlFile = new File(filePath);
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        Document doc = dBuilder.parse(fXmlFile);

        doc.getDocumentElement().normalize();

        NodeList nList = doc.getElementsByTagName(tagName);
        Node aNode = nList.item(2);
        NamedNodeMap attributes = aNode.getAttributes();
        String attrValue = "";
        for (int a = 0; a < attributes.getLength(); a++) {
            Node theAttribute = attributes.item(a);
            if (attrName.equals(theAttribute.getNodeName()))
                attrValue = theAttribute.getTextContent().split(":")[0];
        }

        return attrValue;
    }
}