LAMP/Tutorial: lamp-sendmanifest.py

File lamp-sendmanifest.py, 31.0 KB (added by mjaffee@indiana.edu, 7 years ago)

send-manifest with rspec v3 support

Line 
1#!/usr/bin/env python
2
3import os
4import sys
5import getopt
6
7import xml.dom.minidom as dom
8from httplib import HTTPConnection, HTTPSConnection
9
10cert_file = os.environ['HOME'] + "/.ssl/emulab.pem"
11key_file = os.environ['HOME'] + "/.ssl/emulab.pem"
12
13if "HTTPS_CERT_FILE" in os.environ:
14    cert_file = os.environ["HTTPS_CERT_FILE"]
15
16if "HTTPS_KEY_FILE" in os.environ:
17    key_file = os.environ["HTTPS_KEY_FILE"]
18
19class SimpleClient:
20    """
21    Very simple client to send SOAP requests to perfSONAR service
22    """
23
24    def __init__(self, host, port, uri, cert=None, key=None):
25        self.host = host
26        self.port = port
27        self.uri  = uri
28        self.cert = cert
29        self.key = key
30   
31    def soapifyMessage(self, message):
32        headerString = """<SOAP-ENV:Envelope
33 xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
34 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
35 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
36 xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
37<SOAP-ENV:Header/>
38<SOAP-ENV:Body>
39%s
40</SOAP-ENV:Body>
41</SOAP-ENV:Envelope>
42""" % message
43        return headerString
44
45    def send_request(self, message, useSSL=False, issoap=False):
46        if useSSL:
47            conn = HTTPSConnection(self.host, self.port, self.key, self.cert)
48        else:
49            conn = HTTPConnection(self.host, self.port)
50           
51        conn.connect()
52        headers = {'SOAPAction':'', 'Content-Type': 'text/xml'}
53        if issoap == False:
54            message = self.soapifyMessage(message)
55        conn.request('POST', self.uri, message, headers)
56        resp = conn.getresponse()
57        response = resp.read()
58        conn.close()
59        return response
60
61
62# pS Namespaces
63UNIS_NS = "http://ogf.org/schema/network/topology/unis/20100528/"
64PSCONFIG_NS = "http://ogf.org/schema/network/topology/psconfig/20100716/"
65PROTOGENI_NS ="http://ogf.org/schema/network/topology/protogeni/20100716/"
66TS_EVENTTYPE = "http://ggf.org/ns/nmwg/topology/20070809"
67XQUERY_EVENTTYPE = "http://ggf.org/ns/nmwg/tools/org/perfsonar/service/lookup/xquery/1.0"
68
69# RSpec Namespaces
70RSPEC_NS = ""
71RSPEC_NS_01 = "http://protogeni.net/resources/rspec/0.1"
72RSPEC_NS_02 = "http://protogeni.net/resources/rspec/0.2"
73RSPEC_NS_2 = "http://www.protogeni.net/resources/rspec/2"
74RSPEC_NS_3 = "http://www.geni.net/resources/rspec/3"
75LAMP_NS = "http://protogeni.net/resources/rspec/0.2/ext/lamp/1"
76
77host = "blackseal.damsl.cis.udel.edu"
78port = 8012
79uri = "/perfSONAR_PS/services/unis"
80
81if "UNIS_ADDRESS" in os.environ:
82    host = os.environ["UNIS_ADDRESS"]
83
84
85class RSpecXMLParsingException(Exception):
86    def __init__(self, msg):
87        self.msg = msg
88   
89    def __str__(self):
90        return self.msg
91
92# TODO: This method needs refactoring.
93def manifest_v2_to_unis(manifest, slice_id):
94    unis_dom = dom.getDOMImplementation().createDocument(UNIS_NS, "topology", None)
95    topology = unis_dom.documentElement
96    topology.setAttribute('id', 'genitopo')
97
98    slice_id = slice_id.replace("urn:publicid:IDN+", "")
99    domain = unis_dom.createElementNS(UNIS_NS, "domain")
100    domain.setAttribute('id', create_urn(domain=slice_id))
101    topology.appendChild(domain)
102
103    # map an interface to its node so we can look it up by client_id
104    node_interfaces = dict()
105    parsed = dict()
106    rspec = manifest.documentElement
107    rsnodes = rspec.getElementsByTagNameNS(RSPEC_NS, "node")
108
109    for rsnode in rsnodes:
110        node_virtual_id = rsnode.getAttribute('client_id')
111        unis_id = create_urn(domain=slice_id, node=node_virtual_id)
112        if unis_id in parsed:
113            raise RSpecXMLParsingException, \
114               "Two node elements found with same id (%s)" % node_virtual_id
115
116        unis_node = unis_dom.createElementNS(UNIS_NS, "node")
117        unis_node.setAttribute('id', unis_id)
118
119        parsed[unis_id] = unis_node
120
121        # Add node's hostname as a unis:address of type 'hostname'
122        rsnodehosts = get_qualified_xml_children(rsnode, RSPEC_NS, 'host')
123        for rsnodehost in rsnodehosts:
124            hostname = rsnodehost.getAttribute('name')
125            unis_address = create_text_element(unis_dom, UNIS_NS, "address", hostname)
126            unis_address.setAttribute('type', 'dns')
127            unis_node.appendChild(unis_address)
128
129        unis_node_properties_bag = unis_dom.createElementNS(UNIS_NS, "nodePropertiesBag")
130        unis_node.appendChild(unis_node_properties_bag)
131
132        unis_node_pgeni_properties = unis_dom.createElementNS(PROTOGENI_NS, "nodeProperties")
133        unis_node_properties_bag.appendChild(unis_node_pgeni_properties)
134
135        # Most RSpec node attributes become attributes of the pgeni properties
136        # element, but we ignore a few because they're represented elsewhere
137        NODE_IGNORED_ATTRS = ('client_id', 'sshdport')
138        for i in range(rsnode.attributes.length):
139            attr = rsnode.attributes.item(i)
140            if attr.name in NODE_IGNORED_ATTRS:
141                continue
142            unis_node_pgeni_properties.setAttribute(attr.name, attr.value)
143            if attr.prefix and attr.prefix != 'xmlns':
144                unis_node_pgeni_properties.setAttribute("xmlns:%s" % attr.prefix,
145                                                        attr.namespaceURI)
146
147        # Now we process the child elements that we recognize
148        rsinterfaces = rsnode.getElementsByTagNameNS(RSPEC_NS, "interface")
149        for rsiface in rsinterfaces:
150            iface_virtual_id = rsiface.getAttribute('client_id').split(':')[-1]
151            unis_id = create_urn(domain=slice_id, node=node_virtual_id,
152                                 port=iface_virtual_id)
153            if unis_id in parsed:
154                raise RSpecXMLParsingException, \
155                   "Two interface elements found with same id (%s) in node %s" \
156                        % iface_virtual_id, node_virtual_id
157           
158           
159            unis_port = unis_dom.createElementNS(UNIS_NS, "port")
160            unis_port.setAttribute('id', unis_id)
161           
162            iface_component_id = rsiface.getAttribute('component_id')
163            node_interfaces[iface_component_id] = node_virtual_id
164            parsed[unis_id] = unis_port
165                       
166            unis_port.appendChild(
167                create_text_element(unis_dom, UNIS_NS, "name", iface_component_id.split(':')[-1]))
168
169            mac = rsiface.getAttribute('mac_address')
170            if mac:
171                unis_port.appendChild(
172                    create_text_element(unis_dom, UNIS_NS, 'address', mac,
173                                        attributes=(('type', 'mac'),) ))
174           
175            rsifaceips = get_qualified_xml_children(rsiface, RSPEC_NS, 'ip')
176            for rsifaceip in rsifaceips:
177                ip = rsifaceip.getAttribute('address')
178                iptype = rsifaceip.getAttribute('type')
179                if ip:
180                    unis_port.appendChild(
181                        create_text_element(unis_dom, UNIS_NS, 'address', ip,
182                                            attributes=(('type', iptype),) ))
183               
184            unis_port_properties_bag = unis_dom.createElementNS(UNIS_NS, "portPropertiesBag")
185            unis_port.appendChild(unis_port_properties_bag)
186
187            unis_port_pgeni_properties = unis_dom.createElementNS(PROTOGENI_NS, "portProperties")
188            unis_port_properties_bag.appendChild(unis_port_pgeni_properties)
189
190            INTERFACE_IGNORED_ATTRS = ('client_id', 'mac_address')
191            for i in range(rsiface.attributes.length):
192                attr = rsiface.attributes.item(i)
193                if attr.name in INTERFACE_IGNORED_ATTRS:
194                    continue
195                unis_port_pgeni_properties.setAttribute(attr.name, attr.value)
196                if attr.prefix and attr.prefix != 'xmlns':
197                    unis_port_pgeni_properties.setAttribute("xmlns:%s" % attr.prefix,
198                                                            attr.namespaceURI)
199
200            INTERFACE_IGNORED_CHILDREN = ((RSPEC_NS, 'ip'),)
201            clone_children(rsiface, unis_port_pgeni_properties, INTERFACE_IGNORED_CHILDREN)
202       
203            unis_node.appendChild(unis_port)
204
205        # We expect a single lamp:config element and we basically just change
206        # the namespace to psconfig and clone the root element as a nodeProperties
207        rslamp_config = get_unique_xml_child(rsnode, LAMP_NS, 'config')
208        if rslamp_config:
209            unis_node_psconfig_properties = unis_dom.createElementNS(PSCONFIG_NS,
210                                                                     "nodeProperties")
211            unis_node_properties_bag.appendChild(unis_node_psconfig_properties)
212
213            clone_children(rslamp_config, unis_node_psconfig_properties)
214
215        # All the other child nodes of rspec node we clone into the pgeni extension
216        NODE_IGNORED_CHILDREN = ((LAMP_NS, 'config'), (RSPEC_NS, 'interface'), (RSPEC_NS, 'host'))
217        clone_children(rsnode, unis_node_pgeni_properties, NODE_IGNORED_CHILDREN)
218
219        domain.appendChild(unis_node)
220
221    rslinks = rspec.getElementsByTagNameNS(RSPEC_NS, "link")
222    for rslink in rslinks:
223        link_virtual_id = rslink.getAttribute('client_id')
224        unis_id = create_urn(domain=slice_id, link=link_virtual_id)
225        if unis_id in parsed:
226            raise RSpecXMLParsingException, \
227               "Found two link elements with same id (%s)" % link_virtual_id
228
229        # XXX: Note that if this RSpec link has more than two interface_refs
230        # it should actually be a UNIS network element. But we don't deal
231        # with this case yet (it will raise an error below).
232        unis_link = unis_dom.createElementNS(UNIS_NS, "link")
233        unis_link.setAttribute('id', unis_id)
234
235        parsed[unis_id] = unis_link
236
237        # XXX: We run away a little from the current NML consensus regarding
238        #   links. We basically add an attribute 'directed' set to 'false'
239        #   to identify the link as a bidirectional link, and make two
240        #   relation elements of type 'endPoint'.
241        unis_link.setAttribute('directed', 'false')
242
243        # Add link's link_type element as unis:type'
244        link_type = get_unique_xml_child(rslink, RSPEC_NS, 'link_type')
245        if link_type:
246            unis_link.appendChild(
247                create_text_element(unis_dom, UNIS_NS, 'type',
248                                    link_type.getAttribute('type_name')))
249
250        unis_link_properties_bag = unis_dom.createElementNS(UNIS_NS, "linkPropertiesBag")
251        unis_link.appendChild(unis_link_properties_bag)
252
253        unis_link_pgeni_properties = unis_dom.createElementNS(PROTOGENI_NS, "linkProperties")
254        unis_link_properties_bag.appendChild(unis_link_pgeni_properties)
255
256        LINK_IGNORED_ATTRS = ('client_id',)
257        for i in range(rslink.attributes.length):
258            attr = rslink.attributes.item(i)
259            if attr.name in LINK_IGNORED_ATTRS:
260                continue
261            unis_link_pgeni_properties.setAttribute(attr.name, attr.value)
262            if attr.prefix and attr.prefix != 'xmlns':
263                unis_link_pgeni_properties.setAttribute("xmlns:%s" % attr.prefix,
264                                                        attr.namespaceURI)
265
266        interface_refs = rslink.getElementsByTagNameNS(RSPEC_NS, "interface_ref")
267        if len(interface_refs) != 2:
268            raise RSpecXMLParsingException, \
269                "Unsupported number of interface_refs in link %s" % link_virtual_id
270
271        for iface_ref in interface_refs:
272            iface_ref_id = iface_ref.getAttribute('client_id').split(':')[-1]
273            iface_ref_compid = iface_ref.getAttribute('component_id')
274            port_id_ref = create_urn(domain=slice_id,
275                                     node=node_interfaces[iface_ref_compid],
276                                     port=iface_ref_id)
277
278            if port_id_ref not in parsed:
279                raise RSpecXMLParsingException, \
280                    "Link %s references nonexistent interface" % link_virtual_id
281
282            # Finally we can create the actual relation for this iface_ref
283            unis_relation = unis_dom.createElementNS(UNIS_NS, "relation")
284            unis_relation.setAttribute('type', 'endPoint')
285            unis_relation.appendChild(
286                create_text_element(unis_dom, UNIS_NS, 'portIdRef', port_id_ref))
287            unis_link.appendChild(unis_relation)
288
289        # We clone everything else that's left
290        LINK_IGNORED_CHILDREN = ((RSPEC_NS, 'interface_ref'), (RSPEC_NS, 'link_type'))
291        clone_children(rslink, unis_link_pgeni_properties, LINK_IGNORED_CHILDREN)
292
293        domain.appendChild(unis_link)
294
295    # Alright, seems like we're done. Now we change all the namespaces to the
296    # appropriate UNIS based namespace and make sure tags are correct. This
297    # could have been done while processing the elements, but it's simpler to
298    # do here (we don't have to worry about the n levels of cloned children).
299    for e in domain.getElementsByTagNameNS(PROTOGENI_NS, "*"):
300        e.tagName = 'pgeni:' + e.localName
301
302    for e in domain.getElementsByTagNameNS(RSPEC_NS, "*"):
303        e.namespaceURI = PROTOGENI_NS
304        e.tagName = 'pgeni:' + e.localName
305
306    for e in domain.getElementsByTagNameNS(PSCONFIG_NS, "*"):
307        e.tagName = 'psconfig:' + e.localName
308
309    for e in domain.getElementsByTagNameNS(LAMP_NS, "*"):
310        e.namespaceURI = PSCONFIG_NS
311        e.tagName = 'psconfig:' + e.localName
312
313    # Now set the namespace globally in the document, minidom doesn't
314    # do this for us so we must do it manually. UNIS_NS is the default.
315    topology.setAttribute("xmlns", UNIS_NS)
316    topology.setAttribute("xmlns:pgeni", PROTOGENI_NS)
317    topology.setAttribute("xmlns:psconfig", PSCONFIG_NS)
318
319    return unis_dom
320   
321# TODO: This method needs refactoring.
322def manifest_to_unis(manifest, slice_id):
323    unis_dom = dom.getDOMImplementation().createDocument(UNIS_NS, "topology", None)
324    topology = unis_dom.documentElement
325    topology.setAttribute('id', 'genitopo')
326   
327    slice_id = slice_id.replace("urn:publicid:IDN+", "")
328    domain = unis_dom.createElementNS(UNIS_NS, "domain")
329    domain.setAttribute('id', create_urn(domain=slice_id))
330    topology.appendChild(domain)
331   
332    parsed = dict()
333    rspec = manifest.documentElement
334    rsnodes = rspec.getElementsByTagNameNS(RSPEC_NS, "node")
335    for rsnode in rsnodes:
336        node_virtual_id = rsnode.getAttribute('virtual_id')
337        unis_id = create_urn(domain=slice_id, node=node_virtual_id)
338        if unis_id in parsed:
339            raise RSpecXMLParsingException, \
340               "Two node elements found with same id (%s)" % node_virtual_id
341       
342        unis_node = unis_dom.createElementNS(UNIS_NS, "node")
343        unis_node.setAttribute('id', unis_id)
344       
345        parsed[unis_id] = unis_node
346         
347        # Add node's hostname as a unis:address of type 'hostname'
348        hostname = rsnode.getAttribute('hostname')
349        unis_address = create_text_element(unis_dom, UNIS_NS, "address", hostname)
350        unis_address.setAttribute('type', 'dns')
351        unis_node.appendChild(unis_address)
352       
353        unis_node_properties_bag = unis_dom.createElementNS(UNIS_NS, "nodePropertiesBag")
354        unis_node.appendChild(unis_node_properties_bag)
355       
356        unis_node_pgeni_properties = unis_dom.createElementNS(PROTOGENI_NS, "nodeProperties")
357        unis_node_properties_bag.appendChild(unis_node_pgeni_properties)
358       
359        # Most RSpec node attributes become attributes of the pgeni properties
360        # element, but we ignore a few because they're represented elsewhere
361        NODE_IGNORED_ATTRS = ('virtual_id', 'hostname', 'sshdport')
362        for i in range(rsnode.attributes.length):
363            attr = rsnode.attributes.item(i)
364            if attr.name in NODE_IGNORED_ATTRS:
365                continue
366            unis_node_pgeni_properties.setAttribute(attr.name, attr.value)
367       
368        # Now we process the child elements that we recognize
369        rsinterfaces = rsnode.getElementsByTagNameNS(RSPEC_NS, "interface")
370        for rsiface in rsinterfaces:
371            iface_virtual_id = rsiface.getAttribute('virtual_id')
372            unis_id = create_urn(domain=slice_id, node=node_virtual_id,
373                                 port=iface_virtual_id)
374            if unis_id in parsed:
375                raise RSpecXMLParsingException, \
376                   "Two interface elements found with same id (%s) in node %s" \
377                        % iface_virtual_id, node_virtual_id
378           
379            unis_port = unis_dom.createElementNS(UNIS_NS, "port")
380            unis_port.setAttribute('id', unis_id)
381           
382            parsed[unis_id] = unis_port
383           
384            iface_component_id = rsiface.getAttribute('component_id')
385            unis_port.appendChild(
386                create_text_element(unis_dom, UNIS_NS, "name", iface_component_id))
387           
388            unis_port_properties_bag = unis_dom.createElementNS(UNIS_NS, "portPropertiesBag")
389            unis_port.appendChild(unis_port_properties_bag)
390       
391            unis_port_pgeni_properties = unis_dom.createElementNS(PROTOGENI_NS, "portProperties")
392            unis_port_properties_bag.appendChild(unis_port_pgeni_properties)
393           
394            INTERFACE_IGNORED_ATTRS = ('virtual_id',)
395            for i in range(rsiface.attributes.length):
396                attr = rsiface.attributes.item(i)
397                if attr.name in INTERFACE_IGNORED_ATTRS:
398                    continue
399                unis_port_pgeni_properties.setAttribute(attr.name, attr.value)
400             
401            unis_node.appendChild(unis_port)
402           
403        # We expect a single lamp:config element and we basically just change
404        # the namespace to psconfig and clone the root element as a nodeProperties
405        rslamp_config = get_unique_xml_child(rsnode, LAMP_NS, 'config')
406        if rslamp_config:
407            unis_node_psconfig_properties = unis_dom.createElementNS(PSCONFIG_NS,
408                                                                     "nodeProperties")
409            unis_node_properties_bag.appendChild(unis_node_psconfig_properties)
410           
411            clone_children(rslamp_config, unis_node_psconfig_properties)
412       
413        # All the other child nodes of rspec node we clone into the pgeni extension
414        NODE_IGNORED_CHILDREN = ((LAMP_NS, 'config'), (RSPEC_NS, 'interface'))
415        clone_children(rsnode, unis_node_pgeni_properties, NODE_IGNORED_CHILDREN)
416       
417        domain.appendChild(unis_node)
418   
419    rslinks = rspec.getElementsByTagNameNS(RSPEC_NS, "link")
420    for rslink in rslinks:
421        link_virtual_id = rslink.getAttribute('virtual_id')
422        unis_id = create_urn(domain=slice_id, link=link_virtual_id)
423        if unis_id in parsed:
424            raise RSpecXMLParsingException, \
425               "Found two link elements with same id (%s)" % link_virtual_id
426       
427        # XXX: Note that if this RSpec link has more than two interface_refs
428        # it should actually be a UNIS network element. But we don't deal
429        # with this case yet (it will raise an error below).
430        unis_link = unis_dom.createElementNS(UNIS_NS, "link")
431        unis_link.setAttribute('id', unis_id)
432       
433        parsed[unis_id] = unis_link
434         
435        # XXX: We run away a little from the current NML consensus regarding
436        #   links. We basically add an attribute 'directed' set to 'false'
437        #   to identify the link as a bidirectional link, and make two
438        #   relation elements of type 'endPoint'.
439        unis_link.setAttribute('directed', 'false')
440       
441        # Add link's link_type element as unis:type'
442        link_type = get_unique_xml_child(rslink, RSPEC_NS, 'link_type')
443        if link_type:
444            unis_link.appendChild(
445                create_text_element(unis_dom, UNIS_NS, 'type',
446                                    link_type.getAttribute('type_name')))
447       
448        unis_link_properties_bag = unis_dom.createElementNS(UNIS_NS, "linkPropertiesBag")
449        unis_link.appendChild(unis_link_properties_bag)
450       
451        unis_link_pgeni_properties = unis_dom.createElementNS(PROTOGENI_NS, "linkProperties")
452        unis_link_properties_bag.appendChild(unis_link_pgeni_properties)
453       
454        LINK_IGNORED_ATTRS = ('virtual_id',)
455        for i in range(rslink.attributes.length):
456            attr = rslink.attributes.item(i)
457            if attr.name in LINK_IGNORED_ATTRS:
458                continue
459            unis_link_pgeni_properties.setAttribute(attr.name, attr.value)
460       
461       
462        interface_refs = rslink.getElementsByTagNameNS(RSPEC_NS, "interface_ref")
463        if len(interface_refs) != 2:
464            raise RSpecXMLParsingException, \
465                "Unsupported number of interface_refs in link %s" % link_virtual_id
466       
467        for iface_ref in interface_refs:
468            port_id_ref = create_urn(domain=slice_id,
469                                     node=iface_ref.getAttribute('virtual_node_id'),
470                                     port=iface_ref.getAttribute('virtual_interface_id'))
471           
472            if port_id_ref not in parsed:
473                raise RSpecXMLParsingException, \
474                    "Link %s references nonexistent interface" % link_virtual_id
475           
476            # This part is bizarre. RSpec puts the sliver, component urn,
477            # MAC and IP addresses of an iface as part of the interface_ref
478            unis_port = parsed[port_id_ref]
479           
480            mac = iface_ref.getAttribute('MAC')
481            if mac:
482                unis_port.appendChild(
483                    create_text_element(unis_dom, UNIS_NS, 'address', mac,
484                                        attributes=(('type', 'mac'),) ))
485           
486            ip = iface_ref.getAttribute('IP')
487            if ip:
488                unis_port.appendChild(
489                    create_text_element(unis_dom, UNIS_NS, 'address', ip,
490                                        attributes=(('type', 'ipv4'),) ))
491           
492            unis_port_pgeni_properties = \
493                unis_port.getElementsByTagNameNS(PROTOGENI_NS, "portProperties")
494            assert len(unis_port_pgeni_properties) == 1
495            unis_port_pgeni_properties = unis_port_pgeni_properties[0]
496           
497            IFACEREF_IGNORED_ATTRS = ('virtual_node_id', 'MAC', 'IP',
498                                      'virtual_interface_id')
499            for i in range(iface_ref.attributes.length):
500                attr = iface_ref.attributes.item(i)
501                if attr.name in IFACEREF_IGNORED_ATTRS:
502                    continue
503                unis_port_pgeni_properties.setAttribute(attr.name, attr.value)
504           
505            # Finally we can create the actual relation for this iface_ref
506            unis_relation = unis_dom.createElementNS(UNIS_NS, "relation")
507            unis_relation.setAttribute('type', 'endPoint')
508            unis_relation.appendChild(
509                create_text_element(unis_dom, UNIS_NS, 'portIdRef', port_id_ref))
510            unis_link.appendChild(unis_relation)
511       
512        # We clone everything else that's left
513        LINK_IGNORED_CHILDREN = ((RSPEC_NS, 'interface_ref'), (RSPEC_NS, 'link_type'))
514        clone_children(rslink, unis_link_pgeni_properties, LINK_IGNORED_CHILDREN)
515       
516        domain.appendChild(unis_link)
517   
518    # Alright, seems like we're done. Now we change all the namespaces to the
519    # appropriate UNIS based namespace and make sure tags are correct. This
520    # could have been done while processing the elements, but it's simpler to
521    # do here (we don't have to worry about the n levels of cloned children).
522    for e in domain.getElementsByTagNameNS(PROTOGENI_NS, "*"):
523        e.tagName = 'pgeni:' + e.localName
524   
525    for e in domain.getElementsByTagNameNS(RSPEC_NS, "*"):
526        e.namespaceURI = PROTOGENI_NS
527        e.tagName = 'pgeni:' + e.localName
528       
529    for e in domain.getElementsByTagNameNS(PSCONFIG_NS, "*"):
530        e.tagName = 'psconfig:' + e.localName
531           
532    for e in domain.getElementsByTagNameNS(LAMP_NS, "*"):
533        e.namespaceURI = PSCONFIG_NS
534        e.tagName = 'psconfig:' + e.localName
535       
536    # Now set the namespace globally in the document, minidom doesn't
537    # do this for us so we must do it manually. UNIS_NS is the default.
538    topology.setAttribute("xmlns", UNIS_NS)
539    topology.setAttribute("xmlns:pgeni", PROTOGENI_NS)
540    topology.setAttribute("xmlns:psconfig", PSCONFIG_NS)
541   
542    return unis_dom
543   
544   
545class Usage(Exception):
546    def __init__(self, msg):
547        self.msg = msg
548
549def main(argv=None):
550    if argv is None:
551        argv = sys.argv
552    try:
553        try:
554            opts, args = getopt.getopt(argv[1:], "h", ["help"])
555            if opts or (len(args) != 2 and len(args) != 3):
556                raise Usage('Not enough arguments')
557           
558            manifest_xml = args[0]
559            slice_id = args[1]
560
561            try:
562                open(manifest_xml, 'r')
563            except IOError, msg:
564                raise Usage('Cannot open manifest: ' + msg)
565           
566            if not slice_id.startswith("urn:publicid:IDN+"):
567                raise Usage('Invalid slice urn')
568           
569            credential_xml = None
570            if len(args) == 3:
571                try:
572                    open(args[2], 'r')
573                    credential_xml = args[2]
574                except IOError, msg:
575                    raise Usage('Cannot open credential: ' + msg)
576
577        except getopt.error, msg:
578            raise Usage(msg)
579       
580        manifest_dom = dom.parse(manifest_xml)
581        ns = manifest_dom.documentElement.getAttribute('xmlns')
582
583        global RSPEC_NS
584        RSPEC_NS = ns
585        if ns == RSPEC_NS_01 or ns == RSPEC_NS_02:
586            unis_dom = manifest_to_unis(manifest_dom, slice_id)
587        if ns == RSPEC_NS_2 or ns == RSPEC_NS_3:
588            unis_dom = manifest_v2_to_unis(manifest_dom, slice_id)
589        else:
590            print "Manifest has unknown default namespace"
591            return
592
593        # Clean spurious empty lines in message (toprettyxml() abuses them)
594        unis_str = ""
595        for line in unis_dom.toprettyxml().split("\n"):
596            if line.strip() != '' and not line.lstrip().startswith('<?xml '):
597                unis_str += line + "\n"
598       
599        credential = None
600        if credential_xml:
601            credential = get_slice_cred(credential_xml)
602       
603        # Don't do the above on messages with credentials!
604        message = make_UNISTSReplace_message(unis_str, credential)
605        print message
606        print "\n\n"
607        try:
608            client = SimpleClient(host=host, port=port, uri=uri, cert=cert_file, key=key_file)
609            response = client.send_request(message, useSSL=True)
610        except Exception, err:
611            print "Error contacting UNIS: " + str(err)
612            return
613       
614        print "Received:\n"
615        print response
616       
617    except Usage, err:
618        print >>sys.stderr, err.msg
619        print >>sys.stderr, "Usage: <manifest> <slice_urn> [credential]"
620        return 2
621   
622##################################
623# Utility methods
624##################################
625
626def get_slice_cred(fname):
627    f = open(fname, 'r')
628    return f.read()
629
630def make_credential_metadata(cred, metadata_id, metadata_idref):
631    return """
632  <nmwg:metadata id="%s">
633    <nmwg:subject metadataIdRef="%s">
634%s
635    </nmwg:subject>
636    <nmwg:eventType>http://perfsonar.net/ns/protogeni/auth/credential/1</nmwg:eventType>
637  </nmwg:metadata>
638""" % (metadata_id, metadata_idref, cred)
639
640
641def make_UNIS_request(type, eventType, subject="", data_content="", cred=None):
642    metadata_id = 'meta0'
643   
644    cred_metadata = ""
645    if cred:
646        cred_metadata = make_credential_metadata(cred, 'cred0', metadata_id)
647        metadata_id = 'cred0'
648
649    msg="""
650<nmwg:message type="%s" xmlns:nmwg="http://ggf.org/ns/nmwg/base/2.0/">
651  <nmwg:metadata id="meta0">
652%s
653    <nmwg:eventType>%s</nmwg:eventType>
654  </nmwg:metadata>
655%s
656  <nmwg:data id="data0" metadataIdRef="%s">
657%s
658  </nmwg:data>
659</nmwg:message>
660"""
661    return msg % (type, subject, eventType, cred_metadata, metadata_id, data_content)   
662   
663def make_UNISTSQueryAll_message(cred=None):
664    return make_UNIS_request("TSQueryRequest", TS_EVENTTYPE, cred=cred)
665
666def make_UNISTSAdd_message(topology, cred=None):
667    return make_UNIS_request("TSAddRequest", TS_EVENTTYPE,
668                             data_content=topology, cred=cred)
669
670def make_UNISTSReplace_message(topology, cred=None):
671    return make_UNIS_request("TSReplaceRequest", TS_EVENTTYPE,
672                             data_content=topology, cred=cred)
673
674def make_UNISLSQuery_message(xquery=None, cred=None):
675    if xquery is None:
676        xquery = """
677declare namespace nmwg="http://ggf.org/ns/nmwg/base/2.0/";
678/nmwg:store[@type="LSStore"]
679"""
680   
681    subject = """
682    <xquery:subject id="sub1" xmlns:xquery="http://ggf.org/ns/nmwg/tools/org/perfsonar/service/lookup/xquery/1.0/">
683%s
684    </xquery:subject>
685""" % xquery
686   
687    return make_UNIS_request("LSQueryRequest", XQUERY_EVENTTYPE,
688                             subject=subject, cred=cred)
689
690def create_urn(domain, node=None, port=None, link=None, service=None):
691    """
692    Create UNIS URN.
693   
694    Example if domain is udel.edu then the URN is
695    'urn:ogf:network:domain=udel.edu'
696    And if domain is udel.edu and node is stout then the URN is
697    'urn:ogf:network:domain=udel.edu:node=stout'
698    """
699    assert domain, "URN must be fully qualified; no domain provided"
700    urn = "urn:ogf:network:domain=%s" % domain
701   
702    if node != None:
703        urn = urn + ":node=%s" % node
704       
705    if port != None:
706        assert node != None, "URN must be fully qualified; no node given for port"
707        urn = urn + ":port=%s" % port
708   
709    if link != None:
710        assert node == None, "URN must be fully qualified; invalid link urn"
711        urn = urn + ":link=%s" % link
712   
713    if service != None:
714        assert node != None and port == None, "URN must be fully qualified; invalid service urn"
715        urn = urn + ":service=%s" % service
716   
717    return urn
718
719
720# As bizarre as it sounds, (mini)DOM doesn't provide a
721# getElementsByTag that gets from only the immediate children.
722def get_qualified_xml_children(element, ns, name):
723    elements = []
724    for child in element.childNodes:
725        if isinstance(child, dom.Element):
726            if child.namespaceURI == ns and child.localName == name:
727                elements.append(child)
728    return elements
729
730def get_unique_xml_child(element, ns, name):
731    # XXX: Maybe namespace could be None?
732    assert element != None and ns != None and name != None
733   
734    children = get_qualified_xml_children(element, ns, name)
735    if len(children) > 1:
736        raise Exception, element.localName + ": has more than one " + \
737                         name + " element!"
738   
739    if len(children) == 1:
740        return children[0]
741    return None
742
743def create_text_element(dom, ns, name, data, attributes=()):
744    if ns:
745        e = dom.createElementNS(ns, name)
746    else:
747        e = dom.createElement(name)
748   
749    e.appendChild(dom.createTextNode(data))
750   
751    for (attr_name, attr_value) in attributes:
752        e.setAttribute(attr_name, attr_value)
753       
754    return e
755
756def clone_children(node_from, node_to, ignore_list=()):
757    for child in node_from.childNodes:
758        if (child.namespaceURI, child.localName) in ignore_list:
759            continue
760        node_to.appendChild( child.cloneNode(True) )
761
762
763if __name__ == "__main__":
764    sys.exit(main())