Revision b9e12624
b/lib/backend.py | ||
---|---|---|
1440 | 1440 |
|
1441 | 1441 |
""" |
1442 | 1442 |
output = {} |
1443 |
|
|
1444 | 1443 |
for hname in hypervisor_list: |
1445 | 1444 |
hvparams = all_hvparams[hname] |
1446 | 1445 |
iinfo = hypervisor.GetHypervisor(hname).GetAllInstancesInfo(hvparams) |
... | ... | |
1465 | 1464 |
return output |
1466 | 1465 |
|
1467 | 1466 |
|
1467 |
def GetInstanceConsoleInfo(instance_param_dict, |
|
1468 |
get_hv_fn=hypervisor.GetHypervisor): |
|
1469 |
"""Gather data about the console access of a set of instances of this node. |
|
1470 |
|
|
1471 |
This function assumes that the caller already knows which instances are on |
|
1472 |
this node, by calling a function such as L{GetAllInstancesInfo} or |
|
1473 |
L{GetInstanceList}. |
|
1474 |
|
|
1475 |
For every instance, a large amount of configuration data needs to be |
|
1476 |
provided to the hypervisor interface in order to receive the console |
|
1477 |
information. Whether this could or should be cut down can be discussed. |
|
1478 |
The information is provided in a dictionary indexed by instance name, |
|
1479 |
allowing any number of instance queries to be done. |
|
1480 |
|
|
1481 |
@type instance_param_dict: dict of string to tuple of dictionaries, where the |
|
1482 |
dictionaries represent: L{objects.Instance}, L{objects.Node}, HvParams, |
|
1483 |
BeParams |
|
1484 |
@param instance_param_dict: mapping of instance name to parameters necessary |
|
1485 |
for console information retrieval |
|
1486 |
|
|
1487 |
@rtype: dict |
|
1488 |
@return: dictionary of instance: data, with data having the following keys: |
|
1489 |
- instance: instance name |
|
1490 |
- kind: console kind |
|
1491 |
- message: used with kind == CONS_MESSAGE, indicates console to be |
|
1492 |
unavailable, supplies error message |
|
1493 |
- host: host to connect to |
|
1494 |
- port: port to use |
|
1495 |
- user: user for login |
|
1496 |
- command: the command, broken into parts as an array |
|
1497 |
- display: unknown, potentially unused? |
|
1498 |
|
|
1499 |
""" |
|
1500 |
|
|
1501 |
output = {} |
|
1502 |
for inst_name in instance_param_dict: |
|
1503 |
instance = instance_param_dict[inst_name]["instance"] |
|
1504 |
pnode = instance_param_dict[inst_name]["node"] |
|
1505 |
hvparams = instance_param_dict[inst_name]["hvParams"] |
|
1506 |
beparams = instance_param_dict[inst_name]["beParams"] |
|
1507 |
|
|
1508 |
instance = objects.Instance.FromDict(instance) |
|
1509 |
pnode = objects.Node.FromDict(pnode) |
|
1510 |
|
|
1511 |
h = get_hv_fn(instance.hypervisor) |
|
1512 |
output[inst_name] = h.GetInstanceConsole(instance, pnode, hvparams, |
|
1513 |
beparams).ToDict() |
|
1514 |
|
|
1515 |
return output |
|
1516 |
|
|
1517 |
|
|
1468 | 1518 |
def _InstanceLogName(kind, os_name, instance, component): |
1469 | 1519 |
"""Compute the OS log filename for a given instance and operation. |
1470 | 1520 |
|
b/lib/server/noded.py | ||
---|---|---|
166 | 166 |
"""Handle a request. |
167 | 167 |
|
168 | 168 |
""" |
169 |
|
|
169 | 170 |
if req.request_method.upper() != http.HTTP_POST: |
170 | 171 |
raise http.HttpBadRequest("Only the POST method is supported") |
171 | 172 |
|
... | ... | |
714 | 715 |
return backend.GetAllInstancesInfo(hypervisor_list, all_hvparams) |
715 | 716 |
|
716 | 717 |
@staticmethod |
718 |
def perspective_instance_console_info(params): |
|
719 |
"""Query information on how to get console access to instances |
|
720 |
|
|
721 |
""" |
|
722 |
return backend.GetInstanceConsoleInfo(params) |
|
723 |
|
|
724 |
@staticmethod |
|
717 | 725 |
def perspective_instance_list(params): |
718 | 726 |
"""Query the list of running instances. |
719 | 727 |
|
b/src/Ganeti/Rpc.hs | ||
---|---|---|
49 | 49 |
, RpcCallAllInstancesInfo(..) |
50 | 50 |
, RpcResultAllInstancesInfo(..) |
51 | 51 |
|
52 |
, InstanceConsoleInfoParams(..) |
|
53 |
, InstanceConsoleInfo(..) |
|
54 |
, RpcCallInstanceConsoleInfo(..) |
|
55 |
, RpcResultInstanceConsoleInfo(..) |
|
56 |
|
|
52 | 57 |
, RpcCallInstanceList(..) |
53 | 58 |
, RpcResultInstanceList(..) |
54 | 59 |
|
... | ... | |
246 | 251 |
|
247 | 252 |
-- ** Instance info |
248 | 253 |
|
249 |
-- | InstanceInfo |
|
250 |
-- Returns information about a single instance. |
|
251 |
|
|
254 |
-- | Returns information about a single instance |
|
252 | 255 |
$(buildObject "RpcCallInstanceInfo" "rpcCallInstInfo" |
253 | 256 |
[ simpleField "instance" [t| String |] |
254 | 257 |
, simpleField "hname" [t| Hypervisor |] |
... | ... | |
287 | 290 |
|
288 | 291 |
-- ** AllInstancesInfo |
289 | 292 |
|
290 |
-- | AllInstancesInfo |
|
291 |
-- Returns information about all running instances on the given nodes |
|
293 |
-- | Returns information about all running instances on the given nodes |
|
292 | 294 |
$(buildObject "RpcCallAllInstancesInfo" "rpcCallAllInstInfo" |
293 | 295 |
[ simpleField "hypervisors" [t| [(Hypervisor, HvParams)] |] ]) |
294 | 296 |
|
... | ... | |
316 | 318 |
_ -> Left $ JsonDecodeError |
317 | 319 |
("Expected JSObject, got " ++ show (pp_value res)) |
318 | 320 |
|
321 |
-- ** InstanceConsoleInfo |
|
322 |
|
|
323 |
-- | Returns information about how to access instances on the given node |
|
324 |
$(buildObject "InstanceConsoleInfoParams" "instConsInfoParams" |
|
325 |
[ simpleField "instance" [t| Instance |] |
|
326 |
, simpleField "node" [t| Node |] |
|
327 |
, simpleField "hvParams" [t| HvParams |] |
|
328 |
, simpleField "beParams" [t| FilledBeParams |] |
|
329 |
]) |
|
330 |
|
|
331 |
$(buildObject "RpcCallInstanceConsoleInfo" "rpcCallInstConsInfo" |
|
332 |
[ simpleField "instanceInfo" [t| [(String, InstanceConsoleInfoParams)] |] ]) |
|
333 |
|
|
334 |
$(buildObject "InstanceConsoleInfo" "instConsInfo" |
|
335 |
[ simpleField "instance" [t| String |] |
|
336 |
, simpleField "kind" [t| String |] |
|
337 |
, optionalField $ |
|
338 |
simpleField "message" [t| String |] |
|
339 |
, optionalField $ |
|
340 |
simpleField "host" [t| String |] |
|
341 |
, optionalField $ |
|
342 |
simpleField "port" [t| Int |] |
|
343 |
, optionalField $ |
|
344 |
simpleField "user" [t| String |] |
|
345 |
, optionalField $ |
|
346 |
simpleField "command" [t| [String] |] |
|
347 |
, optionalField $ |
|
348 |
simpleField "display" [t| String |] |
|
349 |
]) |
|
350 |
|
|
351 |
$(buildObject "RpcResultInstanceConsoleInfo" "rpcResInstConsInfo" |
|
352 |
[ simpleField "instancesInfo" [t| [(String, InstanceConsoleInfo)] |] ]) |
|
353 |
|
|
354 |
instance RpcCall RpcCallInstanceConsoleInfo where |
|
355 |
rpcCallName _ = "instance_console_info" |
|
356 |
rpcCallTimeout _ = rpcTimeoutToRaw Urgent |
|
357 |
rpcCallAcceptOffline _ = False |
|
358 |
rpcCallData _ call = J.encode . |
|
359 |
GenericContainer $ Map.fromList (rpcCallInstConsInfoInstanceInfo call) |
|
360 |
|
|
361 |
instance Rpc RpcCallInstanceConsoleInfo RpcResultInstanceConsoleInfo where |
|
362 |
rpcResultFill _ res = |
|
363 |
case res of |
|
364 |
J.JSObject res' -> |
|
365 |
let res'' = map (second J.readJSON) (J.fromJSObject res') |
|
366 |
:: [(String, J.Result InstanceConsoleInfo)] in |
|
367 |
case sanitizeDictResults res'' of |
|
368 |
Left err -> Left err |
|
369 |
Right instInfos -> Right $ RpcResultInstanceConsoleInfo instInfos |
|
370 |
_ -> Left $ JsonDecodeError |
|
371 |
("Expected JSObject, got " ++ show (pp_value res)) |
|
372 |
|
|
319 | 373 |
-- ** InstanceList |
320 | 374 |
|
321 |
-- | InstanceList |
|
322 |
-- Returns the list of running instances on the given nodes. |
|
375 |
-- | Returns the list of running instances on the given nodes |
|
323 | 376 |
$(buildObject "RpcCallInstanceList" "rpcCallInstList" |
324 | 377 |
[ simpleField "hypervisors" [t| [Hypervisor] |] ]) |
325 | 378 |
|
... | ... | |
337 | 390 |
|
338 | 391 |
-- ** NodeInfo |
339 | 392 |
|
340 |
-- | NodeInfo |
|
341 |
-- Return node information. |
|
393 |
-- | Returns node information |
|
342 | 394 |
$(buildObject "RpcCallNodeInfo" "rpcCallNodeInfo" |
343 | 395 |
[ simpleField "storage_units" [t| Map.Map String [StorageUnit] |] |
344 | 396 |
, simpleField "hypervisors" [t| [ (Hypervisor, HvParams) ] |] |
b/src/rpc-test.hs | ||
---|---|---|
80 | 80 |
requestComp o = o { optShowComp = True } |
81 | 81 |
|
82 | 82 |
-- | The rpcs we support. Sadly this duplicates the RPC list. |
83 |
data KnownRpc = KRInstanceInfo RpcCallInstanceInfo |
|
84 |
| KRAllInstancesInfo RpcCallAllInstancesInfo |
|
85 |
| KRInstanceList RpcCallInstanceList |
|
86 |
| KRNodeInfo RpcCallNodeInfo |
|
87 |
| KRVersion RpcCallVersion |
|
88 |
| KRStorageList RpcCallStorageList |
|
89 |
| KRTestDelay RpcCallTestDelay |
|
90 |
| KRExportList RpcCallExportList |
|
83 |
data KnownRpc = KRInstanceInfo RpcCallInstanceInfo |
|
84 |
| KRAllInstancesInfo RpcCallAllInstancesInfo |
|
85 |
| KRInstanceConsoleInfo RpcCallInstanceConsoleInfo |
|
86 |
| KRInstanceList RpcCallInstanceList |
|
87 |
| KRNodeInfo RpcCallNodeInfo |
|
88 |
| KRVersion RpcCallVersion |
|
89 |
| KRStorageList RpcCallStorageList |
|
90 |
| KRTestDelay RpcCallTestDelay |
|
91 |
| KRExportList RpcCallExportList |
|
91 | 92 |
deriving (Show) |
92 | 93 |
|
93 | 94 |
-- | The command line options. |
... | ... | |
144 | 145 |
fromJResult "parsing rpc" (decode f) >>= Ok . KRInstanceInfo |
145 | 146 |
parseRpc "all_instances_info" f = |
146 | 147 |
fromJResult "parsing rpc" (decode f) >>= Ok . KRAllInstancesInfo |
148 |
parseRpc "console_instance_info" f = |
|
149 |
fromJResult "parsing rpc" (decode f) >>= Ok . KRConsoleInstanceInfo |
|
147 | 150 |
parseRpc "instance_list" f = |
148 | 151 |
fromJResult "parsing rpc" (decode f) >>= Ok . KRInstanceList |
149 | 152 |
parseRpc "node_info" f = |
... | ... | |
162 | 165 |
-- polymorphism of 'executeRpcCall', and the binding of the result |
163 | 166 |
-- based on the input rpc call. |
164 | 167 |
execRpc :: [Node] -> KnownRpc -> IO [[String]] |
165 |
execRpc n (KRInstanceInfo v) = formatRpcRes `fmap` executeRpcCall n v |
|
166 |
execRpc n (KRAllInstancesInfo v) = formatRpcRes `fmap` executeRpcCall n v |
|
167 |
execRpc n (KRInstanceList v) = formatRpcRes `fmap` executeRpcCall n v |
|
168 |
execRpc n (KRNodeInfo v) = formatRpcRes `fmap` executeRpcCall n v |
|
169 |
execRpc n (KRVersion v) = formatRpcRes `fmap` executeRpcCall n v |
|
170 |
execRpc n (KRStorageList v) = formatRpcRes `fmap` executeRpcCall n v |
|
171 |
execRpc n (KRTestDelay v) = formatRpcRes `fmap` executeRpcCall n v |
|
172 |
execRpc n (KRExportList v) = formatRpcRes `fmap` executeRpcCall n v |
|
168 |
execRpc n (KRInstanceInfo v) = formatRpcRes `fmap` executeRpcCall n v |
|
169 |
execRpc n (KRAllInstancesInfo v) = formatRpcRes `fmap` executeRpcCall n v |
|
170 |
execRpc n (KRConsoleInstanceInfo v) = formatRpcRes `fmap` executeRpcCall n v |
|
171 |
execRpc n (KRInstanceList v) = formatRpcRes `fmap` executeRpcCall n v |
|
172 |
execRpc n (KRNodeInfo v) = formatRpcRes `fmap` executeRpcCall n v |
|
173 |
execRpc n (KRVersion v) = formatRpcRes `fmap` executeRpcCall n v |
|
174 |
execRpc n (KRStorageList v) = formatRpcRes `fmap` executeRpcCall n v |
|
175 |
execRpc n (KRTestDelay v) = formatRpcRes `fmap` executeRpcCall n v |
|
176 |
execRpc n (KRExportList v) = formatRpcRes `fmap` executeRpcCall n v |
|
173 | 177 |
|
174 | 178 |
-- | Helper to format the RPC result such that it can be printed by |
175 | 179 |
-- 'printTable'. |
b/test/hs/Test/Ganeti/Rpc.hs | ||
---|---|---|
36 | 36 |
|
37 | 37 |
import Test.Ganeti.TestHelper |
38 | 38 |
import Test.Ganeti.TestCommon |
39 |
import Test.Ganeti.Objects () |
|
39 |
import Test.Ganeti.Objects (genInst)
|
|
40 | 40 |
|
41 | 41 |
import qualified Ganeti.Rpc as Rpc |
42 | 42 |
import qualified Ganeti.Objects as Objects |
... | ... | |
44 | 44 |
import qualified Ganeti.JSON as JSON |
45 | 45 |
import Ganeti.Types |
46 | 46 |
|
47 |
instance Arbitrary Rpc.RpcCallInstanceConsoleInfo where |
|
48 |
arbitrary = Rpc.RpcCallInstanceConsoleInfo <$> genConsoleInfoCallParams |
|
49 |
|
|
47 | 50 |
genStorageUnit :: Gen StorageUnit |
48 | 51 |
genStorageUnit = do |
49 | 52 |
storage_type <- arbitrary |
... | ... | |
86 | 89 |
instance Arbitrary Rpc.RpcCallNodeInfo where |
87 | 90 |
arbitrary = Rpc.RpcCallNodeInfo <$> genStorageUnitMap <*> genHvSpecs |
88 | 91 |
|
89 |
-- | Monadic check that, for an offline node and a call that does not |
|
92 |
-- | Generates per-instance console info params for the 'InstanceConsoleInfo' |
|
93 |
-- call. |
|
94 |
genConsoleInfoCallParams :: Gen [(String, Rpc.InstanceConsoleInfoParams)] |
|
95 |
genConsoleInfoCallParams = do |
|
96 |
numInstances <- choose (0, 3) |
|
97 |
names <- vectorOf numInstances arbitrary |
|
98 |
params <- vectorOf numInstances genInstanceConsoleInfoParams |
|
99 |
return $ zip names params |
|
100 |
|
|
101 |
-- | Generates parameters for the console info call, consisting of an instance |
|
102 |
-- object, node object, 'HvParams', and 'FilledBeParams'. |
|
103 |
genInstanceConsoleInfoParams :: Gen Rpc.InstanceConsoleInfoParams |
|
104 |
genInstanceConsoleInfoParams = Rpc.InstanceConsoleInfoParams <$> |
|
105 |
genInst <*> arbitrary <*> genHvParams <*> arbitrary |
|
106 |
|
|
107 |
-- | Monadic check that, for an offline node and a call that does not support |
|
90 | 108 |
-- offline nodes, we get a OfflineNodeError response. |
91 |
-- FIXME: We need a way of generalizing this, running it for |
|
92 |
-- every call manually will soon get problematic |
|
93 |
prop_noffl_request_allinstinfo :: Rpc.RpcCallAllInstancesInfo -> Property |
|
94 |
prop_noffl_request_allinstinfo call = |
|
109 |
runOfflineTest :: (Rpc.Rpc a b, Eq b, Show b) => a -> Property |
|
110 |
runOfflineTest call = |
|
95 | 111 |
forAll (arbitrary `suchThat` Objects.nodeOffline) $ \node -> monadicIO $ do |
96 | 112 |
res <- run $ Rpc.executeRpcCall [node] call |
97 | 113 |
stop $ res ==? [(node, Left Rpc.OfflineNodeError)] |
98 | 114 |
|
115 |
prop_noffl_request_allinstinfo :: Rpc.RpcCallAllInstancesInfo -> Property |
|
116 |
prop_noffl_request_allinstinfo = runOfflineTest |
|
117 |
|
|
118 |
prop_noffl_request_instconsinfo :: Rpc.RpcCallInstanceConsoleInfo -> Property |
|
119 |
prop_noffl_request_instconsinfo = runOfflineTest |
|
120 |
|
|
99 | 121 |
prop_noffl_request_instlist :: Rpc.RpcCallInstanceList -> Property |
100 |
prop_noffl_request_instlist call = |
|
101 |
forAll (arbitrary `suchThat` Objects.nodeOffline) $ \node -> monadicIO $ do |
|
102 |
res <- run $ Rpc.executeRpcCall [node] call |
|
103 |
stop $ res ==? [(node, Left Rpc.OfflineNodeError)] |
|
122 |
prop_noffl_request_instlist = runOfflineTest |
|
104 | 123 |
|
105 | 124 |
prop_noffl_request_nodeinfo :: Rpc.RpcCallNodeInfo -> Property |
106 |
prop_noffl_request_nodeinfo call = |
|
107 |
forAll (arbitrary `suchThat` Objects.nodeOffline) $ \node -> monadicIO $ do |
|
108 |
res <- run $ Rpc.executeRpcCall [node] call |
|
109 |
stop $ res ==? [(node, Left Rpc.OfflineNodeError)] |
|
125 |
prop_noffl_request_nodeinfo = runOfflineTest |
|
110 | 126 |
|
111 | 127 |
testSuite "Rpc" |
112 | 128 |
[ 'prop_noffl_request_allinstinfo |
129 |
, 'prop_noffl_request_instconsinfo |
|
113 | 130 |
, 'prop_noffl_request_instlist |
114 | 131 |
, 'prop_noffl_request_nodeinfo |
115 | 132 |
] |
b/test/py/ganeti.backend_unittest.py | ||
---|---|---|
33 | 33 |
from ganeti import errors |
34 | 34 |
from ganeti import hypervisor |
35 | 35 |
from ganeti import netutils |
36 |
from ganeti import objects |
|
36 | 37 |
from ganeti import utils |
37 | 38 |
|
38 | 39 |
|
... | ... | |
590 | 591 |
self._test_hv.ListInstances.assert_called_with(hvparams=fake_hvparams) |
591 | 592 |
|
592 | 593 |
|
594 |
class TestInstanceConsoleInfo(unittest.TestCase): |
|
595 |
|
|
596 |
def setUp(self): |
|
597 |
self._test_hv_a = self._TestHypervisor() |
|
598 |
self._test_hv_a.GetInstanceConsole = mock.Mock( |
|
599 |
return_value = objects.InstanceConsole(instance="inst", kind="aHy") |
|
600 |
) |
|
601 |
self._test_hv_b = self._TestHypervisor() |
|
602 |
self._test_hv_b.GetInstanceConsole = mock.Mock( |
|
603 |
return_value = objects.InstanceConsole(instance="inst", kind="bHy") |
|
604 |
) |
|
605 |
|
|
606 |
class _TestHypervisor(hypervisor.hv_base.BaseHypervisor): |
|
607 |
def __init__(self): |
|
608 |
hypervisor.hv_base.BaseHypervisor.__init__(self) |
|
609 |
|
|
610 |
def _GetHypervisor(self, name): |
|
611 |
if name == "a": |
|
612 |
return self._test_hv_a |
|
613 |
else: |
|
614 |
return self._test_hv_b |
|
615 |
|
|
616 |
def testRightHypervisor(self): |
|
617 |
dictMaker = lambda hyName: { |
|
618 |
"instance":{"hypervisor":hyName}, |
|
619 |
"node":{}, |
|
620 |
"hvParams":{}, |
|
621 |
"beParams":{}, |
|
622 |
} |
|
623 |
|
|
624 |
call = { |
|
625 |
'i1':dictMaker("a"), |
|
626 |
'i2':dictMaker("b"), |
|
627 |
} |
|
628 |
|
|
629 |
res = backend.GetInstanceConsoleInfo(call, get_hv_fn=self._GetHypervisor) |
|
630 |
|
|
631 |
self.assertTrue(res["i1"]["kind"] == "aHy") |
|
632 |
self.assertTrue(res["i2"]["kind"] == "bHy") |
|
633 |
|
|
634 |
|
|
593 | 635 |
class TestGetHvInfo(unittest.TestCase): |
594 | 636 |
|
595 | 637 |
def setUp(self): |
Also available in: Unified diff