Change bdev.LogicalVolume.GetPVInfo usage
[ganeti-local] / lib / serializer.py
1 #
2 #
3
4 # Copyright (C) 2007, 2008 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21 """Serializer abstraction module
22
23 This module introduces a simple abstraction over the serialization
24 backend (currently json).
25
26 """
27
28 import simplejson
29 import re
30 import hmac
31
32 from ganeti import errors
33
34 try:
35   from hashlib import sha1
36 except ImportError:
37   import sha as sha1
38
39
40 _JSON_INDENT = 2
41
42 _RE_EOLSP = re.compile('[ \t]+$', re.MULTILINE)
43
44
45 def _GetJsonDumpers():
46   """Returns two JSON functions to serialize data.
47
48   @rtype: (callable, callable)
49   @return: The function to generate a compact form of JSON and another one to
50            generate a more readable, indented form of JSON (if supported)
51
52   """
53   plain_dump = simplejson.dumps
54
55   # Check whether the simplejson module supports indentation
56   try:
57     simplejson.dumps(1, indent=_JSON_INDENT)
58   except TypeError:
59     # Indentation not supported
60     indent_dump = plain_dump
61   else:
62     # Indentation supported
63     indent_dump = lambda data: simplejson.dumps(data, indent=_JSON_INDENT)
64
65   assert callable(plain_dump)
66   assert callable(indent_dump)
67
68   return (plain_dump, indent_dump)
69
70
71 (_DumpJson, _DumpJsonIndent) = _GetJsonDumpers()
72
73
74 def DumpJson(data, indent=True):
75   """Serialize a given object.
76
77   @param data: the data to serialize
78   @param indent: whether to indent output (depends on simplejson version)
79
80   @return: the string representation of data
81
82   """
83   if indent:
84     fn = _DumpJsonIndent
85   else:
86     fn = _DumpJson
87
88   txt = _RE_EOLSP.sub("", fn(data))
89   if not txt.endswith('\n'):
90     txt += '\n'
91
92   return txt
93
94
95 def LoadJson(txt):
96   """Unserialize data from a string.
97
98   @param txt: the json-encoded form
99
100   @return: the original data
101
102   """
103   return simplejson.loads(txt)
104
105
106 def DumpSignedJson(data, key, salt=None):
107   """Serialize a given object and authenticate it.
108
109   @param data: the data to serialize
110   @param key: shared hmac key
111   @return: the string representation of data signed by the hmac key
112
113   """
114   txt = DumpJson(data, indent=False)
115   if salt is None:
116     salt = ''
117   signed_dict = {
118     'msg': txt,
119     'salt': salt,
120     'hmac': hmac.new(key, salt + txt, sha1).hexdigest(),
121   }
122   return DumpJson(signed_dict, indent=False)
123
124
125 def LoadSignedJson(txt, key):
126   """Verify that a given message was signed with the given key, and load it.
127
128   @param txt: json-encoded hmac-signed message
129   @param key: shared hmac key
130   @rtype: tuple of original data, string
131   @return: original data, salt
132   @raises errors.SignatureError: if the message signature doesn't verify
133
134   """
135   signed_dict = LoadJson(txt)
136   if not isinstance(signed_dict, dict):
137     raise errors.SignatureError('Invalid external message')
138   try:
139     msg = signed_dict['msg']
140     salt = signed_dict['salt']
141     hmac_sign = signed_dict['hmac']
142   except KeyError:
143     raise errors.SignatureError('Invalid external message')
144
145   if hmac.new(key, salt + msg, sha1).hexdigest() != hmac_sign:
146     raise errors.SignatureError('Invalid Signature')
147
148   return LoadJson(msg), salt
149
150
151 Dump = DumpJson
152 Load = LoadJson
153 DumpSigned = DumpSignedJson
154 LoadSigned = LoadSignedJson