3 from subprocess import Popen, PIPE, call
4 from tempfile import NamedTemporaryFile
5 from xml.sax.saxutils import escape, quoteattr, XMLGenerator
7 from PLC.Faults import *
8 from PLC.Slices import Slice, Slices
9 from PLC.Nodes import Node, Nodes
10 from PLC.Persons import Person, Persons
11 from PLC.SliceTags import SliceTag, SliceTags
13 from PLC.Methods.GetSliceTicket import GetSliceTicket
15 class PrettyXMLGenerator(XMLGenerator):
17 Adds indentation to the beginning and newlines to the end of
18 opening and closing tags.
21 def __init__(self, out = sys.stdout, encoding = "utf-8", indent = "", addindent = "", newl = ""):
22 XMLGenerator.__init__(self, out, encoding)
23 # XMLGenerator does not export _write()
24 self.write = self.ignorableWhitespace
25 self.indents = [indent]
26 self.addindent = addindent
29 def startDocument(self):
30 XMLGenerator.startDocument(self)
32 def startElement(self, name, attrs, indent = True, newl = True):
34 self.ignorableWhitespace("".join(self.indents))
35 self.indents.append(self.addindent)
37 XMLGenerator.startElement(self, name, attrs)
40 self.ignorableWhitespace(self.newl)
42 def characters(self, content):
45 self.write(escape(content, {
50 def endElement(self, name, indent = True, newl = True):
53 self.ignorableWhitespace("".join(self.indents))
55 XMLGenerator.endElement(self, name)
58 self.ignorableWhitespace(self.newl)
60 def simpleElement(self, name, attrs = {}, indent = True, newl = True):
62 self.ignorableWhitespace("".join(self.indents))
64 self.write('<' + name)
65 for (name, value) in list(attrs.items()):
66 self.write(' %s=%s' % (name, quoteattr(value)))
70 self.ignorableWhitespace(self.newl)
72 class SliceGetTicket(GetSliceTicket):
74 Deprecated. See GetSliceTicket.
76 Warning: This function exists solely for backward compatibility
77 with the old public PlanetLab 3.0 Node Manager, which will be
78 removed from service by 2007. This call is not intended to be used
79 by any other PLC except the public PlanetLab.
84 def call(self, auth, slice_id_or_name):
85 slices = Slices(self.api, [slice_id_or_name])
87 raise PLCInvalidArgument("No such slice")
90 # Allow peers to obtain tickets for their own slices
91 if slice['peer_id'] is not None:
92 raise PLCInvalidArgument("Not a local slice")
94 if slice['instantiation'] != 'delegated':
95 raise PLCInvalidArgument("Not in delegated state")
97 nodes = Nodes(self.api, slice['node_ids']).dict()
98 persons = Persons(self.api, slice['person_ids']).dict()
99 slice_tags = SliceTags(self.api, slice['slice_tag_ids']).dict()
101 ticket = NamedTemporaryFile()
103 xml = PrettyXMLGenerator(out = ticket, encoding = self.api.encoding, indent = "", addindent = " ", newl = "\n")
107 xml.startElement('ticket', {})
109 # <slice name="site_slice" id="12345" expiry="1138712648">
110 xml.startElement('slice',
111 {'id': str(slice['slice_id']),
112 'name': str(slice['name']),
113 'expiry': str(int(slice['expires']))})
116 xml.startElement('nodes', {})
117 for node_id in slice['node_ids']:
118 if node_id not in nodes:
120 node = nodes[node_id]
121 # <node id="12345" hostname="node.site.domain"/>
122 xml.simpleElement('node',
123 {'id': str(node['node_id']),
124 'hostname': str(node['hostname'])})
126 xml.endElement('nodes')
129 xml.startElement('users', {})
130 for person_id in slice['person_ids']:
131 if person_id not in persons:
133 user = persons[person_id]
134 # <user person_id="12345" email="user@site.domain"/>
135 xml.simpleElement('user',
136 {'person_id': str(user['person_id']),
137 'email': str(user['email'])})
139 xml.endElement('users')
142 xml.startElement('rspec', {})
143 for slice_tag_id in slice['slice_tag_ids']:
144 if slice_tag_id not in slice_tags:
146 slice_tag = slice_tags[slice_tag_id]
148 name = slice_tag['name']
149 value = slice_tag['value']
151 def kbps_to_bps(kbps):
152 bps = int(kbps) * 1000
155 def max_kbyte_to_bps(max_kbyte):
156 bps = int(max_kbyte) * 1000 * 8 / 24 / 60 / 60
159 # XXX Used to support multiple named values for each attribute type
161 'cpu_share': ('nm_cpu_share', 'cpu_share', 'integer', int),
163 'net_share': ('nm_net_share', 'rate', 'integer', int),
164 'net_min_rate': ('nm_net_min_rate', 'rate', 'integer', int),
165 'net_max_rate': ('nm_net_max_rate', 'rate', 'integer', int),
166 'net_max_kbyte': ('nm_net_avg_rate', 'rate', 'integer', max_kbyte_to_bps),
168 'net_i2_share': ('nm_net_exempt_share', 'rate', 'integer', int),
169 'net_i2_min_rate': ('nm_net_exempt_min_rate', 'rate', 'integer', kbps_to_bps),
170 'net_i2_max_rate': ('nm_net_exempt_max_rate', 'rate', 'integer', kbps_to_bps),
171 'net_i2_max_kbyte': ('nm_net_exempt_avg_rate', 'rate', 'integer', max_kbyte_to_bps),
173 'disk_max': ('nm_disk_quota', 'quota', 'integer', int),
174 'plc_agent_version': ('plc_agent_version', 'version', 'string', str),
175 'plc_slice_type': ('plc_slice_type', 'type', 'string', str),
176 'plc_ticket_pubkey': ('plc_ticket_pubkey', 'key', 'string', str),
179 if name == 'initscript':
180 (attribute_name, value_name, type) = ('initscript', 'initscript_id', 'integer')
181 value = slice_tag['slice_tag_id']
182 elif name in name_type_cast:
183 (attribute_name, value_name, type, cast) = name_type_cast[name]
186 attribute_name = value_name = name
189 # <resource name="tag_type">
190 xml.startElement('resource', {'name': str(attribute_name)})
192 # <value name="element_name" type="element_type">
193 xml.startElement('value',
194 {'name': str(value_name),
198 xml.characters(str(value))
200 xml.endElement('value', indent = False)
203 xml.endElement('resource')
205 xml.endElement('rspec')
208 xml.endElement('slice')
210 # Add signature template
211 xml.startElement('Signature', {'xmlns': "http://www.w3.org/2000/09/xmldsig#"})
212 xml.startElement('SignedInfo', {})
213 xml.simpleElement('CanonicalizationMethod', {'Algorithm': "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"})
214 xml.simpleElement('SignatureMethod', {'Algorithm': "http://www.w3.org/2000/09/xmldsig#rsa-sha1"})
215 xml.startElement('Reference', {'URI': ""})
216 xml.startElement('Transforms', {})
217 xml.simpleElement('Transform', {'Algorithm': "http://www.w3.org/2000/09/xmldsig#enveloped-signature"})
218 xml.endElement('Transforms')
219 xml.simpleElement('DigestMethod', {'Algorithm': "http://www.w3.org/2000/09/xmldsig#sha1"})
220 xml.simpleElement('DigestValue', {})
221 xml.endElement('Reference')
222 xml.endElement('SignedInfo')
223 xml.simpleElement('SignatureValue', {})
224 xml.endElement('Signature')
226 xml.endElement('ticket')
229 if not hasattr(self.api.config, 'PLC_API_TICKET_KEY') or \
230 not os.path.exists(self.api.config.PLC_API_TICKET_KEY):
231 raise PLCAPIError("Slice ticket signing key not found")
236 p = Popen(["xmlsec1", "--sign",
237 "--privkey-pem", self.api.config.PLC_API_TICKET_KEY,
239 stdin = PIPE, stdout = PIPE, stderr = PIPE, close_fds = True)
240 signed_ticket = p.stdout.read()
241 err = p.stderr.read()
247 raise PLCAPIError(err)