Commit 34d18441 authored by Tarun Prabhu's avatar Tarun Prabhu

Add extension and vclass support. It should be working correctly now. There's...

Add extension and vclass support. It should be working correctly now. There's a lot of ugliness w.r.t. hardware types and sliver types, while dealing with switches, nodes and vclasses. I have tried to hide most of it in rspec_parser_v2.cc and rspec_parser_helper.cc.
parent 742f3035
......@@ -84,21 +84,20 @@ struct property emulab_extensions_parser::readProperty (const DOMElement* tag)
struct hardness emulab_extensions_parser::readHardness (const DOMElement* tag)
{
struct hardness hardnessObject;
if (this->hasChild(tag, "hard")) {
// XXX: This is a temporary fix. We will need to deal with hard
// vclasses correctly at some point
string strWeight = this->getAttribute(tag, "weight");
if (strWeight == "hard") {
hardnessObject.type = HARD_VCLASS;
}
else /* (this->hasChildTag(tag, "soft")) */ {
hardnessObject.weight
= rspec_parser_helper::stringToNum (this->readChild(tag, "weight"));
else {
hardnessObject.type = SOFT_VCLASS;
hardnessObject.weight = rspec_parser_helper::stringToNum(strWeight);
}
return hardnessObject;
}
vector<struct vclass>
emulab_extensions_parser::readAllVClasses (const DOMElement* elem)
emulab_extensions_parser::readVClasses (const DOMElement* elem)
{
DOMNodeList* vclassNodes
= elem->getElementsByTagName(XStr("emulab:vclass").x());
......@@ -113,10 +112,31 @@ emulab_extensions_parser::readAllVClasses (const DOMElement* elem)
struct vclass emulab_extensions_parser::readVClass (const DOMElement* tag)
{
vector<string> physTypes;
DOMNodeList* physNodes
= tag->getElementsByTagName(XStr("emulab:physical_type").x());
for (int i = 0; i < physNodes->getLength(); i++) {
DOMElement* physNode = dynamic_cast<DOMElement*>(physNodes->item(i));
// XXX: This is nasty because the type name that we give assign
// has to be the concatation of the hardware type and sliver type
// It's not clear which is the best place to do this
// i.e. whether to generate this concatenated type here in the parser
// (in which case any time the formula to convert the single type to
// a hardware type and sliver type changes, it will have to be updated
// both here and in libvtop) or to do it just once in libvtop in which
// case the reverse holds true i.e. when we change the way we concatenate
// the types and present it to assign, we will have to change the code
// in libvtop
string physNodeName = this->getAttribute(physNode, "name");
cerr << "Converted vclass name to "
<< rspec_parser_helper::convertType(physNodeName) << endl;
physTypes.push_back(rspec_parser_helper::convertType(physNodeName));
}
struct vclass vclassObject = {
this->getAttribute(tag, "name"),
rspec_parser_helper::convertType(this->getAttribute(tag, "name")),
this->readHardness(tag),
this->readChild(tag, "physical_type")
physTypes
};
return vclassObject;
}
......@@ -163,4 +183,72 @@ emulab_extensions_parser::readTypeLimits (const DOMElement* tag, int& count)
return rv;
}
string emulab_extensions_parser::readSubnodeOf (const DOMElement* tag,
bool& isSubnode)
{
string rv = "";
DOMNodeList* subnodes
= tag->getElementsByTagName(XStr("emulab:subnode_of").x());
isSubnode = (subnodes->getLength() > 0);
if (isSubnode) {
DOMElement* subnode = dynamic_cast<DOMElement*>(subnodes->item(0));
rv = this->getAttribute(subnode, "parent");
}
return rv;
}
bool emulab_extensions_parser::readDisallowTrivialMix (const DOMElement* tag)
{
DOMNodeList* trivialMix
= tag->getElementsByTagName(XStr("emulab:disallow_trivial_mix").x());
return (trivialMix->getLength() > 0);
}
bool emulab_extensions_parser::readUnique (const DOMElement* tag)
{
DOMNodeList* uniques = tag->getElementsByTagName(XStr("emulab:unique").x());
return (uniques->getLength() > 0);
}
int emulab_extensions_parser::readTrivialBandwidth(const DOMElement* tag,
bool& hasTrivialBw)
{
int trivialBw = 0;
DOMNodeList* bws
= tag->getElementsByTagName(XStr("emulab:trivial_bandwidth").x());
hasTrivialBw = (bws->getLength() > 0);
if (hasTrivialBw) {
trivialBw =(int)this->stringToNum(this->getAttribute
(dynamic_cast<DOMElement*>(bws->item(0)),
"value"));
}
return trivialBw;
}
string emulab_extensions_parser::readHintTo (const DOMElement* tag,
bool& hasHint)
{
string hint = "";
DOMNodeList* hints = tag->getElementsByTagName(XStr("emulab:hint_to").x());
hasHint = (hints->getLength() > 0);
if (hasHint) {
hint = this->getAttribute(dynamic_cast<DOMElement*>(hints->item(0)),
"value");
}
return hint;
}
bool emulab_extensions_parser::readNoDelay (const DOMElement* tag)
{
DOMNodeList* nodelays= tag->getElementsByTagName(XStr("emulab:nodelay").x());
return (nodelays->getLength() > 0);
}
bool emulab_extensions_parser::readTrivialOk (const DOMElement* tag)
{
DOMNodeList* trivial_oks
= tag->getElementsByTagName(XStr("emulab:trivial_ok").x());
return (trivial_oks->getLength() > 0);
}
#endif // WITH_XML
......@@ -61,7 +61,7 @@ namespace rspec_emulab_extension {
struct vclass {
std::string name;
struct hardness type;
std::string physicalType;
std::vector<std::string> physicalTypes;
};
struct property {
......@@ -96,14 +96,21 @@ namespace rspec_emulab_extension {
std::vector<struct property> readProperties
(const xercesc::DOMElement* elem);
struct property readProperty (const xercesc::DOMElement* tag);
std::vector<struct vclass> readAllVClasses (const xercesc::DOMElement*);
std::vector<struct vclass> readVClasses (const xercesc::DOMElement*);
struct vclass readVClass (const xercesc::DOMElement* tag);
std::string readAssignedTo (const xercesc::DOMElement* tag);
std::string readHintTo (const xercesc::DOMElement* tag);
std::string readTypeSlots (const xercesc::DOMElement* tag);
bool readStaticType (const xercesc::DOMElement* tag);
std::vector<struct type_limit> readTypeLimits(const xercesc::DOMElement* tag,
std::vector<struct type_limit> readTypeLimits(const xercesc::DOMElement*,
int& count);
std::string readSubnodeOf (const xercesc::DOMElement* tag, bool&);
virtual bool readDisallowTrivialMix (const xercesc::DOMElement* tag);
virtual bool readUnique (const xercesc::DOMElement* tag);
virtual int readTrivialBandwidth (const xercesc::DOMElement* tag, bool&);
virtual std::string readHintTo (const xercesc::DOMElement* tag, bool&);
virtual bool readNoDelay (const xercesc::DOMElement* tag);
virtual bool readTrivialOk (const xercesc::DOMElement* tag);
};
} // namespace rspec_emulab_extension
......
......@@ -139,7 +139,7 @@ int parse_advertisement(tb_pgraph &pg, tb_sgraph &sg, char *filename) {
rspecParser = new rspec_parser_v2(RSPEC_TYPE_ADVT);
break;
default:
cerr << "ERROR: Unsupported rspec ver. " << rspecVersion
cerr << "*** Unsupported rspec ver. " << rspecVersion
<< " ... Aborting " << endl;
exit(EXIT_FATAL);
}
......@@ -162,21 +162,21 @@ int parse_advertisement(tb_pgraph &pg, tb_sgraph &sg, char *filename) {
*/
XMLDEBUG("starting node population" << endl);
if (!populate_nodes(advt_root,pg,sg,unavailable)) {
cerr << "Error reading nodes from physical topology "
cerr << "*** Error reading nodes from physical topology "
<< filename << endl;
exit(EXIT_FATAL);
}
XMLDEBUG("finishing node population" << endl);
XMLDEBUG("starting link population" << endl);
if (!populate_links(advt_root,pg,sg,unavailable)) {
cerr << "Error reading links from physical topology "
cerr << "*** Error reading links from physical topology "
<< filename << endl;
exit(EXIT_FATAL);
}
XMLDEBUG("finishing link population" << endl);
XMLDEBUG("setting type limits" << endl);
if (!populate_type_limits(advt_root, pg, sg)) {
cerr << "Error setting type limits " << filename << endl;
cerr << "*** Error setting type limits " << filename << endl;
exit(EXIT_FATAL);
}
XMLDEBUG("finishing setting type limits" << endl);
......@@ -219,7 +219,7 @@ bool populate_nodes(DOMElement *root,
bool hasCMId;
string componentId = rspecParser->readPhysicalId(elt, hasComponentId);
string componentManagerId = rspecParser->readComponentManagerId(elt,hasCMId);
if (!hasComponentId || !hasCMId) {
is_ok = false;
continue;
......@@ -228,28 +228,29 @@ bool populate_nodes(DOMElement *root,
// Maintain a list of componentId's seen so far to ensure no duplicates
insert_ret = advertisement_elements->insert
(pair<string, DOMElement*>(componentId, elt));
if (insert_ret.second == false)
{
cerr << componentId << " already exists" << endl;
is_ok = false;
}
if (insert_ret.second == false) {
cerr << "*** " << componentId << " already exists" << endl;
is_ok = false;
}
// XXX: This should not have to be called manually
bool allUnique;
rspecParser->readInterfacesOnNode(elt, allUnique);
// XXX: We don't ever do anything with this, so I am commenting it out
/* Deal with the location tag */
int locationDataCount;
vector<string> locationData
= rspecParser->readLocation(elt, locationDataCount);
string country = locationData[0];
string latitude = "";
string longitude = "";
if (locationDataCount == 3) {
latitude = locationData[1];
longitude = locationData[2];
}
// /* Deal with the location tag */
// int locationDataCount;
// vector<string> locationData
// = rspecParser->readLocation(elt, locationDataCount);
// string country = locationData[0];
// string latitude = "";
// string longitude = "";
// if (locationDataCount == 3) {
// latitude = locationData[1];
// longitude = locationData[2];
// }
pvertex pv;
......@@ -268,13 +269,14 @@ bool populate_nodes(DOMElement *root,
pname2vertex[componentId.c_str()] = pv;
int typeCount;
vector<struct node_type> types = rspecParser->readNodeTypes(elt, typeCount);
vector<struct node_type>types = rspecParser->readNodeTypes(elt, typeCount);
bool switchAdded = false;
for (int i = 0; i < typeCount; i++) {
node_type type = types[i];
const char* typeName = type.typeName.c_str();
int typeSlots = type.typeSlots;
bool isStatic = type.isStatic;
// Add the type into assign's data structures
if (ptypes.find(typeName) == ptypes.end()) {
ptypes[typeName] = new tb_ptype(typeName);
......@@ -290,6 +292,7 @@ bool populate_nodes(DOMElement *root,
* else!
*/
if (strcmp(typeName, "switch") == 0) {
// if (rspecParser->checkIsSwitch(componentId) && !switchAdded) {
p->is_switch = true;
p->types["switch"] = new tb_pnode::type_record(1,false,ptype);
svertex sv = add_vertex(sg);
......@@ -324,13 +327,26 @@ bool populate_nodes(DOMElement *root,
(tb_node_featuredesire(XStr(componentManagerId.c_str()).f(),
1.0, false, featuredesire::FD_TYPE_NORMAL));
bool isSubnode;
string subnodeOf = rspecParser->readSubnodeOf (elt, isSubnode);
// This has to be at the end becuase if we don't populate
// at least the interfaces, we get all kinds of crappy errors
bool isAvailable;
string available = rspecParser->readAvailable(elt, isAvailable);
if (available == "false") {
unavailable.insert(componentId);
continue;
}
++availableCount;
bool isSubnode = false;
int subnodeCnt;
string subnodeOf = rspecParser->readSubnodeOf (elt, isSubnode, subnodeCnt);
if (isSubnode) {
if (!p->subnode_of_name.empty()){
if (subnodeCnt > 1) {
cerr << "*** Too many \"subnode\" relations found in "
<< componentId << "Allowed 1 ... " << endl;
is_ok = false;
continue;
}
}
else {
// Just store the name for now, we'll do late binding to
// an actual pnode later
......@@ -338,16 +354,6 @@ bool populate_nodes(DOMElement *root,
}
}
// This has to be at the end becuase if we don't populate
// at least the interfaces, we get all kinds of crappy errors
bool isAvailable;
string available = rspecParser->readAvailable(elt, isAvailable);
if (available == "false") {
unavailable.insert(componentId);
continue;
}
++availableCount;
// Deal with features
int fdsCount;
vector<struct fd> fds = rspecParser->readFeaturesDesires(elt, fdsCount);
......@@ -377,6 +383,17 @@ bool populate_nodes(DOMElement *root,
(p->features).push_front(node_fd);
}
// Read extensions for emulab-specific flags
bool hasTrivialBw;
int trivialBw = rspecParser->readTrivialBandwidth(elt, hasTrivialBw);
if (hasTrivialBw) {
p->trivial_bw = trivialBw;
}
if (rspecParser->readUnique(elt)) {
p->unique = true;
}
/*
* XXX: Is this really necessary?
*/
......@@ -423,8 +440,7 @@ bool populate_links(DOMElement *root, tb_pgraph &pg, tb_sgraph &sg,
string cmId = rspecParser->readComponentManagerId(elt, hasCMId);
if (!hasComponentId || !hasCMId) {
cerr << "All elements must have a component_uuid/component_urn "
<< "and a component_manager_uuid/component_manager_urn" << endl;
cerr << "*** Require component ID and component manager ID" << endl;
is_ok = false;
}
else {
......@@ -432,11 +448,11 @@ bool populate_links(DOMElement *root, tb_pgraph &pg, tb_sgraph &sg,
advertisement_elements->insert(pair<string, DOMElement*>
(componentId, elt));
if (insert_ret.second == false) {
cerr << componentId << " already exists" << endl;
cerr << "*** " << componentId << " already exists" << endl;
is_ok = false;
}
}
/*
* Get source and destination interfaces - we use knowledge of the
* schema that there is awlays exactly one source and one destination
......@@ -447,26 +463,27 @@ bool populate_links(DOMElement *root, tb_pgraph &pg, tb_sgraph &sg,
// Error handling
switch (ifaceCount) {
case RSPEC_ERROR_BAD_IFACE_COUNT:
cerr << "Incorrect number of interfaces found on link "
<< componentId << ". Expected 2 (found "
<< ifaceCount << ")" << endl;
is_ok = false;
continue;
case RSPEC_ERROR_UNSEEN_NODEIFACE_SRC:
cerr << "Unseen node-interface pair on the source interface ref"
cerr << "*** Unseen node-interface pair on the source interface ref"
<< endl;
is_ok = false;
continue;
case RSPEC_ERROR_UNSEEN_NODEIFACE_DST:
cerr << "Unseen node-interface pair on the destination interface ref"
cerr << "*** Unseen node-interface pair on the destination interface ref"
<< endl;
is_ok = false;
continue;
}
if (ifaceCount != 2) {
cerr << "*** Incorrect number of interfaces found on link "
<< componentId << ". Expected 2 (found "
<< ifaceCount << ")" << endl;
is_ok = false;
continue;
}
/* NOTE: In a request, we assume that each link has only two interfaces
* Although the order is immaterial, assign expects a source first
* and a destination second and we assume the same
......@@ -477,26 +494,26 @@ bool populate_links(DOMElement *root, tb_pgraph &pg, tb_sgraph &sg,
string dst_iface = interfaces[1].physicalIfaceId;
if (src_node == "" || src_iface == "") {
cerr << "Physical link " << componentId
cerr << "*** Physical link " << componentId
<< " must have a component id and component interface id "
<< " specified for the source node" << endl;
is_ok = false;
continue;
}
if (dst_node == "" || dst_iface == "") {
cerr << "Physical link " << componentId
cerr << "*** Physical link " << componentId
<< " must have a component id and component interface id"
<< " specified for the destination node" << endl;
is_ok = false;
continue;
}
if( unavailable.count( src_node ) ||
unavailable.count( dst_node ) )
//one or both of the endpoints are unavailable; silently
//ignore the link
continue;
/*
* Get standard link characteristics
*/
......
This diff is collapsed.
......@@ -99,25 +99,31 @@ vector<struct node_type> rspec_parser::readNodeTypes (const DOMElement* node,
int& typeCount,
int unlimitedSlots)
{
bool isSwitch = false;
DOMNodeList* nodeTypes = node->getElementsByTagName(XStr("node_type").x());
vector<struct node_type> types;
for (int i = 0; i < nodeTypes->getLength(); i++)
{
DOMElement *tag = dynamic_cast<DOMElement*>(nodeTypes->item(i));
string typeName = XStr(tag->getAttribute(XStr("type_name").x())).c();
int typeSlots;
string slot = XStr(tag->getAttribute(XStr("type_slots").x())).c();
if (slot == "unlimited")
typeSlots = unlimitedSlots;
else
typeSlots = (int)stringToNum(slot);
bool isStatic = tag->hasAttribute(XStr("static").x());
struct node_type type = {typeName, typeSlots, isStatic};
types.push_back(type);
for (int i = 0; i < nodeTypes->getLength(); i++) {
DOMElement *tag = dynamic_cast<DOMElement*>(nodeTypes->item(i));
string typeName = XStr(tag->getAttribute(XStr("type_name").x())).c();
if (typeName == "switch") {
isSwitch = true;
}
int typeSlots;
string slot = XStr(tag->getAttribute(XStr("type_slots").x())).c();
if (slot == "unlimited")
typeSlots = unlimitedSlots;
else
typeSlots = (int)stringToNum(slot);
bool isStatic = tag->hasAttribute(XStr("static").x());
struct node_type type = {typeName, typeSlots, isStatic};
types.push_back(type);
}
if (isSwitch) {
this->addSwitch(node);
}
typeCount = nodeTypes->getLength();
return types;
}
......@@ -161,6 +167,7 @@ rspec_parser::readInterfacesOnNode (const DOMElement* node,
}
// Returns a link_characteristics element
// count should be 1 on success.
struct link_characteristics
rspec_parser :: readLinkCharacteristics (const DOMElement* link,
int& count,
......@@ -184,6 +191,7 @@ rspec_parser :: readLinkCharacteristics (const DOMElement* link,
latency = hasLatency ? atoi(strLat.c_str()) : 0 ;
packetLoss = hasPacketLoss ? atof(strLoss.c_str()) : 0.0;
count = 1;
struct link_characteristics rv = {bandwidth, latency, packetLoss};
return rv;
}
......@@ -233,7 +241,6 @@ rspec_parser :: readLinkInterface (const DOMElement* link, int& ifaceCount)
return rv;
}
vector<struct link_type> rspec_parser::readLinkTypes (const DOMElement* link,
int& typeCount)
{
......@@ -252,8 +259,32 @@ vector<struct link_type> rspec_parser::readLinkTypes (const DOMElement* link,
return types;
}
string rspec_parser :: readSubnodeOf (const DOMElement* tag, bool& isSubnode)
bool rspec_parser::checkIsSwitch (string nodeId)
{
return (((this->switches).find(nodeId)) != (this->switches).end());
}
void rspec_parser::addSwitch (const DOMElement* node)
{
bool dummy;
string nodeId = this->readPhysicalId(node, dummy);
if (this->rspecType == RSPEC_TYPE_REQ) {
nodeId = this->readVirtualId(node, dummy);
}
(this->switches).insert(nodeId);
}
vector<struct vclass>
rspec_parser :: readVClasses (const DOMElement* tag)
{
return vector<struct vclass>();
}
string rspec_parser :: readSubnodeOf (const DOMElement* tag,
bool& isSubnode,
int& count)
{
count = (tag->getElementsByTagName(XStr("subnode_of").x()))->getLength();
return (this->readChild(tag, "subnode_of", isSubnode));
}
......@@ -281,4 +312,38 @@ rspec_parser::readFeaturesDesires (const DOMElement* tag, int& count)
return vector<struct fd>();
}
bool rspec_parser::readDisallowTrivialMix (const DOMElement* tag)
{
return false;
}
bool rspec_parser::readUnique (const DOMElement* tag)
{
return false;
}
int rspec_parser::readTrivialBandwidth (const DOMElement* tag,
bool& hasTrivialBw)
{
hasTrivialBw = false;
return 0;
}
string rspec_parser::readHintTo (const DOMElement* tag, bool& hasHintTo)
{
hasHintTo = false;
return "";
}
bool rspec_parser::readNoDelay (const DOMElement* tag)
{
return false;
}
bool rspec_parser::readTrivialOk (const DOMElement* tag)
{
return false;
}
#endif
......@@ -74,6 +74,8 @@ class rspec_parser : public rspec_parser_helper
int rspecType;
std::set< std::pair<std::string, std::string> >ifacesSeen;
std::set< std::string > switches;
virtual void addSwitch (const xercesc::DOMElement*);
struct link_interface getIface (const xercesc::DOMElement*);
public:
......@@ -96,9 +98,6 @@ class rspec_parser : public rspec_parser_helper
readNodeTypes (const xercesc::DOMElement*,
int&, int unlimitedSlots=1000);
// Reads subnode tag, if present
virtual std::string readSubnodeOf (const xercesc::DOMElement*, bool&);
// Reads the exclusive tag if present
virtual std::string readExclusive (const xercesc::DOMElement*, bool&);
......@@ -133,6 +132,20 @@ class rspec_parser : public rspec_parser_helper
virtual std::vector<struct rspec_emulab_extension::fd>
readFeaturesDesires(const xercesc::DOMElement*, int&);
virtual bool checkIsSwitch (std::string nodeId);
virtual std::vector<struct rspec_emulab_extension::vclass>
readVClasses (const xercesc::DOMElement* tag);
virtual std::string readSubnodeOf (const xercesc::DOMElement* tag,
bool& isSubnode,
int& count);
virtual bool readDisallowTrivialMix (const xercesc::DOMElement* tag);
virtual bool readUnique (const xercesc::DOMElement* tag);
virtual int readTrivialBandwidth (const xercesc::DOMElement* tag, bool&);
virtual std::string readHintTo (const xercesc::DOMElement* tag, bool&);
virtual bool readNoDelay (const xercesc::DOMElement* tag);
virtual bool readTrivialOk (const xercesc::DOMElement* tag);
};
......
......@@ -20,88 +20,108 @@
// Returns the attribute value and an out paramter if the attribute exists
string rspec_parser_helper :: getAttribute(const DOMElement* tag,
const string attrName,
bool& hasAttr)
const string attrName,
bool& hasAttr)
{
hasAttr = tag->hasAttribute(XStr(attrName.c_str()).x());
if (hasAttr)
return XStr(tag->getAttribute(XStr(attrName.c_str()).x())).c();
return "";
hasAttr = tag->hasAttribute(XStr(attrName.c_str()).x());
string rv = "";
if (hasAttr)
rv = XStr(tag->getAttribute(XStr(attrName.c_str()).x())).c();
return rv;
}
string rspec_parser_helper :: getAttribute(const DOMElement* tag,
const string attr)
const string attr)
{
bool dummy = false;
return (this->getAttribute(tag, attr, dummy));
bool dummy = false;
return (this->getAttribute(tag, attr, dummy));
}
bool rspec_parser_helper :: hasAttribute(const DOMElement* tag,
const string attrName)
const string attrName)
{
return (tag->hasAttribute(XStr(attrName).x()));
return (tag->hasAttribute(XStr(attrName).x()));
}
string rspec_parser_helper :: readChild (const DOMElement* elt,
const char* tagName,
bool& hasTag)
const char* tagName,
bool& hasTag)
{
hasTag = hasChildTag(elt, tagName);
if (hasTag)
return XStr(getChildValue(elt, tagName)).c();
return "";
hasTag = hasChildTag(elt, tagName);
string rv = "";
if (hasTag)
rv = XStr(getChildValue(elt, tagName)).c();
return rv;
}
string rspec_parser_helper :: readChild (const DOMElement* elt,
const char* tagName)
const char* tagName)
{
bool dummy = false;
return (this->readChild(elt, tagName, dummy));
bool dummy = false;
return (this->readChild(elt, tagName, dummy));
}
bool rspec_parser_helper::hasChild (const DOMElement* elt,
const char* childName)
const char* childName)
{
return ((elt->getElementsByTagName(XStr(childName).x()))->getLength()
!= 0);
return ((elt->getElementsByTagName(XStr(childName).x()))->getLength()
!= 0);
}
string rspec_parser_helper :: numToString(int num)
{
std::ostringstream oss;
oss << num;
return oss.str();
std::ostringstream oss;
oss << num;
return oss.str();
}
string rspec_parser_helper :: numToString(double num)
{
std::ostringstream oss;
oss << num;
std::ostringstream oss;
oss << num;
return oss.str();
}
float rspec_parser_helper :: stringToNum (string s)
{
float num;
std::istringstream iss(s);
iss >> num;
return num;