Revision 4884f187
b/lib/cli.py | ||
---|---|---|
44 | 44 |
from ganeti import qlang |
45 | 45 |
from ganeti import objects |
46 | 46 |
from ganeti import pathutils |
47 |
from ganeti import serializer |
|
47 | 48 |
|
48 | 49 |
from ganeti.runtime import (GetClient) |
49 | 50 |
|
... | ... | |
679 | 680 |
return _SplitKeyVal(opt, value, True) |
680 | 681 |
|
681 | 682 |
|
683 |
def check_key_private_val(option, opt, value): # pylint: disable=W0613 |
|
684 |
"""Custom parser class for private and secret key=val,key=val options. |
|
685 |
|
|
686 |
This will store the parsed values as a dict {key: val}. |
|
687 |
|
|
688 |
""" |
|
689 |
return serializer.PrivateDict(_SplitKeyVal(opt, value, True)) |
|
690 |
|
|
691 |
|
|
682 | 692 |
def _SplitListKeyVal(opt, value): |
683 | 693 |
retval = {} |
684 | 694 |
for elem in value.split("/"): |
... | ... | |
781 | 791 |
"multilistidentkeyval", |
782 | 792 |
"identkeyval", |
783 | 793 |
"keyval", |
794 |
"keyprivateval", |
|
784 | 795 |
"unit", |
785 | 796 |
"bool", |
786 | 797 |
"list", |
... | ... | |
790 | 801 |
TYPE_CHECKER["multilistidentkeyval"] = check_multilist_ident_key_val |
791 | 802 |
TYPE_CHECKER["identkeyval"] = check_ident_key_val |
792 | 803 |
TYPE_CHECKER["keyval"] = check_key_val |
804 |
TYPE_CHECKER["keyprivateval"] = check_key_private_val |
|
793 | 805 |
TYPE_CHECKER["unit"] = check_unit |
794 | 806 |
TYPE_CHECKER["bool"] = check_bool |
795 | 807 |
TYPE_CHECKER["list"] = check_list |
b/lib/config.py | ||
---|---|---|
2488 | 2488 |
if destination is None: |
2489 | 2489 |
destination = self._cfg_file |
2490 | 2490 |
self._BumpSerialNo() |
2491 |
txt = serializer.Dump(self._config_data.ToDict()) |
|
2491 |
txt = serializer.DumpJson( |
|
2492 |
self._config_data.ToDict(_with_private=True), |
|
2493 |
private_encoder=serializer.EncodeWithPrivateFields |
|
2494 |
) |
|
2492 | 2495 |
|
2493 | 2496 |
getents = self._getents() |
2494 | 2497 |
try: |
b/lib/ht.py | ||
---|---|---|
29 | 29 |
from ganeti import utils |
30 | 30 |
from ganeti import constants |
31 | 31 |
from ganeti import objects |
32 |
|
|
32 |
from ganeti.serializer import Private |
|
33 | 33 |
|
34 | 34 |
_PAREN_RE = re.compile("^[a-zA-Z0-9_-]+$") |
35 | 35 |
|
... | ... | |
77 | 77 |
def __str__(self): |
78 | 78 |
return self._text |
79 | 79 |
|
80 |
def __repr__(self): |
|
81 |
return "<%s %r>" % (self._text, self._fn) |
|
82 |
|
|
80 | 83 |
|
81 | 84 |
class _CommentWrapper(_WrapperBase): |
82 | 85 |
"""Wrapper class for comment. |
... | ... | |
269 | 272 |
def TDict(val): |
270 | 273 |
"""Checks if the given value is a dictionary. |
271 | 274 |
|
275 |
Note that L{PrivateDict}s subclass dict and pass this check. |
|
276 |
|
|
272 | 277 |
""" |
273 | 278 |
return isinstance(val, dict) |
274 | 279 |
|
... | ... | |
416 | 421 |
return desc(lambda val: isinstance(val, cls)) |
417 | 422 |
|
418 | 423 |
|
424 |
def TPrivate(val_type): |
|
425 |
"""Checks if a given value is an instance of Private. |
|
426 |
|
|
427 |
""" |
|
428 |
def fn(val): |
|
429 |
return isinstance(val, Private) and val_type(val.Get()) |
|
430 |
|
|
431 |
desc = WithDesc("Private %s" % Parens(val_type)) |
|
432 |
|
|
433 |
return desc(fn) |
|
434 |
|
|
435 |
|
|
419 | 436 |
def TListOf(my_type): |
420 | 437 |
"""Checks if a given value is a list with all elements of the same type. |
421 | 438 |
|
b/lib/serializer.py | ||
---|---|---|
166 | 166 |
Load = LoadJson |
167 | 167 |
DumpSigned = DumpSignedJson |
168 | 168 |
LoadSigned = LoadSignedJson |
169 |
|
|
170 |
|
|
171 |
class Private(object): |
|
172 |
"""Wrap a value so it is hard to leak it accidentally. |
|
173 |
|
|
174 |
>>> x = Private("foo") |
|
175 |
>>> print "Value: %s" % x |
|
176 |
Value: <redacted> |
|
177 |
>>> print "Value: {0}".format(x) |
|
178 |
Value: <redacted> |
|
179 |
>>> x.upper() == "FOO" |
|
180 |
True |
|
181 |
|
|
182 |
""" |
|
183 |
def __init__(self, item, descr="redacted"): |
|
184 |
if isinstance(item, Private): |
|
185 |
raise ValueError("Attempted to nest Private values.") |
|
186 |
self._item = item |
|
187 |
self._descr = descr |
|
188 |
|
|
189 |
def Get(self): |
|
190 |
"Return the wrapped value." |
|
191 |
return self._item |
|
192 |
|
|
193 |
def __str__(self): |
|
194 |
return "<{._descr}>".format(self) |
|
195 |
|
|
196 |
def __repr__(self): |
|
197 |
return "Private(?, descr='{._descr}')".format(self) |
|
198 |
|
|
199 |
# pylint: disable=W0212 |
|
200 |
# If it doesn't access _item directly, the call will go through __getattr__ |
|
201 |
# because this class defines __slots__ and "item" is not in it. |
|
202 |
# OTOH, if we do add it there, we'd risk shadowing an "item" attribute. |
|
203 |
def __eq__(self, other): |
|
204 |
if isinstance(other, Private): |
|
205 |
return self._item == other._item |
|
206 |
else: |
|
207 |
return self._item == other |
|
208 |
|
|
209 |
def __hash__(self): |
|
210 |
return hash(self._item) |
|
211 |
|
|
212 |
def __format__(self, *_1, **_2): |
|
213 |
return self.__str__() |
|
214 |
|
|
215 |
def __getattr__(self, attr): |
|
216 |
return Private(getattr(self._item, attr), |
|
217 |
descr="%s.%s" % (self._descr, attr)) |
|
218 |
|
|
219 |
def __call__(self, *args, **kwargs): |
|
220 |
return Private(self._item(*args, **kwargs), |
|
221 |
descr="%s()" % self._descr) |
|
222 |
|
|
223 |
# pylint: disable=R0201 |
|
224 |
# While this could get away with being a function, it needs to be a method. |
|
225 |
# Required by the copy.deepcopy function used by FillDict. |
|
226 |
def __getnewargs__(self): |
|
227 |
return tuple() |
|
228 |
|
|
229 |
def __nonzero__(self): |
|
230 |
return bool(self._item) |
|
231 |
|
|
232 |
# Get in the way of Pickle by implementing __slots__ but not __getstate__ |
|
233 |
# ...and get a performance boost, too. |
|
234 |
__slots__ = ["_item", "_descr"] |
|
235 |
|
|
236 |
|
|
237 |
class PrivateDict(dict): |
|
238 |
"""A dictionary that turns its values to private fields. |
|
239 |
|
|
240 |
>>> PrivateDict() |
|
241 |
{} |
|
242 |
>>> supersekkrit = PrivateDict({"password": "foobar"}) |
|
243 |
>>> print supersekkrit["password"] |
|
244 |
<password> |
|
245 |
>>> supersekkrit["password"].Get() |
|
246 |
'foobar' |
|
247 |
>>> supersekkrit.GetPrivate("password") |
|
248 |
'foobar' |
|
249 |
>>> supersekkrit["user"] = "eggspam" |
|
250 |
>>> supersekkrit.Unprivate() |
|
251 |
{'password': 'foobar', 'user': 'eggspam'} |
|
252 |
|
|
253 |
""" |
|
254 |
def __init__(self, data=None): |
|
255 |
dict.__init__(self) |
|
256 |
self.update(data) |
|
257 |
|
|
258 |
def __setitem__(self, item, value): |
|
259 |
if not isinstance(value, Private): |
|
260 |
if not isinstance(item, dict): |
|
261 |
value = Private(value, descr=item) |
|
262 |
else: |
|
263 |
value = PrivateDict(value) |
|
264 |
dict.__setitem__(self, item, value) |
|
265 |
|
|
266 |
# The actual conversion to Private containers is done by __setitem__ |
|
267 |
|
|
268 |
# copied straight from cpython/Lib/UserDict.py |
|
269 |
# Copyright (c) 2001-2014 Python Software Foundation; All Rights Reserved |
|
270 |
def update(self, other=None, **kwargs): |
|
271 |
# Make progressively weaker assumptions about "other" |
|
272 |
if other is None: |
|
273 |
pass |
|
274 |
elif hasattr(other, 'iteritems'): # iteritems saves memory and lookups |
|
275 |
for k, v in other.iteritems(): |
|
276 |
self[k] = v |
|
277 |
elif hasattr(other, 'keys'): |
|
278 |
for k in other.keys(): |
|
279 |
self[k] = other[k] |
|
280 |
else: |
|
281 |
for k, v in other: |
|
282 |
self[k] = v |
|
283 |
if kwargs: |
|
284 |
self.update(kwargs) |
|
285 |
|
|
286 |
def GetPrivate(self, *args): |
|
287 |
"""Like dict.get, but extracting the value in the process. |
|
288 |
|
|
289 |
Arguments are semantically equivalent to ``dict.get`` |
|
290 |
|
|
291 |
>>> PrivateDict({"foo": "bar"}).GetPrivate("foo") |
|
292 |
'bar' |
|
293 |
>>> PrivateDict({"foo": "bar"}).GetPrivate("baz", "spam") |
|
294 |
'spam' |
|
295 |
|
|
296 |
""" |
|
297 |
if len(args) == 1: |
|
298 |
key, = args |
|
299 |
return self[key].Get() |
|
300 |
elif len(args) == 2: |
|
301 |
key, default = args |
|
302 |
if key not in self: |
|
303 |
return default |
|
304 |
else: |
|
305 |
return self[key].Get() |
|
306 |
else: |
|
307 |
raise TypeError("GetPrivate() takes 2 arguments (%d given)" % len(args)) |
|
308 |
|
|
309 |
def Unprivate(self): |
|
310 |
"""Turn this dict of Private() values to a dict of values. |
|
311 |
|
|
312 |
>>> PrivateDict({"foo": "bar"}).Unprivate() |
|
313 |
{'foo': 'bar'} |
|
314 |
|
|
315 |
@rtype: dict |
|
316 |
|
|
317 |
""" |
|
318 |
returndict = {} |
|
319 |
for key in self: |
|
320 |
returndict[key] = self[key].Get() |
|
321 |
return returndict |
b/src/Ganeti/JSON.hs | ||
---|---|---|
48 | 48 |
, toArray |
49 | 49 |
, optionalJSField |
50 | 50 |
, optFieldsToObj |
51 |
, readContainer |
|
51 | 52 |
, HasStringRepr(..) |
52 | 53 |
, GenericContainer(..) |
53 | 54 |
, Container |
b/src/Ganeti/Types.hs | ||
---|---|---|
159 | 159 |
, hotplugTargetToRaw |
160 | 160 |
, HotplugAction(..) |
161 | 161 |
, hotplugActionToRaw |
162 |
, Private(..) |
|
163 |
, showPrivateJSObject |
|
162 | 164 |
) where |
163 | 165 |
|
164 | 166 |
import Control.Monad (liftM) |
... | ... | |
689 | 691 |
absoluteJobIdDep :: (Monad m) => JobIdDep -> JobId -> m JobIdDep |
690 | 692 |
absoluteJobIdDep (JobDepAbsolute jid) _ = return $ JobDepAbsolute jid |
691 | 693 |
absoluteJobIdDep (JobDepRelative rjid) jid = |
692 |
liftM JobDepAbsolute . makeJobId $ fromJobId jid + fromNegative rjid
|
|
694 |
liftM JobDepAbsolute . makeJobId $ fromJobId jid + fromNegative rjid |
|
693 | 695 |
|
694 | 696 |
-- | Job Dependency type. |
695 | 697 |
data JobDependency = JobDependency JobIdDep [FinalizedJobStatus] |
... | ... | |
702 | 704 |
-- | From job dependency and job id compute an absolute job dependency. |
703 | 705 |
absoluteJobDependency :: (Monad m) => JobDependency -> JobId -> m JobDependency |
704 | 706 |
absoluteJobDependency (JobDependency jdep fstats) jid = |
705 |
liftM (flip JobDependency fstats) $ absoluteJobIdDep jdep jid
|
|
707 |
liftM (flip JobDependency fstats) $ absoluteJobIdDep jdep jid |
|
706 | 708 |
|
707 | 709 |
-- | Valid opcode priorities for submit. |
708 | 710 |
$(THH.declareIADT "OpSubmitPriority" |
... | ... | |
889 | 891 |
, ("HTNic", "hotnic") |
890 | 892 |
]) |
891 | 893 |
$(THH.makeJSONInstance ''HotplugTarget) |
894 |
|
|
895 |
-- * Private type and instances |
|
896 |
|
|
897 |
-- | A container for values that should be happy to be manipulated yet |
|
898 |
-- refuses to be shown unless explicitly requested. |
|
899 |
newtype Private a = Private { getPrivate :: a } |
|
900 |
deriving Eq |
|
901 |
|
|
902 |
instance (Show a, JSON.JSON a) => JSON.JSON (Private a) where |
|
903 |
readJSON = liftM Private . JSON.readJSON |
|
904 |
showJSON (Private x) = JSON.showJSON x |
|
905 |
|
|
906 |
-- | "Show" the value of the field. |
|
907 |
-- |
|
908 |
-- It would be better not to implement this at all. |
|
909 |
-- Alas, Show OpCode requires Show Private. |
|
910 |
instance Show a => Show (Private a) where |
|
911 |
show _ = "<redacted>" |
|
912 |
|
|
913 |
instance THH.PyValue a => THH.PyValue (Private a) where |
|
914 |
showValue (Private x) = "Private(" ++ THH.showValue x ++ ")" |
|
915 |
|
|
916 |
instance Functor Private where |
|
917 |
fmap f (Private x) = Private $ f x |
|
918 |
|
|
919 |
instance Monad Private where |
|
920 |
(Private x) >>= f = f x |
|
921 |
return = Private |
|
922 |
|
|
923 |
showPrivateJSObject :: (JSON.JSON a) => |
|
924 |
[(String, a)] -> JSON.JSObject (Private JSON.JSValue) |
|
925 |
showPrivateJSObject value = JSON.toJSObject $ map f value |
|
926 |
where f (k, v) = (k, Private $ JSON.showJSON v) |
b/test/hs/Test/Ganeti/Objects.hs | ||
---|---|---|
219 | 219 |
instance Arbitrary OsParams where |
220 | 220 |
arbitrary = (GenericContainer . Map.fromList) <$> arbitrary |
221 | 221 |
|
222 |
instance Arbitrary Objects.ClusterOsParamsPrivate where |
|
223 |
arbitrary = (GenericContainer . Map.fromList) <$> arbitrary |
|
224 |
|
|
225 |
instance Arbitrary a => Arbitrary (Private a) where |
|
226 |
arbitrary = Private <$> arbitrary |
|
227 |
|
|
222 | 228 |
instance Arbitrary ClusterOsParams where |
223 | 229 |
arbitrary = (GenericContainer . Map.fromList) <$> arbitrary |
224 | 230 |
|
... | ... | |
552 | 558 |
caseIncludeLogicalIdDrbd = |
553 | 559 |
let vg_name = "xenvg" :: String |
554 | 560 |
lv_name = "1234sdf-qwef-2134-asff-asd2-23145d.data" :: String |
555 |
d =
|
|
561 |
d = |
|
556 | 562 |
Disk |
557 | 563 |
(LIDDrbd8 "node1.example.com" "node2.example.com" 2000 1 5 "secret") |
558 | 564 |
[ Disk (LIDPlain "onevg" "onelv") [] "disk1" 1000 DiskRdWr Nothing |
b/test/hs/Test/Ganeti/OpCodes.hs | ||
---|---|---|
431 | 431 |
octets <- vectorOf 3 $ choose (0::Int, 255) |
432 | 432 |
mkNonEmpty . intercalate ":" $ map (printf "%02x") octets |
433 | 433 |
|
434 |
-- | JSObject of arbitrary data. |
|
435 |
-- |
|
436 |
-- Since JSValue does not implement Arbitrary, I'll simply generate |
|
437 |
-- (String, String) objects. |
|
438 |
arbitraryPrivateJSObj :: Gen (J.JSObject (Private J.JSValue)) |
|
439 |
arbitraryPrivateJSObj = |
|
440 |
constructor <$> (fromNonEmpty <$> genNameNE) |
|
441 |
<*> (fromNonEmpty <$> genNameNE) |
|
442 |
where constructor k v = showPrivateJSObject [(k, v)] |
|
443 |
|
|
434 | 444 |
-- | Arbitrary instance for MetaOpCode, defined here due to TH ordering. |
435 | 445 |
$(genArbitrary ''OpCodes.MetaOpCode) |
436 | 446 |
|
Also available in: Unified diff