/*
 * esx_vi.h: client for the VMware VI API 2.5 to manage ESX hosts
 *
 * Copyright (C) 2011 Red Hat, Inc.
 * Copyright (C) 2009-2012, 2014 Matthias Bolte <matthias.bolte@googlemail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 */

#pragma once

#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <curl/curl.h>

#include "internal.h"
#include "virerror.h"
#include "datatypes.h"
#include "esx_vi_types.h"
#include "esx_util.h"

/* curl_multi_wait was added in libcurl 7.28.0, emulate it on older versions */
#define ESX_EMULATE_CURL_MULTI_WAIT (LIBCURL_VERSION_NUM < 0x071C00)



#define ESX_VI__SOAP__REQUEST_HEADER \
    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
    "<soapenv:Envelope\n" \
    " xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n" \
    " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"\n" \
    " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" \
    " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n" \
    "<soapenv:Body>\n"



#define ESX_VI__SOAP__REQUEST_FOOTER \
    "</soapenv:Body>\n" \
    "</soapenv:Envelope>"



#define ESV_VI__XML_TAG__OPEN(_buffer, _element, _type) \
    do { \
        virBufferAddLit(_buffer, "<"); \
        virBufferAdd(_buffer, _element, -1); \
        virBufferAddLit(_buffer, " xmlns=\"urn:vim25\" xsi:type=\""); \
        virBufferAdd(_buffer, _type, -1); \
        virBufferAddLit(_buffer, "\">"); \
    } while (0)



#define ESV_VI__XML_TAG__CLOSE(_buffer, _element) \
    do { \
        virBufferAddLit(_buffer, "</"); \
        virBufferAdd(_buffer, _element, -1); \
        virBufferAddLit(_buffer, ">"); \
    } while (0)



typedef enum _esxVI_ProductLine esxVI_ProductLine;
typedef enum _esxVI_Occurrence esxVI_Occurrence;
typedef struct _esxVI_ParsedHostCpuIdInfo esxVI_ParsedHostCpuIdInfo;
typedef struct _esxVI_CURL esxVI_CURL;
typedef struct _esxVI_SharedCURL esxVI_SharedCURL;
typedef struct _esxVI_MultiCURL esxVI_MultiCURL;
typedef struct _esxVI_Context esxVI_Context;
typedef struct _esxVI_Response esxVI_Response;
typedef struct _esxVI_Enumeration esxVI_Enumeration;
typedef struct _esxVI_EnumerationValue esxVI_EnumerationValue;
typedef struct _esxVI_List esxVI_List;



enum _esxVI_ProductLine {
    esxVI_ProductLine_GSX = 0,
    esxVI_ProductLine_ESX,
    esxVI_ProductLine_VPX
};

enum _esxVI_Occurrence {
    esxVI_Occurrence_Undefined = 0,
    esxVI_Occurrence_RequiredItem,
    esxVI_Occurrence_RequiredList,
    esxVI_Occurrence_OptionalItem,
    esxVI_Occurrence_OptionalList,
    esxVI_Occurrence_None
};

struct _esxVI_ParsedHostCpuIdInfo {
    int level;
    char eax[32];
    char ebx[32];
    char ecx[32];
    char edx[32];
};



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * CURL
 */

struct _esxVI_CURL {
    CURL *handle;
    virMutex lock;
    struct curl_slist *headers;
    char error[CURL_ERROR_SIZE];
    esxVI_SharedCURL *shared;
    esxVI_MultiCURL *multi;
};

int esxVI_CURL_Alloc(esxVI_CURL **curl);
void esxVI_CURL_Free(esxVI_CURL **curl);
int esxVI_CURL_Connect(esxVI_CURL *curl, esxUtil_ParsedUri *parsedUri);
int esxVI_CURL_Download(esxVI_CURL *curl, const char *url, char **content,
                        unsigned long long offset, unsigned long long *length);
int esxVI_CURL_Upload(esxVI_CURL *curl, const char *url, const char *content);



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * SharedCURL
 */

struct _esxVI_SharedCURL {
    CURLSH *handle;
    virMutex locks[3]; /* share, cookie, dns */
    size_t count; /* number of added easy handle */
};

int esxVI_SharedCURL_Alloc(esxVI_SharedCURL **shared);
void esxVI_SharedCURL_Free(esxVI_SharedCURL **shared);
int esxVI_SharedCURL_Add(esxVI_SharedCURL *shared, esxVI_CURL *curl);
int esxVI_SharedCURL_Remove(esxVI_SharedCURL *shared, esxVI_CURL *curl);



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * MultiCURL
 */

struct _esxVI_MultiCURL {
    CURLM *handle;
    size_t count; /* number of added easy handle */
#if ESX_EMULATE_CURL_MULTI_WAIT
    struct pollfd *pollfds;
    size_t npollfds;
    bool timeoutPending;
#endif
};

int esxVI_MultiCURL_Alloc(esxVI_MultiCURL **multi);
void esxVI_MultiCURL_Free(esxVI_MultiCURL **multi);
int esxVI_MultiCURL_Add(esxVI_MultiCURL *multi, esxVI_CURL *curl);
int esxVI_MultiCURL_Remove(esxVI_MultiCURL *multi, esxVI_CURL *curl);
int esxVI_MultiCURL_Wait(esxVI_MultiCURL *multi, int *runningHandles);
int esxVI_MultiCURL_Perform(esxVI_MultiCURL *multi, int *runningHandles);
int esxVI_MultiCURL_CheckFirstMessage(esxVI_MultiCURL *multi, long *responseCode,
                                      CURLcode *errorCode);



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Context
 */

struct _esxVI_Context {
    /* All members are used read-only after esxVI_Context_Connect ... */
    esxVI_CURL *curl;
    char *url;
    char *ipAddress;
    char *username;
    char *password;
    esxVI_ServiceContent *service;
    unsigned long apiVersion; /* = 1000000 * major + 1000 * minor + micro */
    esxVI_ProductLine productLine;
    unsigned long productVersion; /* = 1000000 * major + 1000 * minor + micro */
    esxVI_UserSession *session; /* ... except the session ... */
    virMutexPtr sessionLock; /* ... that is protected by this mutex */
    esxVI_Datacenter *datacenter;
    char *datacenterPath; /* including folders */
    esxVI_ComputeResource *computeResource;
    char *computeResourcePath; /* including folders */
    esxVI_HostSystem *hostSystem;
    char *hostSystemName;
    esxVI_SelectionSpec *selectSet_folderToChildEntity;
    esxVI_SelectionSpec *selectSet_hostSystemToParent;
    esxVI_SelectionSpec *selectSet_hostSystemToVm;
    esxVI_SelectionSpec *selectSet_hostSystemToDatastore;
    esxVI_SelectionSpec *selectSet_computeResourceToHost;
    esxVI_SelectionSpec *selectSet_computeResourceToParentToParent;
    esxVI_SelectionSpec *selectSet_datacenterToNetwork;
    bool hasQueryVirtualDiskUuid;
    bool hasSessionIsActive;
};

int esxVI_Context_Alloc(esxVI_Context **ctx);
void esxVI_Context_Free(esxVI_Context **ctx);
int esxVI_Context_Connect(esxVI_Context *ctx, const char *ipAddress,
                          const char *url, const char *username,
                          const char *password, esxUtil_ParsedUri *parsedUri);
int esxVI_Context_LookupManagedObjects(esxVI_Context *ctx);
int esxVI_Context_LookupManagedObjectsByPath(esxVI_Context *ctx, const char *path);
int esxVI_Context_LookupManagedObjectsByHostSystemIp(esxVI_Context *ctx,
                                                     const char *hostSystemIPAddress);
int esxVI_Context_Execute(esxVI_Context *ctx, const char *methodName,
                          const char *request, esxVI_Response **response,
                          esxVI_Occurrence occurrence);



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Response
 */

struct _esxVI_Response {
    int responseCode;                                 /* required */
    char *content;                                    /* required */
    xmlDocPtr document;                               /* optional */
    xmlNodePtr node;                                  /* optional, list */
};

int esxVI_Response_Alloc(esxVI_Response **response);
void esxVI_Response_Free(esxVI_Response **response);



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Enumeration
 */

struct _esxVI_EnumerationValue {
    const char *name;
    int value;
};

struct _esxVI_Enumeration {
    esxVI_Type type;
    esxVI_EnumerationValue values[10];
};

int esxVI_Enumeration_CastFromAnyType(const esxVI_Enumeration *enumeration,
                                      esxVI_AnyType *anyType, int *value);
int esxVI_Enumeration_Serialize(const esxVI_Enumeration *enumeration,
                                int value, const char *element,
                                virBufferPtr output);
int esxVI_Enumeration_Deserialize(const esxVI_Enumeration *enumeration,
                                  xmlNodePtr node, int *value);



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * List
 */

struct _esxVI_List {
    esxVI_List *_next;
};

typedef int (*esxVI_List_FreeFunc) (esxVI_List **item);
typedef int (*esxVI_List_DeepCopyFunc) (esxVI_List **dest, esxVI_List *src);
typedef int (*esxVI_List_CastFromAnyTypeFunc) (esxVI_AnyType *anyType,
                                               esxVI_List **item);
typedef int (*esxVI_List_SerializeFunc) (esxVI_List *item, const char *element,
                                         virBufferPtr output);
typedef int (*esxVI_List_DeserializeFunc) (xmlNodePtr node, esxVI_List **item);

int esxVI_List_Append(esxVI_List **list, esxVI_List *item);
int esxVI_List_DeepCopy(esxVI_List **destList, esxVI_List *srcList,
                        esxVI_List_DeepCopyFunc deepCopyFunc,
                        esxVI_List_FreeFunc freeFunc);
int esxVI_List_CastFromAnyType(esxVI_AnyType *anyType, esxVI_List **list,
                               esxVI_List_CastFromAnyTypeFunc castFromAnyTypeFunc,
                               esxVI_List_FreeFunc freeFunc);
int esxVI_List_Serialize(esxVI_List *list, const char *element,
                         virBufferPtr output,
                         esxVI_List_SerializeFunc serializeFunc);
int esxVI_List_Deserialize(xmlNodePtr node, esxVI_List **list,
                           esxVI_List_DeserializeFunc deserializeFunc,
                           esxVI_List_FreeFunc freeFunc);



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Utility and Convenience Functions
 *
 * Function naming scheme:
 *  - 'lookup' functions query the ESX or vCenter for information
 *  - 'get' functions get information from a local object
 */

int esxVI_BuildSelectSet
      (esxVI_SelectionSpec **selectSet, const char *name,
       const char *type, const char *path, const char *selectSetNames);

int esxVI_BuildSelectSetCollection(esxVI_Context *ctx);

int esxVI_EnsureSession(esxVI_Context *ctx);

int esxVI_LookupObjectContentByType(esxVI_Context *ctx,
                                    esxVI_ManagedObjectReference *root,
                                    const char *type,
                                    esxVI_String *propertyNameList,
                                    esxVI_ObjectContent **objectContentList,
                                    esxVI_Occurrence occurrence);

int esxVI_GetManagedEntityStatus
      (esxVI_ObjectContent *objectContent, const char *propertyName,
       esxVI_ManagedEntityStatus *managedEntityStatus);

int esxVI_GetVirtualMachinePowerState
      (esxVI_ObjectContent *virtualMachine,
       esxVI_VirtualMachinePowerState *powerState);

int esxVI_GetVirtualMachineQuestionInfo
      (esxVI_ObjectContent *virtualMachine,
       esxVI_VirtualMachineQuestionInfo **questionInfo);

int esxVI_GetVirtualMachineMORef
      (esxVI_ObjectContent *virtualMachine,
       char **moref);

int esxVI_GetBoolean(esxVI_ObjectContent *objectContent,
                     const char *propertyName,
                     esxVI_Boolean *value, esxVI_Occurrence occurrence);

int esxVI_GetInt(esxVI_ObjectContent *objectContent, const char *propertyName,
                 esxVI_Int **value, esxVI_Occurrence occurrence);

int esxVI_GetLong(esxVI_ObjectContent *objectContent, const char *propertyName,
                  esxVI_Long **value, esxVI_Occurrence occurrence);

int esxVI_GetStringValue(esxVI_ObjectContent *objectContent,
                         const char *propertyName,
                         char **value, esxVI_Occurrence occurrence);

int esxVI_GetManagedObjectReference(esxVI_ObjectContent *objectContent,
                                    const char *propertyName,
                                    esxVI_ManagedObjectReference **value,
                                    esxVI_Occurrence occurrence);

int esxVI_LookupNumberOfDomainsByPowerState
      (esxVI_Context *ctx, esxVI_VirtualMachinePowerState powerState,
       bool inverse);

int esxVI_GetVirtualMachineIdentity(esxVI_ObjectContent *virtualMachine,
                                    int *id, char **name, unsigned char *uuid);

int esxVI_GetNumberOfSnapshotTrees
      (esxVI_VirtualMachineSnapshotTree *snapshotTreeList,
       bool recurse, bool leaves);

int esxVI_GetSnapshotTreeNames
      (esxVI_VirtualMachineSnapshotTree *snapshotTreeList, char **names,
       int nameslen, bool recurse, bool leaves);

int esxVI_GetSnapshotTreeByName
      (esxVI_VirtualMachineSnapshotTree *snapshotTreeList, const char *name,
       esxVI_VirtualMachineSnapshotTree **snapshotTree,
       esxVI_VirtualMachineSnapshotTree **snapshotTreeParent,
       esxVI_Occurrence occurrence);

int esxVI_GetSnapshotTreeBySnapshot
      (esxVI_VirtualMachineSnapshotTree *snapshotTreeList,
       esxVI_ManagedObjectReference *snapshot,
       esxVI_VirtualMachineSnapshotTree **snapshotTree);

int esxVI_LookupHostSystemProperties(esxVI_Context *ctx,
                                     esxVI_String *propertyNameList,
                                     esxVI_ObjectContent **hostSystem);

int esxVI_LookupVirtualMachineList(esxVI_Context *ctx,
                                   esxVI_String *propertyNameList,
                                   esxVI_ObjectContent **virtualMachineList);

int esxVI_LookupVirtualMachineByUuid(esxVI_Context *ctx,
                                     const unsigned char *uuid,
                                     esxVI_String *propertyNameList,
                                     esxVI_ObjectContent **virtualMachine,
                                     esxVI_Occurrence occurrence);

int esxVI_LookupVirtualMachineByName(esxVI_Context *ctx, const char *name,
                                     esxVI_String *propertyNameList,
                                     esxVI_ObjectContent **virtualMachine,
                                     esxVI_Occurrence occurrence);

int esxVI_LookupVirtualMachineByUuidAndPrepareForTask
      (esxVI_Context *ctx, const unsigned char *uuid,
       esxVI_String *propertyNameList, esxVI_ObjectContent **virtualMachine,
       bool autoAnswer);

int esxVI_LookupDatastoreList(esxVI_Context *ctx, esxVI_String *propertyNameList,
                              esxVI_ObjectContent **datastoreList);

int esxVI_LookupDatastoreByName(esxVI_Context *ctx, const char *name,
                                esxVI_String *propertyNameList,
                                esxVI_ObjectContent **datastore,
                                esxVI_Occurrence occurrence);

int esxVI_LookupDatastoreByAbsolutePath(esxVI_Context *ctx,
                                        const char *absolutePath,
                                        esxVI_String *propertyNameList,
                                        esxVI_ObjectContent **datastore,
                                        esxVI_Occurrence occurrence);

int esxVI_LookupDatastoreHostMount(esxVI_Context *ctx,
                                   esxVI_ManagedObjectReference *datastore,
                                   esxVI_DatastoreHostMount **hostMount,
                                   esxVI_Occurrence occurrence);

int esxVI_LookupTaskInfoByTask(esxVI_Context *ctx,
                               esxVI_ManagedObjectReference *task,
                               esxVI_TaskInfo **taskInfo);

int esxVI_LookupPendingTaskInfoListByVirtualMachine
      (esxVI_Context *ctx, esxVI_ObjectContent *virtualMachine,
       esxVI_TaskInfo **pendingTaskInfoList);

int esxVI_LookupAndHandleVirtualMachineQuestion(esxVI_Context *ctx,
                                                const unsigned char *uuid,
                                                esxVI_Occurrence occurrence,
                                                bool autoAnswer, bool *blocked);

int esxVI_LookupRootSnapshotTreeList
      (esxVI_Context *ctx, const unsigned char *virtualMachineUuid,
       esxVI_VirtualMachineSnapshotTree **rootSnapshotTreeList);

int esxVI_LookupCurrentSnapshotTree
      (esxVI_Context *ctx, const unsigned char *virtualMachineUuid,
       esxVI_VirtualMachineSnapshotTree **currentSnapshotTree,
       esxVI_Occurrence occurrence);

int esxVI_LookupFileInfoByDatastorePath(esxVI_Context *ctx,
                                        const char *datastorePath,
                                        bool lookupFolder,
                                        esxVI_FileInfo **fileInfo,
                                        esxVI_Occurrence occurrence);

int esxVI_LookupDatastoreContentByDatastoreName
      (esxVI_Context *ctx, const char *datastoreName,
       esxVI_HostDatastoreBrowserSearchResults **searchResultsList);

int esxVI_LookupStorageVolumeKeyByDatastorePath(esxVI_Context *ctx,
                                                const char *datastorePath,
                                                char **key);

int esxVI_LookupAutoStartDefaults(esxVI_Context *ctx,
                                  esxVI_AutoStartDefaults **defaults);

int esxVI_LookupAutoStartPowerInfoList(esxVI_Context *ctx,
                                       esxVI_AutoStartPowerInfo **powerInfoList);

int esxVI_LookupPhysicalNicList(esxVI_Context *ctx,
                                esxVI_PhysicalNic **physicalNicList);

int esxVI_LookupPhysicalNicByName(esxVI_Context *ctx, const char *name,
                                  esxVI_PhysicalNic **physicalNic,
                                  esxVI_Occurrence occurrence);

int esxVI_LookupPhysicalNicByMACAddress(esxVI_Context *ctx, const char *mac,
                                        esxVI_PhysicalNic **physicalNic,
                                        esxVI_Occurrence occurrence);

int esxVI_LookupHostVirtualSwitchList
      (esxVI_Context *ctx, esxVI_HostVirtualSwitch **hostVirtualSwitchList);

int esxVI_LookupHostVirtualSwitchByName(esxVI_Context *ctx, const char *name,
                                        esxVI_HostVirtualSwitch **hostVirtualSwitch,
                                        esxVI_Occurrence occurrence);

int esxVI_LookupHostPortGroupList(esxVI_Context *ctx,
                                  esxVI_HostPortGroup **hostPortGroupList);

int esxVI_LookupNetworkList(esxVI_Context *ctx, esxVI_String *propertyNameList,
                            esxVI_ObjectContent **networkList);

int esxVI_HandleVirtualMachineQuestion
      (esxVI_Context *ctx, esxVI_ManagedObjectReference *virtualMachine,
       esxVI_VirtualMachineQuestionInfo *questionInfo, bool autoAnswer,
       bool *blocked);

int esxVI_WaitForTaskCompletion(esxVI_Context *ctx,
                                esxVI_ManagedObjectReference *task,
                                const unsigned char *virtualMachineUuid,
                                esxVI_Occurrence virtualMachineOccurrence,
                                bool autoAnswer,
                                esxVI_TaskInfoState *finalState,
                                char **errorMessage);

int esxVI_ParseHostCpuIdInfo(esxVI_ParsedHostCpuIdInfo *parsedHostCpuIdInfo,
                             esxVI_HostCpuIdInfo *hostCpuIdInfo);

const char *esxVI_ProductLineToDisplayName(esxVI_ProductLine productLine);

int esxVI_ProductVersionToDefaultVirtualHWVersion
      (esxVI_ProductLine productLine, unsigned long productVersion);

int esxVI_LookupHostInternetScsiHbaStaticTargetByName
      (esxVI_Context *ctx, const char *name,
       esxVI_HostInternetScsiHbaStaticTarget **target,
       esxVI_Occurrence occurrence);

int esxVI_LookupHostInternetScsiHba
      (esxVI_Context *ctx, esxVI_HostInternetScsiHba **hostInternetScsiHba);

int esxVI_LookupScsiLunList(esxVI_Context *ctx, esxVI_ScsiLun **scsiLunList);

int esxVI_LookupHostScsiTopologyLunListByTargetName
      (esxVI_Context *ctx, const char *name,
       esxVI_HostScsiTopologyLun **hostScsiTopologyLunList);

int esxVI_LookupStoragePoolNameByScsiLunKey(esxVI_Context *ctx, const char *key,
                                            char **poolName);

#include "esx_vi.generated.h"
