Skip to content
Snippets Groups Projects
pathComp_RESTapi.c 75.4 KiB
Newer Older
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
/*
 * Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
 *
 * 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.
 */
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed

#include <stdio.h>
#include <stdlib.h> 
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <glib.h>
#include <sys/time.h>
#include <ctype.h>
#include <strings.h>
#include <time.h>
#include <fcntl.h>
#include <uuid/uuid.h>
#include <string.h>

#include "pathComp_log.h"
#include "pathComp_tools.h"
#include "pathComp_cjson.h"
#include "pathComp_ksp.h"
#include "pathComp_sp.h"
martinezric's avatar
martinezric committed
#include "pathComp_ear.h"
#include "pathComp_RESTapi.h"

#define ISspace(x) isspace((int)(x))

#define SERVER_STRING "Server: PATHCOMP/0.1.0\r\n"

// List of Clients connected to the PATH COMP
GList *RESTapi_tcp_client_list = NULL;

// Id for CLient HTTP (REST API) Connection
guint CLIENT_ID = 0;
guint32 paId_req = 0;

// Global variables
martinezric's avatar
martinezric committed
GList* linkList;
GList* deviceList;
GList* serviceList;
GList* activeServList;

gchar algId[MAX_ALG_ID_LENGTH];
gboolean syncPath = FALSE;

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Seek a connected tcp client by its fd in the RESTapi_tcp_client_list
 * 	
 * 	@param data
 *  @param userdata
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gint find_rl_client_by_fd (gconstpointer data, gconstpointer userdata)
{
	 /** check values */
     g_assert(data != NULL);
     g_assert(userdata != NULL);
	 
	 struct pathComp_client *client = (struct pathComp_client*)data;
     gint fd = *(gint *)userdata; 
     
martinezric's avatar
martinezric committed
	 if (client->fd == fd) {
		 return 0;
	 }
    return -1;	
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to send a message to the corresponding channel
 * 	
 * 	@param source
 *  @param buf
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gint rapi_send_message (GIOChannel *channel, char *buf)	{
	gsize nbytes, buffersize;

	//DEBUG_PC ("Msg prepared to be sent REST API RESPONSE ");
    gint len = strlen ((const char*) buf);
    //DEBUG_PC ("Targeted Length of the buffer: %d", len);       

    buffersize = g_io_channel_get_buffer_size (channel);
    //DEBUG_PC ("GIOChannel with Buffer Size: %d", (gint)buffersize);
    
    gsize newBufferSize = MAX_GIO_CHANNEL_BUFFER_SIZE;
    g_io_channel_set_buffer_size (channel, newBufferSize);
    
    buffersize = g_io_channel_get_buffer_size (channel);
    //DEBUG_PC ("GIOChannel with Buffer Size: %d", (gint)buffersize);
    
	/** Send message.  */
    GError *error = NULL;
    
    char *ptr = buf;
    gint nleft = strlen ((const char *)buf);
    
    while (nleft > 0) {
        g_io_channel_write_chars (channel, (void *)ptr, nleft, &nbytes, &error);
        if (error) {
            DEBUG_PC ("Error sending the message to TCP Client");
            return (-1);
        }
        
        //DEBUG_PC ("Sent %d bytes", (gint)nbytes);        
        nleft = nleft - nbytes;
        //DEBUG_PC ("Remaining to be sent %d", nleft);
        ptr += nbytes;        
    } 
	DEBUG_PC("RESPONSE MSG SENT");
	return 0;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to return when something goes wrong when processing the REST API Command
 * 	
 * 	@param source
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void RESTapi_unimplemented (GIOChannel *source)
{
	gint ret = 0;
	guchar buftmp[1024];
	char *buf = g_malloc0 (sizeof (char) * 2048000); 
	sprintf((char *)buf, "HTTP/1.1 400 Bad request\r\n");

	sprintf((char *)buftmp, SERVER_STRING);
	strcat ((char *)buf, (const char *)buftmp);

	sprintf((char *)buftmp, "Content-Type: text/plain\r\n");
	strcat ((char *)buf, (const char *)buftmp);

	sprintf((char *)buftmp, "\r\n");
	strcat ((char *)buf, (const char *)buftmp);
	      
	sprintf((char *)buftmp, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
	strcat ((char *)buf, (const char *)buftmp);

	sprintf((char *)buftmp, "</TITLE></HEAD>\r\n");
	strcat ((char *)buf, (const char *)buftmp);

	sprintf((char *)buftmp, "<BODY><P>HTTP request method not supported.\r\n");
	strcat ((char *)buf, (const char *)buftmp);	

	sprintf((char *)buftmp, "</BODY></HTML>\r\n");
	strcat ((char *)buf, (const char *)buftmp);

	ret = rapi_send_message (source, buf);
	g_free (buf);
	(void)ret;

	return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to put in the buffer the date according to RFC 1123
 * 	
 * 	@param date
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void rapi_add_date_header (char *date)
{
    static const char *DAYS[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
    static const char *MONTHS[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

    time_t t = time(NULL);
    struct tm *tm = NULL;
    struct tm sys;
    gmtime_r(&t, &sys);
    tm = &sys;

    sprintf((char *)date, "DATE: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", DAYS[tm->tm_wday], tm->tm_mday, 
								      MONTHS[tm->tm_mon], 1900 + tm->tm_year, 
								      tm->tm_hour, tm->tm_min, tm->tm_sec);
    
    return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to add DeviceId and EndpointId forming the computed path
 *
 * 	@param pathObj
 *  @param p
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void add_comp_path_deviceId_endpointId_json(cJSON* pathObj, struct path_t* p, struct compRouteOutput_t* oElem) {
	g_assert(p);
	g_assert(pathObj);

	// add array for the devideId and endpointIds
	cJSON* devicesArray = cJSON_CreateArray();
	cJSON_AddItemToObject(pathObj, "devices", devicesArray);

	// Add the source endpoint
	cJSON* sEndPointIdObj;
	cJSON_AddItemToArray(devicesArray, sEndPointIdObj = cJSON_CreateObject());
	// Add the topology Id Object containing the topology uuid and context uuid
	cJSON* stopIdObj;
	cJSON_AddItemToObject(sEndPointIdObj, "topology_id", stopIdObj = cJSON_CreateObject());
	cJSON_AddItemToObject(stopIdObj, "contextId", cJSON_CreateString(oElem->service_endpoints_id[0].topology_id.contextId));
	cJSON_AddItemToObject(stopIdObj, "topology_uuid", cJSON_CreateString(oElem->service_endpoints_id[0].topology_id.topology_uuid));

	// Add the device Id (uuid) 
	cJSON_AddItemToObject(sEndPointIdObj, "device_id", cJSON_CreateString(oElem->service_endpoints_id[0].device_uuid));
	// Add the endpoint Id (uuid)
	cJSON_AddItemToObject(sEndPointIdObj, "endpoint_uuid", cJSON_CreateString(oElem->service_endpoints_id[0].endpoint_uuid));


	for (gint i = 0; i < p->numPathLinks; i++) {
		struct pathLink_t* pL = &(p->pathLinks[i]);
		cJSON* dElemObj; // Device Element Object of the array
		cJSON_AddItemToArray(devicesArray, dElemObj = cJSON_CreateObject());

		// Add the topologyId with the topologyuuid and contextId
		cJSON* tIdObj;
		cJSON_AddItemToObject(dElemObj, "topology_id", tIdObj = cJSON_CreateObject());
		cJSON_AddItemToObject(tIdObj, "contextId", cJSON_CreateString(pL->topologyId.contextId));
		cJSON_AddItemToObject(tIdObj, "topology_uuid", cJSON_CreateString(pL->topologyId.topology_uuid));

		// Add Device Id
		cJSON_AddItemToObject(dElemObj, "device_id", cJSON_CreateString(pL->aDeviceId));

		// Add endpoint Id
martinezric's avatar
martinezric committed
		cJSON_AddItemToObject(dElemObj, "endpoint_uuid", cJSON_CreateString(pL->aEndPointId));  
	}
	
	// Add the sink	endpoint
	cJSON* sinkEndPointIdObj;
	cJSON_AddItemToArray(devicesArray, sinkEndPointIdObj = cJSON_CreateObject());
	// Add the topology Id Object containing the topology uuid and context uuid
	cJSON* sinkTopIdObj;
	cJSON_AddItemToObject(sinkEndPointIdObj, "topology_id", sinkTopIdObj = cJSON_CreateObject());
	cJSON_AddItemToObject(sinkTopIdObj, "contextId", cJSON_CreateString(oElem->service_endpoints_id[1].topology_id.contextId));
	cJSON_AddItemToObject(sinkTopIdObj, "topology_uuid", cJSON_CreateString(oElem->service_endpoints_id[1].topology_id.topology_uuid));

	// Add the device Id (uuid) 
	cJSON_AddItemToObject(sinkEndPointIdObj, "device_id", cJSON_CreateString(oElem->service_endpoints_id[1].device_uuid));
	// Add the endpoint Id (uuid)
	cJSON_AddItemToObject(sinkEndPointIdObj, "endpoint_uuid", cJSON_CreateString(oElem->service_endpoints_id[1].endpoint_uuid));
	
martinezric's avatar
martinezric committed
	return;				 
////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to add the links forming the computed path
 *
 * 	@param pathObj
 *  @param p
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void add_comp_path_link_json(cJSON* pathObj, struct path_t* p) {
	g_assert(p);
	g_assert(pathObj);

	// Add array for the links
	cJSON* linkArray = cJSON_CreateArray();
	cJSON_AddItemToObject(pathObj, "link", linkArray);

	for (gint i = 0; i < p->numPathLinks; i++) {
		struct pathLink_t* pL = &(p->pathLinks[i]);
		cJSON* lElemObj; // Link Element Object of the array
		cJSON_AddItemToArray(linkArray, lElemObj = cJSON_CreateObject());

		// Add link Id
		cJSON_AddItemToObject(lElemObj, "link_Id", cJSON_CreateString(pL->linkId));

		// Add link topologies
		cJSON* linkTopoArray = cJSON_CreateArray();
		cJSON_AddItemToObject(lElemObj, "topology", linkTopoArray);

		for (gint j = 0; j < pL->numLinkTopologies; j++) {
			struct linkTopology_t* linkTopo = &(pL->linkTopologies[j]);
			cJSON* lTopoElemObj;
			cJSON_AddItemToArray(linkTopoArray, lTopoElemObj = cJSON_CreateObject());
			cJSON_AddItemToObject(lTopoElemObj, "topology_uuid", cJSON_CreateString(linkTopo->topologyId));
		}
	}
	return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Compose the JSON Body of the succesfully network connectivity service
 * 	
 * 	@param body
 *  @param length
 *  @param compRouteOutputList
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void rapi_response_json_contents (char *body, gint *length, struct compRouteOutputList_t *compRouteOutputList)
{
    char *buftmp;    
    cJSON *root = cJSON_CreateObject();     
	DEBUG_PC ("Creating the JSON body of the response"); 

	// Create response-list array
	cJSON *responseListArray = cJSON_CreateArray();
	cJSON_AddItemToObject(root, "response-list", responseListArray);
	
	// Add computed routes to the response-list
	for (gint i = 0; i < compRouteOutputList->numCompRouteConnList; i++) {
		struct compRouteOutput_t *oElem = &(compRouteOutputList->compRouteConnection[i]); // reference to output element from the list of computed routes
		cJSON *oElemObj;
		cJSON_AddItemToArray (responseListArray, oElemObj = cJSON_CreateObject());
		
		// Add the service Id Object
		cJSON* servIdObj;
		cJSON_AddItemToObject(oElemObj, "serviceId", servIdObj = cJSON_CreateObject());
		cJSON_AddItemToObject(servIdObj, "contextId", cJSON_CreateString(oElem->serviceId.contextId));
		cJSON_AddItemToObject(servIdObj, "service_uuid", cJSON_CreateString(oElem->serviceId.service_uuid));

		// Add the service endpoints ids array
		cJSON* sEndpointIdsArray = cJSON_CreateArray();
		cJSON_AddItemToObject(oElemObj, "service_endpoints_ids", sEndpointIdsArray);

		for (gint j = 0; j < oElem->num_service_endpoints_id; j++) {
			//DEBUG_PC("parsing service endpoints ids");
			//DEBUG_PC("endpoint: %s [%s]", oElem->service_endpoints_id[j].device_uuid, oElem->service_endpoints_id[j].endpoint_uuid);
			//struct service_endpoints_id_t* sEndPointId = &(oElem->service_endpoints_id[j]);
			cJSON* sEndPointIdObj;
			cJSON_AddItemToArray(sEndpointIdsArray, sEndPointIdObj = cJSON_CreateObject());
			// Add the topology Id Object containing the topology uuid and context uuid
			cJSON* topIdObj;
			cJSON_AddItemToObject(sEndPointIdObj, "topology_id", topIdObj = cJSON_CreateObject());
			cJSON_AddItemToObject(topIdObj, "contextId", cJSON_CreateString(oElem->service_endpoints_id[j].topology_id.contextId));
			cJSON_AddItemToObject(topIdObj, "topology_uuid", cJSON_CreateString(oElem->service_endpoints_id[j].topology_id.topology_uuid));

			// Add the device Id (uuid) 
			cJSON_AddItemToObject(sEndPointIdObj, "device_id", cJSON_CreateString(oElem->service_endpoints_id[j].device_uuid));
			// Add the endpoint Id (uuid)
			cJSON_AddItemToObject(sEndPointIdObj, "endpoint_uuid", cJSON_CreateString(oElem->service_endpoints_id[j].endpoint_uuid));
		}
		// Add no path issue
		if (oElem->noPathIssue == NO_PATH_CONS_ISSUE) { // Error on finding the route, e.g., no feasible path
			DEBUG_PC("NO PATH FOUND, AN ISSUE OCCURRED: %d", oElem->noPathIssue);
			cJSON* noPathObj;
			cJSON_AddItemToObject(oElemObj, "noPath", noPathObj = cJSON_CreateObject());
			char str[5];
			sprintf(str, "%d", oElem->noPathIssue);
			cJSON_AddItemToObject(noPathObj, "issue", cJSON_CreateString(str));
			continue;
		}
		
		// Create the array to parse the computed path from the oElemObj
		cJSON* pathArray = cJSON_CreateArray();
		cJSON_AddItemToObject(oElemObj, "path", pathArray);
		for (gint k = 0; k < oElem->numPaths; k++) {
			struct path_t* p = &(oElem->paths[k]);
			cJSON* pathObj;
			cJSON_AddItemToArray(pathArray, pathObj = cJSON_CreateObject());

			// Add path capacity
			cJSON* pathCapObj;
			cJSON_AddItemToObject(pathObj, "path-capacity", pathCapObj = cJSON_CreateObject());
			cJSON* totalSizeObj;
			cJSON_AddItemToObject(pathCapObj, "total-size", totalSizeObj = cJSON_CreateObject());
			cJSON_AddItemToObject(totalSizeObj, "value", cJSON_CreateNumber(p->path_capacity.value));
			cJSON_AddItemToObject(totalSizeObj, "unit", cJSON_CreateNumber(p->path_capacity.unit));

			// Add path latency
			cJSON* pathLatObj;
			char lat[16];
			sprintf(lat, "%lf", p->path_latency.fixed_latency);
			cJSON_AddItemToObject(pathObj, "path-latency", pathLatObj= cJSON_CreateObject());
			cJSON_AddItemToObject(pathLatObj, "fixed-latency-characteristic", cJSON_CreateString(lat));

			// Add path cost
			cJSON* pathCostObj;
			cJSON_AddItemToObject(pathObj, "path-cost", pathCostObj = cJSON_CreateObject());
			cJSON_AddItemToObject(pathCostObj, "cost-name", cJSON_CreateString(p->path_cost.cost_name));
			char value[16];
			sprintf(value, "%lf", p->path_cost.cost_value);
			cJSON_AddItemToObject(pathCostObj, "cost-value", cJSON_CreateString(value));
			char algorithm[16];
			sprintf(algorithm, "%lf", p->path_cost.cost_algorithm);
			cJSON_AddItemToObject(pathCostObj, "cost-algorithm", cJSON_CreateString(algorithm));

			// Add the links
			//add_comp_path_link_json(pathObj, p);
			// Add deviceId, endpointId
			add_comp_path_deviceId_endpointId_json(pathObj, p, oElem);
		}	
	}
	
    //DEBUG_PC ("JSON Body Response DONE");	
    buftmp = (char *)cJSON_Print(root);
    strcat (body, (const char*) buftmp);
    *length = strlen ((const char*)body);    
    //DEBUG_PC ("JSON Body (length: %d)", *length);
    //DEBUG_PC ("%s", body);    
	cJSON_Delete (root);
	g_free(buftmp);
    return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to return response OK via REST API with the computed serviceId
 * 	
 * 	@param source
 *  @param httpCode
 *  @param compRouteOutputList
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void rapi_response_ok (GIOChannel *source, gint httpCode, struct compRouteOutputList_t *compRouteOutputList) {
	gint ret = 0;
    
    //DEBUG_PC ("Creating the JSON Body and sending the response of the computed Route List");
    
    char buftmp[1024];
    char *buf = g_malloc0 (sizeof (char) * 2048000); 
    // Create the Body of the Response 
    char * msgBody = g_malloc0 (sizeof (char) * 2048000);    
	
	//  If path computation was requested, the resulting computation is returned in the msg body
	if (compRouteOutputList != NULL) {
		rapi_response_json_contents(msgBody, &length, compRouteOutputList);
	}
	// no path computation was requested, then a basic msg is just added
	else {
		cJSON* root = cJSON_CreateObject();
		char status[3];
		sprintf(status, "OK");
		cJSON_AddItemToObject(root, "Status", cJSON_CreateString(status));
		msgBody = (char*)cJSON_Print(root);
		length = strlen((const char*)msgBody);
		cJSON_Delete(root);
	}
		
	sprintf((char *)buf, "HTTP/1.1 200 OK\r\n");    
	
    sprintf((char *)buftmp, SERVER_STRING);
    strcat ((char *)buf, (const char *)buftmp);    
  
    sprintf ((char *)buftmp, "Content-Type: application/json\r\n");
    strcat ((char *)buf, (const char *)buftmp);    
    
    // Add the length of the JSON enconding to the Content_Length
    char buff_length[16];    
    sprintf(buff_length, "%d", length);
    
    sprintf ((char *)buftmp, "Content-Length: ");
    strcat ((char *)buftmp, (const char *)buff_length);
    strcat ((char *)buftmp, "\r\n");
    strcat ((char *)buf, (const char *)buftmp);    
    
    // Add DATE header
    rapi_add_date_header ((char *)buftmp);
    strcat ((char *)buf, (const char *)buftmp);     
    sprintf((char *)buftmp, "\r\n");
    strcat ((char *)buf, (const char *)buftmp);

	strcat((char*)buf, (const char*)msgBody);
			
	//DEBUG_PC ("%s", buf);	    
    ret = rapi_send_message (source, buf);    
    g_free (buf);
    memset (buftmp, '\0', sizeof ( buftmp));    
    g_free (msgBody);
    (void)ret;    
    return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to return response OK via REST API
 * 	
 * 	@param source
 *  @param error
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
martinezric's avatar
martinezric committed
void rapi_response (GIOChannel *source, gint error) {
	 int ret = 0;	
	 guchar buftmp[1024];
	 char * buf = g_malloc0 (sizeof (char) * 2048000);
	 if (error == HTTP_RETURN_CODE_BAD_REQUEST)
		sprintf((char *)buf, "HTTP/1.1 400 BAD REQUEST\r\n");
	 else if (error == HTTP_RETURN_CODE_UNAUTHORIZED)
		sprintf((char *)buf, "HTTP/1.1 401 UNAUTHORIZED\r\n");
	 else if (error == HTTP_RETURN_CODE_FORBIDDEN)
		sprintf((char *)buf, "HTTP/1.1 403 FORBIDDEN\r\n");    
	 else if (error == HTTP_RETURN_CODE_NOT_FOUND)
		sprintf((char *)buf, "HTTP/1.1 404 NOT FOUND\r\n");
	 
	 sprintf((char *)buftmp, SERVER_STRING);
	 strcat ((char *)buf, (const char *)buftmp);    
	
	 sprintf((char *)buftmp, "Content-Type: text/plain\r\n");
	 strcat ((char *)buf, (const char *)buftmp);    
	 
	 sprintf((char *)buftmp, "Content-Length: 0/plain\r\n");
	 strcat ((char *)buf, (const char *)buftmp);    
	 
	 // Add DATE header
	 rapi_add_date_header ((char *)buftmp);
	 strcat ((char *)buf, (const char *)buftmp);     
	     
	 sprintf((char *)buftmp, "\r\n");
	 strcat ((char *)buf, (const char *)buftmp);
	 // Print the prepared message
	 DEBUG_PC ("%s", buf);
	 
	 // Send the message
	 ret = rapi_send_message (source, buf);	 
	 g_free (buf);	 
	 (void)ret;
	
	 return;
}

martinezric's avatar
martinezric committed
///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief parsing topology Identifier Object (contains Context Id and Toplogy UUID) JSON object
 *
 * 	@param obj
 *  @param topology_id
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_topology_Id(cJSON* obj, struct topology_id_t* topology_id) {
	g_assert(topology_id);
	// Get the context Id (UUID) from the topologyIdObj
	cJSON* contextIdObj = cJSON_GetObjectItem(obj, "contextId");
	if (cJSON_IsString(contextIdObj)) {
		duplicate_string(topology_id->contextId, contextIdObj->valuestring);
	}
	// Get the topologyId (UUID) from the topologyIdObj
	cJSON* topologyUuidObj = cJSON_GetObjectItem(obj, "topology_uuid");
	if (cJSON_IsString(topologyUuidObj)) {
		duplicate_string(topology_id->topology_uuid, topologyUuidObj->valuestring);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief parsing EndpointIds JSON object
 *
 * 	@param item
 *  @param serviceEndPointId
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_endPointsIds(cJSON* item, struct service_endpoints_id_t* serviceEndPointId) {
	// Get the topology Id Object
	cJSON* topologyIdObj = cJSON_GetObjectItem(item, "topology_id");
	if (cJSON_IsObject(topologyIdObj)) {
		parse_topology_Id(topologyIdObj, &serviceEndPointId->topology_id);
	}
	// Get the deviceId (UUID)
	cJSON* deviceIdObj = cJSON_GetObjectItem(item, "device_id");
	if (cJSON_IsString(deviceIdObj)) {
		duplicate_string(serviceEndPointId->device_uuid, deviceIdObj->valuestring);
		DEBUG_PC("DeviceId: %s", serviceEndPointId->device_uuid);
	}
	// Get the endpointId (UUID)
	cJSON* endPointIdObj = cJSON_GetObjectItem(item, "endpoint_uuid");
	if (cJSON_IsString(endPointIdObj)) {
		duplicate_string(serviceEndPointId->endpoint_uuid, endPointIdObj->valuestring);
		DEBUG_PC("EndPointId: %s", serviceEndPointId->endpoint_uuid);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the array of Endpoint Ids	of the active services
 *
 * 	@param endPointArray
 *  @param actServ
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_act_service_endPointsIds_array(cJSON* endPointIdArray, struct activeService_t* actServ) {
	g_assert(actServ);

	for (gint i = 0; i < cJSON_GetArraySize(endPointIdArray); i++) {
		actServ->num_service_endpoints_id++;
		struct service_endpoints_id_t* serviceEndPointId = &(actServ->service_endpoints_id[i]);
		cJSON* item = cJSON_GetArrayItem(endPointIdArray, i);
		parse_endPointsIds(item, serviceEndPointId);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
martinezric's avatar
martinezric committed
 * 	@brief Function used to parse the array of Endpoint Ids	of the requested services
 *
 * 	@param endPointArray
 *  @param s
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_service_endPointsIds_array(cJSON* endPointIdArray, struct service_t* s) {

	for (gint i = 0; i < cJSON_GetArraySize(endPointIdArray); i++) {
		s->num_service_endpoints_id++;
		struct service_endpoints_id_t* serviceEndPointId = &(s->service_endpoints_id[i]);

		cJSON* item = cJSON_GetArrayItem(endPointIdArray, i);
martinezric's avatar
martinezric committed
		parse_endPointsIds(item, serviceEndPointId);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the array with the required service constraints
 *
 * 	@param constraintArray
 *  @param s
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_service_constraints(cJSON* constraintArray, struct service_t* s) {
	for (gint i = 0; i < cJSON_GetArraySize(constraintArray); i++) {
		s->num_service_constraints++;
		struct constraint_t* constraint = &(s->constraints[i]);

		cJSON* item = cJSON_GetArrayItem(constraintArray, i);

		// Get the constraint type
		cJSON* typeObj = cJSON_GetObjectItem(item, "constraint_type");
		if (cJSON_IsString(typeObj)) {
			duplicate_string(constraint->constraint_type, typeObj->valuestring);
		}
		// Get the constraint value
		cJSON* valueObj = cJSON_GetObjectItem(item, "constraint_value");
		if (cJSON_IsString(valueObj)) {
			 duplicate_string(constraint->constraint_value, valueObj->valuestring);
		} 
		DEBUG_PC("Service Reqs [%d] -- Type: %s | Value: %s", i+1, constraint->constraint_type, constraint->constraint_value);
	}
	return;
}

martinezric's avatar
martinezric committed
///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the serviceId information from a JSON obj
 *
 * 	@param obj
 *  @param serviceId
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_json_serviceId(cJSON* obj, struct serviceId_t* serviceId) {
	g_assert(obj);
	g_assert(serviceId);

	// Get context Id uuid
	cJSON* contextIdObj = cJSON_GetObjectItem(obj, "contextId");
	if (cJSON_IsString(contextIdObj)) {
		// convert the string in contextId->valuestring in uuid binary format
		duplicate_string(serviceId->contextId, contextIdObj->valuestring);
		DEBUG_PC("ContextId: %s (uuid string format)", serviceId->contextId);
	}
	// Get service Id uuid
	cJSON* serviceUuidObj = cJSON_GetObjectItem(obj, "service_uuid");
	if (cJSON_IsString(serviceUuidObj)) {
		duplicate_string(serviceId->service_uuid, serviceUuidObj->valuestring);
		DEBUG_PC("Service UUID: %s (uuid string format)", serviceId->service_uuid);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the array with the different
 * network services
 *
 * 	@param serviceArray
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parsing_json_serviceList_array(cJSON* serviceArray) {

	for (gint i = 0; i < cJSON_GetArraySize(serviceArray); i++)
	{
martinezric's avatar
martinezric committed
		struct service_t* service = g_malloc0(sizeof(struct service_t));
		if (service == NULL) {
			DEBUG_PC("Memory allocation error ...");
			exit(-1);
		}
		cJSON* item = cJSON_GetArrayItem(serviceArray, i);
		// Get the algorithm Id
		cJSON* algIdItem = cJSON_GetObjectItem(item, "algId");
martinezric's avatar
martinezric committed
		if (cJSON_IsString(algIdItem)) {
			duplicate_string(service->algId, algIdItem->valuestring);
			DEBUG_PC ("algId: %s", service->algId);
			// assumed that all the services request the same algId
			duplicate_string(algId, service->algId);
		}

		// Get the syncPaths
		cJSON* synchPathObj = cJSON_GetObjectItemCaseSensitive(item, "syncPaths");
martinezric's avatar
martinezric committed
		if (cJSON_IsBool(synchPathObj)) {
			// Check Synchronization of multiple Paths to attain e.g. global concurrent optimization
martinezric's avatar
martinezric committed
			if (cJSON_IsTrue(synchPathObj)) {
				syncPath = TRUE;
				DEBUG_PC("Path Synchronization is required");
			}
martinezric's avatar
martinezric committed
			if (cJSON_IsFalse(synchPathObj)) {
				syncPath = FALSE;
				DEBUG_PC("No Path Synchronization");
			}
		}

		// Get service Id in terms of contextId and service uuids
		cJSON* serviceIdObj = cJSON_GetObjectItem(item, "serviceId");
		if (cJSON_IsObject(serviceIdObj)) {
martinezric's avatar
martinezric committed
			parse_json_serviceId(serviceIdObj, &service->serviceId);
		}		

		// Get de service type
		cJSON* serviceTypeObj = cJSON_GetObjectItem(item, "serviceType");
		if (cJSON_IsNumber(serviceTypeObj))
		{
			service->service_type = (guint)(serviceTypeObj->valuedouble);
			print_service_type(service->service_type);
		}

		// Get the endPoints array of the service
		cJSON* endPointIdsArray = cJSON_GetObjectItem(item, "service_endpoints_ids");
		if (cJSON_IsArray(endPointIdsArray)) {
			parse_service_endPointsIds_array(endPointIdsArray, service);
		}	

		// Get the service constraints
		cJSON* constraintArray = cJSON_GetObjectItem(item, "service_constraints");
		if (cJSON_IsArray(constraintArray)) {
			parse_service_constraints(constraintArray, service);
		}		

martinezric's avatar
martinezric committed
		// Get the maximum number of paths to be computed (kPaths) inspected/explored
		cJSON* kPathsInspObj = cJSON_GetObjectItemCaseSensitive(item, "kPaths_inspection");
		if (cJSON_IsNumber(kPathsInspObj)){
			service->kPaths_inspected = (guint)(kPathsInspObj->valuedouble);
martinezric's avatar
martinezric committed

martinezric's avatar
martinezric committed
		// Get the maximum number of paths to be computed (kPaths) returned
		cJSON* kPathsRetpObj = cJSON_GetObjectItemCaseSensitive(item, "kPaths_return");
		if (cJSON_IsNumber(kPathsRetpObj)){
			service->kPaths_returned = (guint)(kPathsRetpObj->valuedouble);
		}	
		
martinezric's avatar
martinezric committed
		// Append the requested service to the serviceList
		serviceList = g_list_append(serviceList, service);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function parsing the capacity attributes in the endpoint
 *
 * 	@param capacity
 *  @param c
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_capacity_object(cJSON* capacity, struct capacity_t* c) {
	cJSON* totalSizeObj = cJSON_GetObjectItem(capacity, "total-size");
	if (cJSON_IsObject(totalSizeObj)) {
		//Get the capacity value
		cJSON* valueObj = cJSON_GetObjectItem(totalSizeObj, "value");
		if (cJSON_IsNumber(valueObj)) {
			memcpy(&c->value, &valueObj->valuedouble, sizeof (gdouble));
		}
		// Get the Units
		cJSON* unitObj = cJSON_GetObjectItem(totalSizeObj, "unit");
		if (cJSON_IsNumber(unitObj)) {
			c->unit = (guint)(unitObj->valuedouble);
		}	
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function parsing the device endpoints
 *
 * 	@param endPointsArray
 *  @param d
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_json_device_endpoints_array(cJSON* endPointsArray, struct device_t* d) {
	for (gint i = 0; i < cJSON_GetArraySize(endPointsArray); i++) {
		d->numEndPoints++;
		if (d->numEndPoints >= MAX_DEV_ENDPOINT_LENGTH) {
			DEBUG_PC("d->numEndPoints(%d) exceeded MAX_DEV_ENDPOINT_LENGTH(%d)", d->numEndPoints, MAX_DEV_ENDPOINT_LENGTH);
		}
		struct endPoint_t* endpoint = &(d->endPoints[i]);

		cJSON* item = cJSON_GetArrayItem(endPointsArray, i);

		// Get the Endpoint Identifier: topology, context, device and endpointId
		cJSON* endPointIdObj = cJSON_GetObjectItem(item, "endpoint_id");
		if (cJSON_IsObject(endPointIdObj)) {
			// Get the topology Id Object
			cJSON* topologyIdObj = cJSON_GetObjectItem(endPointIdObj, "topology_id");
			if (cJSON_IsObject(topologyIdObj)) {
martinezric's avatar
martinezric committed
				parse_topology_Id(topologyIdObj, &endpoint->endPointId.topology_id);
			}
			// Get the deviceId
			cJSON* deviceIdObj = cJSON_GetObjectItem(endPointIdObj, "device_id");
			if (cJSON_IsString(deviceIdObj)) {				
				duplicate_string(endpoint->endPointId.device_id, deviceIdObj->valuestring);
			}
			// Get the endpoint_uuid
			cJSON* endPointUuidObj = cJSON_GetObjectItem(endPointIdObj, "endpoint_uuid");
			if (cJSON_IsString(endPointUuidObj)) {				
				duplicate_string(endpoint->endPointId.endpoint_uuid, endPointUuidObj->valuestring);
			}
		}
		// Get the EndPoint Type
		cJSON* endPointTypeObj = cJSON_GetObjectItem(item, "endpoint_type");
		if (cJSON_IsString(endPointTypeObj)) {
			duplicate_string(endpoint->endpointType, endPointTypeObj->valuestring);
			//DEBUG_PC("Device Endpoint (%d) -- EndPoint Type: %s", i + 1, endpoint->endpointType);
		}
		// Link Port Direction
		cJSON* linkPortDirectionObj = cJSON_GetObjectItem(item, "link_port_direction");
		if (cJSON_IsNumber(linkPortDirectionObj)) {
			endpoint->link_port_direction = (guint)(linkPortDirectionObj->valuedouble);
			print_link_port_direction(endpoint->link_port_direction);
		}
		// EndPoint Termination Direction
		cJSON* terminationDirectionObj = cJSON_GetObjectItem(item, "termination-direction");
		if (cJSON_IsNumber(terminationDirectionObj)) {
			endpoint->termination_direction = (guint)(terminationDirectionObj->valuedouble);
			print_termination_direction(endpoint->termination_direction);
		}
		// Endpoint Termination State
		cJSON* terminationStateObj = cJSON_GetObjectItem(item, "termination-state");
		if (cJSON_IsNumber(terminationStateObj)) {
			endpoint->termination_state = (guint)(terminationStateObj->valuedouble);
			print_termination_state(endpoint->termination_state);
		}
		// total potential capacity
		cJSON* totalPotentialCapacityObj = cJSON_GetObjectItem(item, "total-potential-capacity");
		if (cJSON_IsObject(totalPotentialCapacityObj))
		{
			parse_capacity_object(totalPotentialCapacityObj, &endpoint->potential_capacity);
			//DEBUG_PC("Device Endpoint (%d) -- Potential Capacity: %f", i + 1, endpoint->potential_capacity.value);
			print_capacity_unit(endpoint->potential_capacity.unit);
		}
		// total available capacity
		cJSON* availableCapacityObj = cJSON_GetObjectItem(item, "available-capacity");
		if (cJSON_IsObject(availableCapacityObj))
		{
			parse_capacity_object(availableCapacityObj, &endpoint->available_capacity);
			//DEBUG_PC("Device Endpoint (%d) -- Available Capacity: %f", i + 1, endpoint->available_capacity.value);
			print_capacity_unit(endpoint->available_capacity.unit);
		}
		// inter-domain plug-in
		cJSON* interDomainPlugInObj = cJSON_GetObjectItem(item, "inter-domain-plug-in");
		if (cJSON_IsObject(interDomainPlugInObj)) {
			// Get the local
			cJSON* idInterDomainLocal = cJSON_GetObjectItem(interDomainPlugInObj, "plug-id-inter-domain-local-id");
			if (cJSON_IsString(idInterDomainLocal)) {
				duplicate_string(endpoint->inter_domain_plug_in.inter_domain_plug_in_local_id, idInterDomainLocal->valuestring);
				//DEBUG_PC("Inter-Domain Local Id: %s", endpoint->inter_domain_plug_in.inter_domain_plug_in_local_id);				
			}
			// Get the remote
			cJSON* idInterDomainRemote = cJSON_GetObjectItem(interDomainPlugInObj, "plug-id-inter-domain-remote-id");
			if (cJSON_IsString(idInterDomainRemote)) {
				duplicate_string(endpoint->inter_domain_plug_in.inter_domain_plug_in_remote_id, idInterDomainRemote->valuestring);
				//DEBUG_PC("Inter-Domain Remote Id: %s", endpoint->inter_domain_plug_in.inter_domain_plug_in_remote_id);
			}
		}
martinezric's avatar
martinezric committed

		// Energy consumption per endPoint port
		cJSON* energyPortObj = cJSON_GetObjectItem(item, "energy_consumption");
		if (cJSON_IsNumber(energyPortObj)) {
			memcpy(&endpoint->energyConsumption, &energyPortObj->valuedouble, sizeof(gdouble));
			DEBUG_PC("Endpoint Energy Consumption: %f", endpoint->energyConsumption);
		}

		// Endpoint Operational Status
		cJSON* operationalStatusObj = cJSON_GetObjectItem(item, "operational_status");
		if (cJSON_IsNumber(operationalStatusObj)) {
			endpoint->operational_status = (gint)(operationalStatusObj->valuedouble);
			DEBUG_PC("Endpoint Operational Status: %d", endpoint->operational_status);
		}
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the set/list of devices forming the context/topology
 *
 * 	@param deviceArray
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parsing_json_deviceList_array(cJSON* deviceArray) {
	DEBUG_PC("");
	DEBUG_PC("========= PARSING DEVICE LIST ============");
martinezric's avatar
martinezric committed
	for (gint i = 0; i < cJSON_GetArraySize(deviceArray); i++) {		
		struct device_t* d = g_malloc0(sizeof(struct device_t));
		if (d == NULL) {
			DEBUG_PC("Memory Allocation Failure");
			exit(-1);
		}
		cJSON* item = cJSON_GetArrayItem(deviceArray, i);

martinezric's avatar
martinezric committed
		// Get the power idle of the switch
		cJSON* powerIdleObj = cJSON_GetObjectItem(item, "power_idle");
		if (cJSON_IsNumber(powerIdleObj)) {