5 from subprocess import Popen, PIPE, call
6 from tempfile import NamedTemporaryFile
7 from xml.sax.saxutils import escape, quoteattr, XMLGenerator
9 from PLC.Faults import *
10 from PLC.Slices import Slice, Slices
11 from PLC.Nodes import Node, Nodes
12 from PLC.Persons import Person, Persons
13 from PLC.SliceTags import SliceTag, SliceTags
15 from PLC.Methods.GetSliceTicket import GetSliceTicket
17 class PrettyXMLGenerator(XMLGenerator):
19 Adds indentation to the beginning and newlines to the end of
20 opening and closing tags.
23 def __init__(self, out = sys.stdout, encoding = "utf-8", indent = "", addindent = "", newl = ""):
24 XMLGenerator.__init__(self, out, encoding)
25 # XMLGenerator does not export _write()
26 self.write = self.ignorableWhitespace
27 self.indents = [indent]
28 self.addindent = addindent
31 def startDocument(self):
32 XMLGenerator.startDocument(self)
34 def startElement(self, name, attrs, indent = True, newl = True):
36 self.ignorableWhitespace("".join(self.indents))
37 self.indents.append(self.addindent)
39 XMLGenerator.startElement(self, name, attrs)
42 self.ignorableWhitespace(self.newl)
44 def characters(self, content):
47 self.write(escape(content, {
52 def endElement(self, name, indent = True, newl = True):
55 self.ignorableWhitespace("".join(self.indents))
57 XMLGenerator.endElement(self, name)
60 self.ignorableWhitespace(self.newl)
62 def simpleElement(self, name, attrs = {}, indent = True, newl = True):
64 self.ignorableWhitespace("".join(self.indents))
66 self.write('<' + name)
67 for (name, value) in attrs.items():
68 self.write(' %s=%s' % (name, quoteattr(value)))
72 self.ignorableWhitespace(self.newl)
74 class SliceGetTicket(GetSliceTicket):
76 Deprecated. See GetSliceTicket.
78 Warning: This function exists solely for backward compatibility
79 with the old public PlanetLab 3.0 Node Manager, which will be
80 removed from service by 2007. This call is not intended to be used
81 by any other PLC except the public PlanetLab.
86 def call(self, auth, slice_id_or_name):
87 slices = Slices(self.api, [slice_id_or_name])
89 raise PLCInvalidArgument, "No such slice"
92 # Allow peers to obtain tickets for their own slices
93 if slice['peer_id'] is not None:
94 raise PLCInvalidArgument, "Not a local slice"
96 if slice['instantiation'] != 'delegated':
97 raise PLCInvalidArgument, "Not in delegated state"
99 nodes = Nodes(self.api, slice['node_ids']).dict()
100 persons = Persons(self.api, slice['person_ids']).dict()
101 slice_tags = SliceTags(self.api, slice['slice_tag_ids']).dict()
103 ticket = NamedTemporaryFile()
105 xml = PrettyXMLGenerator(out = ticket, encoding = self.api.encoding, indent = "", addindent = " ", newl = "\n")
109 xml.startElement('ticket', {})
111 # <slice name="site_slice" id="12345" expiry="1138712648">
112 xml.startElement('slice',
113 {'id': str(slice['slice_id']),
114 'name': unicode(slice['name']),
115 'expiry': unicode(int(slice['expires']))})
118 xml.startElement('nodes', {})
119 for node_id in slice['node_ids']:
120 if not nodes.has_key(node_id):
122 node = nodes[node_id]
123 # <node id="12345" hostname="node.site.domain"/>
124 xml.simpleElement('node',
125 {'id': str(node['node_id']),
126 'hostname': unicode(node['hostname'])})
128 xml.endElement('nodes')
131 xml.startElement('users', {})
132 for person_id in slice['person_ids']:
133 if not persons.has_key(person_id):
135 user = persons[person_id]
136 # <user person_id="12345" email="user@site.domain"/>
137 xml.simpleElement('user',
138 {'person_id': unicode(user['person_id']),
139 'email': unicode(user['email'])})
141 xml.endElement('users')
144 xml.startElement('rspec', {})
145 for slice_tag_id in slice['slice_tag_ids']:
146 if not slice_tags.has_key(slice_tag_id):
148 slice_tag = slice_tags[slice_tag_id]
150 name = slice_tag['name']
151 value = slice_tag['value']
153 def kbps_to_bps(kbps):
154 bps = int(kbps) * 1000
157 def max_kbyte_to_bps(max_kbyte):
158 bps = int(max_kbyte) * 1000 * 8 / 24 / 60 / 60
161 # XXX Used to support multiple named values for each attribute type
163 'cpu_share': ('nm_cpu_share', 'cpu_share', 'integer', int),
165 'net_share': ('nm_net_share', 'rate', 'integer', int),
166 'net_min_rate': ('nm_net_min_rate', 'rate', 'integer', int),
167 'net_max_rate': ('nm_net_max_rate', 'rate', 'integer', int),
168 'net_max_kbyte': ('nm_net_avg_rate', 'rate', 'integer', max_kbyte_to_bps),
170 'net_i2_share': ('nm_net_exempt_share', 'rate', 'integer', int),
171 'net_i2_min_rate': ('nm_net_exempt_min_rate', 'rate', 'integer', kbps_to_bps),
172 'net_i2_max_rate': ('nm_net_exempt_max_rate', 'rate', 'integer', kbps_to_bps),
173 'net_i2_max_kbyte': ('nm_net_exempt_avg_rate', 'rate', 'integer', max_kbyte_to_bps),
175 'disk_max': ('nm_disk_quota', 'quota', 'integer', int),
176 'plc_agent_version': ('plc_agent_version', 'version', 'string', str),
177 'plc_slice_type': ('plc_slice_type', 'type', 'string', str),
178 'plc_ticket_pubkey': ('plc_ticket_pubkey', 'key', 'string', str),
181 if name == 'initscript':
182 (attribute_name, value_name, type) = ('initscript', 'initscript_id', 'integer')
183 value = slice_tag['slice_tag_id']
184 elif name in name_type_cast:
185 (attribute_name, value_name, type, cast) = name_type_cast[name]
188 attribute_name = value_name = name
191 # <resource name="tag_type">
192 xml.startElement('resource', {'name': unicode(attribute_name)})
194 # <value name="element_name" type="element_type">
195 xml.startElement('value',
196 {'name': unicode(value_name),
200 xml.characters(unicode(value))
202 xml.endElement('value', indent = False)
205 xml.endElement('resource')
207 xml.endElement('rspec')
210 xml.endElement('slice')
212 # Add signature template
213 xml.startElement('Signature', {'xmlns': "http://www.w3.org/2000/09/xmldsig#"})
214 xml.startElement('SignedInfo', {})
215 xml.simpleElement('CanonicalizationMethod', {'Algorithm': "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"})
216 xml.simpleElement('SignatureMethod', {'Algorithm': "http://www.w3.org/2000/09/xmldsig#rsa-sha1"})
217 xml.startElement('Reference', {'URI': ""})
218 xml.startElement('Transforms', {})
219 xml.simpleElement('Transform', {'Algorithm': "http://www.w3.org/2000/09/xmldsig#enveloped-signature"})
220 xml.endElement('Transforms')
221 xml.simpleElement('DigestMethod', {'Algorithm': "http://www.w3.org/2000/09/xmldsig#sha1"})
222 xml.simpleElement('DigestValue', {})
223 xml.endElement('Reference')
224 xml.endElement('SignedInfo')
225 xml.simpleElement('SignatureValue', {})
226 xml.endElement('Signature')
228 xml.endElement('ticket')
231 if not hasattr(self.api.config, 'PLC_API_TICKET_KEY') or \
232 not os.path.exists(self.api.config.PLC_API_TICKET_KEY):
233 raise PLCAPIError, "Slice ticket signing key not found"
238 p = Popen(["xmlsec1", "--sign",
239 "--privkey-pem", self.api.config.PLC_API_TICKET_KEY,
241 stdin = PIPE, stdout = PIPE, stderr = PIPE, close_fds = True)
242 signed_ticket = p.stdout.read()
243 err = p.stderr.read()
249 raise PLCAPIError, err