annotate_rspec.cc 20.9 KB
Newer Older
1
/*
Robert Ricci's avatar
Robert Ricci committed
2
 * Copyright (c) 2003-2010 University of Utah and the Flux Group.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * 
 * {{{EMULAB-LICENSE
 * 
 * This file is part of the Emulab network testbed software.
 * 
 * This file is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 * 
 * This file 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 Affero General Public
 * License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this file.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * }}}
22 23
 */

24
static const char rcsid[] = "$Id: annotate_rspec.cc,v 1.9 2009-10-21 20:49:26 tarunp Exp $";
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

#ifdef WITH_XML

#include "annotate.h"
#include "annotate_rspec.h"

#include <xercesc/util/PlatformUtils.hpp>

#include <xercesc/dom/DOM.hpp>

#include "xstr.h"
#include "xmlhelpers.h"

#include <iostream>
#include <utility>
#include <list>
#include <string>
				 
#define XMLDEBUG(x) (cerr << x);
				 
extern DOMDocument* doc;
extern DOMElement* request_root;
extern map<string, DOMElement*>* advertisement_elements;
				 
XERCES_CPP_NAMESPACE_USE

using namespace std;

annotate_rspec :: annotate_rspec ()
{
55
  this->document = doc;
56 57
  this->virtual_root = request_root;
  //  this->virtual_root = dynamic_cast<DOMElement*>(dynamic_cast<DOMNode*>(request_root)->cloneNode(true));
58 59 60 61 62
  this->physical_elements = advertisement_elements;
  
  vector<DOMElement*> lan_links 
    = getElementsHavingAttribute(this->virtual_root, "link", "is_lan");
  vector<DOMElement*>::iterator it;
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
  for (it = lan_links.begin(); it < lan_links.end(); it++) {
    DOMElement* lan_link = *it;
    // Removing annotations inserted earlier
    lan_link->removeAttribute(XStr("is_lan").x());
    string lan_link_id 
      = string(XStr(lan_link->getAttribute(XStr("virtual_id").x())).c());
    set<string> virtual_interface_ids;
    DOMNodeList* interfaces 
      = lan_link->getElementsByTagName(XStr("interface_ref").x());
    for (unsigned int j = 0; j < interfaces->getLength(); j++) {
      DOMElement* interface 
        = dynamic_cast<DOMElement*>(interfaces->item(j));
      virtual_interface_ids.insert
        (string(XStr(interface->getAttribute
                     (XStr("virtual_interface_id").x())).c()));
78
    }
79 80 81
    this->lan_links_map.insert
      (pair< string, set<string> >(lan_link_id, virtual_interface_ids));
  }
82 83
}

84
// Annotate a trivial link
85 86
void annotate_rspec::annotate_element (const char* v_name)
{
87 88 89 90 91 92
  DOMElement* vlink 
    = getElementByAttributeValue(this->virtual_root, 
				 "link", "virtual_id", v_name);
  annotate_interface (vlink, 0);
  annotate_interface (vlink, 1);	
  create_component_hop(vlink);
93
}
94

95 96 97
// This will get called when a node or a direct link needs to be annotated
void annotate_rspec::annotate_element (const char* v_name, const char* p_name)
{
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
  DOMElement* vnode 
    = getElementByAttributeValue(this->virtual_root, 
				 "node", "virtual_id", v_name);
  // If a vnode by that name was found, then go ahead. 
  // If not, that element should be a link
  // We are not terribly concerned about 
  // having to scan the entire physical topology twice
  // because direct links are never really going to happen
  if (vnode != NULL) {
    if (!vnode->hasAttribute(XStr("generated_by_assign").x())) {
      DOMElement* pnode = (this->physical_elements->find(p_name))->second;
      copy_component_spec(pnode, vnode);
    }
  }
  else {
    DOMElement* vlink 
      = getElementByAttributeValue(this->virtual_root, 
				   "link", "virtual_id", v_name);
    DOMElement* plink = (this->physical_elements->find(p_name))->second;
    
    // If plink is NULL, then it must be a trivial link
    if (plink == NULL) {
      
    }
    annotate_interface (plink, vlink, 0);
    annotate_interface (plink, vlink, 1);
    
    create_component_hop(plink, vlink, BOTH, NULL);
    
    if (vlink->hasAttribute(XStr("generated_by_assign").x())) {
      string str_lan_link 
129
        = string(XStr(vlink->getAttribute(XStr("lan_link").x())).c());
130
      DOMElement* lan_link 
131 132 133
        = getElementByAttributeValue(this->virtual_root, "link", 
                                     "virtual_id", 
                                     str_lan_link.c_str());
134
      DOMNodeList* component_hops 
135
        = vlink->getElementsByTagName(XStr("component_hop").x());
136
      for (unsigned int i = 0; i < component_hops->getLength(); i++) {
137 138 139
        DOMElement* component_hop 
          = dynamic_cast<DOMElement*>(component_hops->item(i));
        copy_component_hop(lan_link, component_hop);
140 141 142
      }
    }
  }
143 144 145
}

// This is called when an intraswitch or interswitch link has to be annotated
146
void annotate_rspec::annotate_element (const char* v_name, 
147
				       list<const char*>* links)
148
{
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
  // These are the paths from the source to the first switch
  // and from the last switch to the destination
  const char* psrc_name = links->front();	
  const char* pdst_name = links->back();	
  DOMElement* p_src_switch_link 
    = (this->physical_elements->find(psrc_name))->second;
  DOMElement* p_switch_dst_link 
    = (this->physical_elements->find(pdst_name))->second;
  
  // Remove these links from the list
  // If it is an intra-switch link, the list should now be empty.
  links->pop_front();
  links->pop_back();
  
  DOMElement* vlink = getElementByAttributeValue (this->virtual_root, "link", 
						  "virtual_id", v_name);
  annotate_interface(p_src_switch_link, vlink, 0);
  annotate_interface(p_switch_dst_link, vlink, 1);
  
  DOMElement* prev_component_hop 
    = create_component_hop (p_src_switch_link, vlink, SOURCE, NULL);
170
#if 0
171 172 173 174 175 176
  for (DOMElement *prev_link_in_path = p_src_switch_link; !links->empty(); )
    {
      DOMElement* p_switch_switch_link = find_next_link_in_path (prev_link_in_path, links);
      prev_component_hop = create_component_hop (p_switch_switch_link, vlink, NEITHER, prev_component_hop);
      prev_link_in_path = p_switch_switch_link;
    }
177
#else
178 179 180 181 182 183 184
  {
    static int gave_apology;
    
    if( !links->empty() && !gave_apology ) {
      gave_apology = 1;
      cerr << "Warning: unable to locate interfaces on "
	"switch/switch links; omitting those\n";
185
	    }
186
  }
187
#endif
188 189
  create_component_hop 
    (p_switch_dst_link, vlink, DESTINATION, prev_component_hop);
190 191
}

192 193 194 195
// Creates a component_hop for a trivial link
// Adds the hop to the vlink and returns the hop element that was created
DOMElement* annotate_rspec::create_component_hop (DOMElement* vlink)
{
196
  DOMNodeList* interfaces 
197
			= vlink->getElementsByTagName(XStr("interface_ref").x());
198 199 200
  DOMElement* src_iface = dynamic_cast<DOMElement*>(interfaces->item(0));
  DOMElement* dst_iface = dynamic_cast<DOMElement*>(interfaces->item(1));
  
201 202
  string src_id=XStr(src_iface->getAttribute(XStr("virtual_node_id").x())).c();
  string dst_id=XStr(dst_iface->getAttribute(XStr("virtual_node_id").x())).c();
203 204 205
  
  DOMElement* src_vnode 
    = getElementByAttributeValue(this->virtual_root,
206
                                 "node", "virtual_id", src_id.c_str());
207 208
  DOMElement* dst_vnode 
    = getElementByAttributeValue(this->virtual_root,
209
                                 "node", "virtual_id", dst_id.c_str());
210
  
211 212
  string src_component_id = XStr(find_urn(src_vnode, "component")).c();
  string dst_component_id = XStr(find_urn(dst_vnode, "component")).c();
213 214 215 216
  
  DOMElement* component_hop = doc->createElement(XStr("component_hop").x());
  
  DOMElement* src_iface_clone 
217 218 219
    = dynamic_cast<DOMElement*>(doc->importNode
                                (dynamic_cast<DOMNode*>(src_iface),true));
  
220
  src_iface_clone->setAttribute(XStr("component_node_id").x(),
221
                                XStr(src_component_id).x());
222
  src_iface_clone->setAttribute(XStr("component_interface_id").x(),
223
                                XStr("loopback").x()); 
224 225 226 227 228
  
  DOMElement* dst_iface_clone 
    = dynamic_cast<DOMElement*>
    (doc->importNode(dynamic_cast<DOMNode*>(dst_iface),true));
  dst_iface_clone->setAttribute(XStr("component_node_id").x(),
229
                                XStr(dst_component_id).x());
230
  dst_iface_clone->setAttribute(XStr("component_interface_id").x(),
231
                                XStr("loopback").x());
232 233 234 235 236
  
  component_hop->appendChild(src_iface_clone);
  component_hop->appendChild(dst_iface_clone);
  vlink->appendChild(component_hop);
  return component_hop;
237 238 239 240
}

// Creates a hop from a switch till the next end point. 
// Adds the hop to the vlink and returns the hop element that was created
241 242 243 244
DOMElement* annotate_rspec::create_component_hop (const DOMElement* plink, 
						  DOMElement* vlink, 
						  int endpoint_interface, 
						  const DOMElement* prev_component_hop)
245
{
246 247 248 249 250 251 252 253 254 255 256 257 258 259
  // Create a single_hop_link element
  DOMElement* component_hop = doc->createElement(XStr("component_hop").x());
  copy_component_spec(plink, component_hop);
  
  // We assume the first interface is the source and the second the dest
  DOMNodeList* pinterfaces 
    = plink->getElementsByTagName(XStr("interface_ref").x());
  DOMElement* plink_src_iface 
    = dynamic_cast<DOMElement*>(pinterfaces->item(0));
  DOMElement* plink_dst_iface 
    = dynamic_cast<DOMElement*>(pinterfaces->item(1));
  
  DOMNodeList* vinterfaces 
    = vlink->getElementsByTagName(XStr("interface_ref").x());
260 261
  DOMElement* vlink_src_iface= dynamic_cast<DOMElement*>(vinterfaces->item(0));
  DOMElement* vlink_dst_iface= dynamic_cast<DOMElement*>(vinterfaces->item(1));
262 263 264 265 266 267 268 269 270
  
  // If the previous component hop is not specified (NULL),
  // then the link is either direct 
  // or the direction is guaranteed to be from the node to the switch
  DOMElement* plink_src_iface_clone 
    = dynamic_cast<DOMElement*>
    (doc->importNode(dynamic_cast<DOMNode*>(plink_src_iface),true));
  DOMElement* plink_dst_iface_clone 
    = dynamic_cast<DOMElement*>
271
				(doc->importNode(dynamic_cast<DOMNode*>(plink_dst_iface),true));
272 273 274 275 276
  // If the previous component is specified,
  // the link specification could be the opposite of what we need
  if (prev_component_hop != NULL) {
      // Find the destination of the previous component hop
    DOMElement* prev_hop_dst_iface 
277 278 279 280
      = dynamic_cast<DOMElement*>((prev_component_hop->getElementsByTagName
                                   (XStr("interface_ref").x()))->item(1));
    string prev_hop_dst_uuid 
      = XStr(find_urn(prev_hop_dst_iface, "component_node")).c();
281 282 283 284 285 286 287
    
    // We need to do this because in advertisements, 
    // all links are from nodes to switches
    // and we need to reverse this for the last hop of a multi-hop path
    // This is slightly more expensive, 
    // but definitely more robust than checking based on 
    // whether a destination interface was specified
288 289
    if (prev_hop_dst_uuid == string(XStr(find_urn(plink_dst_iface, 
                                                  "component_node")).c())) {
290
      plink_src_iface_clone 
291 292
        = dynamic_cast<DOMElement*>(doc->importNode(dynamic_cast<DOMNode*>
                                                    (plink_dst_iface), true));
293
      plink_dst_iface_clone 
294 295
        = dynamic_cast<DOMElement*>(doc->importNode(dynamic_cast<DOMNode*>
                                                    (plink_src_iface), true));
296 297 298 299 300 301 302 303
    }
  }
  
  // If the source interface is an end point
  if (endpoint_interface == SOURCE || endpoint_interface == BOTH)
    set_interface_as_link_endpoint
      (plink_src_iface_clone, 
       XStr(vlink_src_iface->getAttribute
304
            (XStr("virtual_node_id").x())).c(), 
305
       XStr(vlink_src_iface->getAttribute
306
            (XStr("virtual_interface_id").x())).c());
307 308 309 310 311 312
  
  // If the destination interface is an end point
  if (endpoint_interface == DESTINATION || endpoint_interface == BOTH)
    set_interface_as_link_endpoint
      (plink_dst_iface_clone, 
       XStr(vlink_dst_iface->getAttribute
313
            (XStr("virtual_node_id").x())).c(),
314
       XStr(vlink_dst_iface->getAttribute
315
            (XStr("virtual_interface_id").x())).c());
316 317 318 319 320 321 322
  
  // Add interface specifications to the link in the single hop element
  component_hop->appendChild(plink_src_iface_clone);
  component_hop->appendChild(plink_dst_iface_clone);
  
  vlink->appendChild(component_hop);
  return (component_hop);
323 324
}

325
// Copies the component_hop from the generated_link to the requsted_link
326 327 328 329
// Also strips the unnecessary annotations from the component_hop after copying
// The "unnecessary annotations" 
// specify the end point of the link to be the auto-generated lan node
// We could just assume that the second interface_ref in the hop contains these
330 331 332 333
// "unnecessary annotations" since we have generated it in the first place
// and we can sure that it will always be in exactly that same position,
// but it sounds like a dirty way of doing it and is not exactly robust,
// so we shall use the slower, but more robust method
334 335
void annotate_rspec::copy_component_hop(DOMElement* lan_link, 
										DOMElement* component_hop)
336
{
337 338 339 340
	string lan_link_id 
			= string(XStr(lan_link->getAttribute(XStr("virtual_id").x())).c());
	DOMNodeList* interfaces 
			= component_hop->getElementsByTagName(XStr("interface_ref").x());
341
	for (unsigned int i = 0; i < interfaces->getLength(); i++)
342 343 344 345
	{
		DOMElement* interface = dynamic_cast<DOMElement*>(interfaces->item(i));
		if (interface->hasAttribute(XStr("virtual_interface_id").x()))
		{
346 347 348
			string str_virtual_interface_id 
					= string(XStr(interface->getAttribute
									(XStr("virtual_interface_id").x())).c());
349 350 351 352 353 354 355
			if (!has_interface_with_id (lan_link_id, str_virtual_interface_id))
			{
				interface->removeAttribute(XStr("virtual_interface_id").x());
				interface->removeAttribute(XStr("virtual_node_id").x());
			}	
		}
	}
356 357
	lan_link->appendChild(dynamic_cast<DOMElement*>
									(doc->importNode(component_hop, true)));
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
}

// Checks if the link contains an interface with virtual_interface_id = id
bool annotate_rspec::has_interface_with_id (string link_virtual_id, string id)
{
	map< string, set<string> >::iterator map_it;
	set<string>::iterator set_it;
	map_it = lan_links_map.find(link_virtual_id);
	if (map_it == this->lan_links_map.end())
		return false;
	set<string> virtual_ids = map_it->second;
	set_it = virtual_ids.find(id);
	if (set_it == virtual_ids.end())
		return false;
	return true;
}

375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
// Annotates the interfaces on a node making up the end points of a trivial link
void annotate_rspec::annotate_interface (const DOMElement* vlink, 
										 int interface_number)
{
	this->annotate_interface (NULL, vlink, interface_number, true);
}

// Annotates the interfaces on a non-trivial link
void annotate_rspec::annotate_interface (const DOMElement* plink,
										 const DOMElement* vlink,
										 int interface_number)
{
	this->annotate_interface (plink, vlink, interface_number, false);
}

390
// Updates the node which is the end point of the link
391 392 393 394 395 396 397 398 399 400 401 402 403 404
// 1) Find the interface_ref from the list of interface_refs on the link 
//    and it's virtual_interface_id
// 2) Find the virtual node to which the interface_ref is referring 
//    and its corresponding interface
// 3) Use this to find the physical node (component_node_uuid/urn) to which 
//    the virtual node has been mapped
// 4) Find the corresponding interface_ref on the physical link to which 
//    the virtual link has been mapped and get its component_interface_id
// 5) Annotate the interface on the virtual node obtained in 2) 
//    with the interface_id obtained in 4)
void annotate_rspec::annotate_interface (const DOMElement* plink, 
										 const DOMElement* vlink, 
										 int interface_number,
										 bool is_trivial_link)
405
{
406 407 408 409
	DOMNodeList* vinterfaces 
					= vlink->getElementsByTagName(XStr("interface_ref").x());
	DOMElement* vlink_iface 
			= dynamic_cast<DOMElement*>(vinterfaces->item(interface_number));
410 411
	string vlink_iface_virtual_interface_id 
    = XStr(vlink_iface->getAttribute(XStr("virtual_interface_id").x())).c();
412
			
413
	// Get the virtual_id of the node to which the interface belongs
414 415
	string vlink_iface_virtual_node_id 
    = XStr(vlink_iface->getAttribute(XStr("virtual_node_id").x())).c();
416 417
	DOMElement* vnode 
			= getElementByAttributeValue(this->virtual_root, 
418 419 420
                                   "node", 
                                   "virtual_id", 
                                   vlink_iface_virtual_node_id.c_str());
421 422
	DOMElement* vnode_iface_decl 
			= getElementByAttributeValue(vnode, "interface", "virtual_id", 
423 424 425
                                   vlink_iface_virtual_interface_id.c_str());

	string component_node_uuid = XStr(find_urn(vnode, "component")).c();
426
	
427
	if (!is_trivial_link) 
428
	{
429
		DOMElement* p_iface 
430 431 432
      = getElementByAttributeValue(plink, "interface_ref", 
                                   "component_node_uuid", 
                                   component_node_uuid.c_str());
433 434 435
		if (p_iface == NULL)
		{
			p_iface = getElementByAttributeValue(plink, "interface_ref", 
436 437
                                           "component_node_urn", 
                                           component_node_uuid.c_str());
438
		}
439

440 441 442 443
		string component_interface_id 
      = XStr(p_iface->getAttribute(XStr("component_interface_id").x())).c();
		vnode_iface_decl->setAttribute (XStr("component_id").x(), 
                                    XStr(component_interface_id).x());
444 445 446
	}
	else 
	{
447 448
		vnode_iface_decl->setAttribute (XStr("component_id").x(), 
                                    XStr("loopback").x());
449
	}
450 451 452 453 454 455
}

// Copies the component spec from the source to the destination
void annotate_rspec::copy_component_spec(const DOMElement* src, DOMElement* dst)
{
	if (src->hasAttribute (XStr("component_name").x()))
456
		dst->setAttribute (XStr("component_name").x(), 
457 458
                       XStr(src->getAttribute
                            (XStr("component_name").x())).x());
459
	dst->setAttribute (XStr("component_uuid").x(), 
460
                     XStr(src->getAttribute(XStr("component_uuid").x())).x());
461
	dst->setAttribute (XStr("component_manager_uuid").x(), 
462 463
                     XStr(src->getAttribute
                          (XStr("component_manager_uuid").x())).x());
464 465
}

466 467
// If the interface belongs to an end point of the link, 
// and additional virtual_id attribute has to be added to it
468 469 470 471
void 
annotate_rspec::set_interface_as_link_endpoint (DOMElement* interface, 
                                                const char* virtual_node_id, 
                                                const char* virtual_iface_id)
472
{
473
	interface->setAttribute(XStr("virtual_node_id").x(), 
474
                          XStr(virtual_node_id).x());
475
	interface->setAttribute(XStr("virtual_interface_id").x(), 
476
                          XStr(virtual_iface_id).x());
477 478 479
}

// Finds the next link in the path returned by assign
480 481
// Assign sometimes reverses the links on the path 
// from the source to destination, 
482
// so you need to look at the entire path to find the next link
483
DOMElement* annotate_rspec::find_next_link_in_path (DOMElement *prev, 
484
                                                    list<const char*>* links)
485 486 487 488 489 490
{
	list<const char*>::iterator it;
	DOMElement* link = NULL;
	for (it = links->begin(); it != links->end(); ++it)
	{
		link = (this->physical_elements->find(*it))->second;
491 492 493 494 495 496 497 498
    
		string link_src = XStr(find_urn(getNthInterface(link,0),
                                    "component_node")).c();
		string link_dst = XStr(find_urn(getNthInterface(link,1),
                                    "component_node")).c();
		string prev_dst = XStr(find_urn(getNthInterface(prev,1),
                                    "component_node")).c();
		if ((link_src == prev_dst) || (link_dst == prev_dst)) {
499 500 501 502 503 504 505
			links->remove(*it);
			break;
		}
	}
	return link;
}

506 507 508 509 510
void annotate_rspec::cleanup()
{
	vector<DOMElement*>::iterator it;
	
	// Remove generated links
511 512 513
	vector<DOMElement*> generated_links 
			= getElementsHavingAttribute(this->virtual_root, "link", 
										 "generated_by_assign");
514
	for (it = generated_links.begin(); it < generated_links.end(); it++) {
515 516 517 518 519
		DOMNode* generated_link = dynamic_cast<DOMNode*>(*it);
		dynamic_cast<DOMNode*>(this->virtual_root)->removeChild(generated_link);
	}
	
	// Remove generated nodes
520 521 522
	vector<DOMElement*> generated_nodes 
			= getElementsHavingAttribute(this->virtual_root, "node", 
										 "generated_by_assign");
523 524 525 526 527 528 529 530 531
	for (it = generated_nodes.begin(); it < generated_nodes.end(); it++)
	{
		DOMNode* generated_link = dynamic_cast<DOMNode*>(*it);
		dynamic_cast<DOMNode*>(this->virtual_root)->removeChild(generated_link);
	}
	
	// Remove additional attributes added to elements
	
	// Remove the is_lan attribute
532 533
	vector<DOMElement*> lan_links 
			= getElementsHavingAttribute(this->virtual_root, "link", "is_lan");
534 535 536 537 538 539 540
	for (it = lan_links.begin(); it < lan_links.end(); it++)
	{
		DOMElement* lan_link = *it;
		lan_link->removeAttribute(XStr("is_lan").x());
	}
}

541 542 543
bool annotate_rspec::is_generated_element(const char* tag, 
										  const char* attr_name, 
										  const char* attr_value)
544
{
545 546 547
	DOMElement* element 
			= getElementByAttributeValue(this->virtual_root, tag, 
										 attr_name, attr_value);
548 549 550 551 552
	if (element == NULL)
		return false;
	return (element->hasAttribute(XStr("generated_by_assign").x()));
}

553
#endif