Package ganeti :: Module serializer
[hide private]
[frames] | no frames]

Source Code for Module ganeti.serializer

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2007, 2008 Google Inc. 
  5  # All rights reserved. 
  6  # 
  7  # Redistribution and use in source and binary forms, with or without 
  8  # modification, are permitted provided that the following conditions are 
  9  # met: 
 10  # 
 11  # 1. Redistributions of source code must retain the above copyright notice, 
 12  # this list of conditions and the following disclaimer. 
 13  # 
 14  # 2. Redistributions in binary form must reproduce the above copyright 
 15  # notice, this list of conditions and the following disclaimer in the 
 16  # documentation and/or other materials provided with the distribution. 
 17  # 
 18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
 19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 29   
 30  """Serializer abstraction module 
 31   
 32  This module introduces a simple abstraction over the serialization 
 33  backend (currently json). 
 34   
 35  """ 
 36  # pylint: disable=C0103 
 37   
 38  # C0103: Invalid name, since pylint doesn't see that Dump points to a 
 39  # function and not a constant 
 40   
 41  import re 
 42   
 43  # Python 2.6 and above contain a JSON module based on simplejson. Unfortunately 
 44  # the standard library version is significantly slower than the external 
 45  # module. While it should be better from at least Python 3.2 on (see Python 
 46  # issue 7451), for now Ganeti needs to work well with older Python versions 
 47  # too. 
 48  import simplejson 
 49   
 50  from ganeti import errors 
 51  from ganeti import utils 
 52   
 53   
 54  _RE_EOLSP = re.compile("[ \t]+$", re.MULTILINE) 
 55   
 56   
57 -def DumpJson(data):
58 """Serialize a given object. 59 60 @param data: the data to serialize 61 @return: the string representation of data 62 63 """ 64 encoded = simplejson.dumps(data) 65 66 txt = _RE_EOLSP.sub("", encoded) 67 if not txt.endswith("\n"): 68 txt += "\n" 69 70 return txt
71 72
73 -def LoadJson(txt):
74 """Unserialize data from a string. 75 76 @param txt: the json-encoded form 77 78 @return: the original data 79 80 """ 81 return simplejson.loads(txt)
82 83
84 -def DumpSignedJson(data, key, salt=None, key_selector=None):
85 """Serialize a given object and authenticate it. 86 87 @param data: the data to serialize 88 @param key: shared hmac key 89 @param key_selector: name/id that identifies the key (in case there are 90 multiple keys in use, e.g. in a multi-cluster environment) 91 @return: the string representation of data signed by the hmac key 92 93 """ 94 txt = DumpJson(data) 95 if salt is None: 96 salt = "" 97 signed_dict = { 98 "msg": txt, 99 "salt": salt, 100 } 101 102 if key_selector: 103 signed_dict["key_selector"] = key_selector 104 else: 105 key_selector = "" 106 107 signed_dict["hmac"] = utils.Sha1Hmac(key, txt, salt=salt + key_selector) 108 109 return DumpJson(signed_dict)
110 111
112 -def LoadSignedJson(txt, key):
113 """Verify that a given message was signed with the given key, and load it. 114 115 @param txt: json-encoded hmac-signed message 116 @param key: the shared hmac key or a callable taking one argument (the key 117 selector), which returns the hmac key belonging to the key selector. 118 Typical usage is to pass a reference to the get method of a dict. 119 @rtype: tuple of original data, string 120 @return: original data, salt 121 @raises errors.SignatureError: if the message signature doesn't verify 122 123 """ 124 signed_dict = LoadJson(txt) 125 if not isinstance(signed_dict, dict): 126 raise errors.SignatureError("Invalid external message") 127 try: 128 msg = signed_dict["msg"] 129 salt = signed_dict["salt"] 130 hmac_sign = signed_dict["hmac"] 131 except KeyError: 132 raise errors.SignatureError("Invalid external message") 133 134 if callable(key): 135 # pylint: disable=E1103 136 key_selector = signed_dict.get("key_selector", None) 137 hmac_key = key(key_selector) 138 if not hmac_key: 139 raise errors.SignatureError("No key with key selector '%s' found" % 140 key_selector) 141 else: 142 key_selector = "" 143 hmac_key = key 144 145 if not utils.VerifySha1Hmac(hmac_key, msg, hmac_sign, 146 salt=salt + key_selector): 147 raise errors.SignatureError("Invalid Signature") 148 149 return LoadJson(msg), salt
150 151
152 -def LoadAndVerifyJson(raw, verify_fn):
153 """Parses and verifies JSON data. 154 155 @type raw: string 156 @param raw: Input data in JSON format 157 @type verify_fn: callable 158 @param verify_fn: Verification function, usually from L{ht} 159 @return: De-serialized data 160 161 """ 162 try: 163 data = LoadJson(raw) 164 except Exception, err: 165 raise errors.ParseError("Can't parse input data: %s" % err) 166 167 if not verify_fn(data): 168 raise errors.ParseError("Data does not match expected format: %s" % 169 verify_fn) 170 171 return data
172 173 174 Dump = DumpJson 175 Load = LoadJson 176 DumpSigned = DumpSignedJson 177 LoadSigned = LoadSignedJson 178