4 from subprocess import Popen, PIPE, call
5 from tempfile import NamedTemporaryFile
6 from xml.sax.saxutils import escape, quoteattr, XMLGenerator
8 from PLC.Faults import *
9 from PLC.Slices import Slice, Slices
10 from PLC.Nodes import Node, Nodes
11 from PLC.Persons import Person, Persons
12 from PLC.SliceAttributes import SliceAttribute, SliceAttributes
14 from PLC.Methods.GetSliceTicket import GetSliceTicket
16 class PrettyXMLGenerator(XMLGenerator):
18 Adds indentation to the beginning and newlines to the end of
19 opening and closing tags.
22 def __init__(self, out = sys.stdout, encoding = "utf-8", indent = "", addindent = "", newl = ""):
23 XMLGenerator.__init__(self, out, encoding)
24 # XMLGenerator does not export _write()
25 self.write = self.ignorableWhitespace
26 self.indents = [indent]
27 self.addindent = addindent
30 def startDocument(self):
31 XMLGenerator.startDocument(self)
33 def startElement(self, name, attrs, indent = True, newl = True):
35 self.ignorableWhitespace("".join(self.indents))
36 self.indents.append(self.addindent)
38 XMLGenerator.startElement(self, name, attrs)
41 self.ignorableWhitespace(self.newl)
43 def characters(self, content):
46 self.write(escape(content, {
51 def endElement(self, name, indent = True, newl = True):
54 self.ignorableWhitespace("".join(self.indents))
56 XMLGenerator.endElement(self, name)
59 self.ignorableWhitespace(self.newl)
61 def simpleElement(self, name, attrs = {}, indent = True, newl = True):
63 self.ignorableWhitespace("".join(self.indents))
65 self.write('<' + name)
66 for (name, value) in attrs.items():
67 self.write(' %s=%s' % (name, quoteattr(value)))
71 self.ignorableWhitespace(self.newl)
73 class SliceGetTicket(GetSliceTicket):
75 Deprecated. See GetSliceTicket.
77 Warning: This function exists solely for backward compatibility
78 with the old public PlanetLab 3.0 Node Manager, which will be
79 removed from service by 2007. This call is not intended to be used
80 by any other PLC except the public PlanetLab.
85 def call(self, auth, slice_id_or_name):
86 slices = Slices(self.api, [slice_id_or_name])
88 raise PLCInvalidArgument, "No such slice"
91 # Allow peers to obtain tickets for their own slices
92 if slice['peer_id'] is not None:
93 raise PLCInvalidArgument, "Not a local slice"
95 if slice['instantiation'] != 'delegated':
96 raise PLCInvalidArgument, "Not in delegated state"
98 nodes = Nodes(self.api, slice['node_ids']).dict()
99 persons = Persons(self.api, slice['person_ids']).dict()
100 slice_attributes = SliceAttributes(self.api, slice['slice_attribute_ids']).dict()
102 ticket = NamedTemporaryFile()
104 xml = PrettyXMLGenerator(out = ticket, encoding = self.api.encoding, indent = "", addindent = " ", newl = "\n")
108 xml.startElement('ticket', {})
110 # <slice name="site_slice" id="12345" expiry="1138712648">
111 xml.startElement('slice',
112 {'id': str(slice['slice_id']),
113 'name': unicode(slice['name']),
114 'expiry': unicode(int(slice['expires']))})
117 xml.startElement('nodes', {})
118 for node_id in slice['node_ids']:
119 if not nodes.has_key(node_id):
121 node = nodes[node_id]
122 # <node id="12345" hostname="node.site.domain"/>
123 xml.simpleElement('node',
124 {'id': str(node['node_id']),
125 'hostname': unicode(node['hostname'])})
127 xml.endElement('nodes')
130 xml.startElement('users', {})
131 for person_id in slice['person_ids']:
132 if not persons.has_key(person_id):
134 user = persons[person_id]
135 # <user person_id="12345" email="user@site.domain"/>
136 xml.simpleElement('user',
137 {'person_id': unicode(user['person_id']),
138 'email': unicode(user['email'])})
140 xml.endElement('users')
143 xml.startElement('rspec', {})
144 for slice_attribute_id in slice['slice_attribute_ids']:
145 if not slice_attributes.has_key(slice_attribute_id):
147 slice_attribute = slice_attributes[slice_attribute_id]
149 name = slice_attribute['name']
150 value = slice_attribute['value']
152 def kbps_to_bps(kbps):
153 bps = int(kbps) * 1000
156 def max_kbyte_to_bps(max_kbyte):
157 bps = int(max_kbyte) * 1000 * 8 / 24 / 60 / 60
160 # XXX Used to support multiple named values for each attribute type
162 'cpu_share': ('nm_cpu_share', 'cpu_share', 'integer', int),
164 'net_share': ('nm_net_share', 'rate', 'integer', int),
165 'net_min_rate': ('nm_net_min_rate', 'rate', 'integer', int),
166 'net_max_rate': ('nm_net_max_rate', 'rate', 'integer', int),
167 'net_max_kbyte': ('nm_net_avg_rate', 'rate', 'integer', max_kbyte_to_bps),
169 'net_i2_share': ('nm_net_exempt_share', 'rate', 'integer', int),
170 'net_i2_min_rate': ('nm_net_exempt_min_rate', 'rate', 'integer', kbps_to_bps),
171 'net_i2_max_rate': ('nm_net_exempt_max_rate', 'rate', 'integer', kbps_to_bps),
172 'net_i2_max_kbyte': ('nm_net_exempt_avg_rate', 'rate', 'integer', max_kbyte_to_bps),
174 'disk_max': ('nm_disk_quota', 'quota', 'integer', int),
175 'plc_agent_version': ('plc_agent_version', 'version', 'string', str),
176 'plc_slice_type': ('plc_slice_type', 'type', 'string', str),
177 'plc_ticket_pubkey': ('plc_ticket_pubkey', 'key', 'string', str),
180 if name == 'initscript':
181 (attribute_name, value_name, type) = ('initscript', 'initscript_id', 'integer')
182 value = slice_attribute['slice_attribute_id']
183 elif name in name_type_cast:
184 (attribute_name, value_name, type, cast) = name_type_cast[name]
187 attribute_name = value_name = name
190 # <resource name="tag_type">
191 xml.startElement('resource', {'name': unicode(attribute_name)})
193 # <value name="element_name" type="element_type">
194 xml.startElement('value',
195 {'name': unicode(value_name),
199 xml.characters(unicode(value))
201 xml.endElement('value', indent = False)
204 xml.endElement('resource')
206 xml.endElement('rspec')
209 xml.endElement('slice')
211 # Add signature template
212 xml.startElement('Signature', {'xmlns': "http://www.w3.org/2000/09/xmldsig#"})
213 xml.startElement('SignedInfo', {})
214 xml.simpleElement('CanonicalizationMethod', {'Algorithm': "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"})
215 xml.simpleElement('SignatureMethod', {'Algorithm': "http://www.w3.org/2000/09/xmldsig#rsa-sha1"})
216 xml.startElement('Reference', {'URI': ""})
217 xml.startElement('Transforms', {})
218 xml.simpleElement('Transform', {'Algorithm': "http://www.w3.org/2000/09/xmldsig#enveloped-signature"})
219 xml.endElement('Transforms')
220 xml.simpleElement('DigestMethod', {'Algorithm': "http://www.w3.org/2000/09/xmldsig#sha1"})
221 xml.simpleElement('DigestValue', {})
222 xml.endElement('Reference')
223 xml.endElement('SignedInfo')
224 xml.simpleElement('SignatureValue', {})
225 xml.endElement('Signature')
227 xml.endElement('ticket')
230 if not hasattr(self.api.config, 'PLC_API_TICKET_KEY') or \
231 not os.path.exists(self.api.config.PLC_API_TICKET_KEY):
232 raise PLCAPIError, "Slice ticket signing key not found"
237 p = Popen(["xmlsec1", "--sign",
238 "--privkey-pem", self.api.config.PLC_API_TICKET_KEY,
240 stdin = PIPE, stdout = PIPE, stderr = PIPE, close_fds = True)
241 signed_ticket = p.stdout.read()
242 err = p.stderr.read()
248 raise PLCAPIError, err