Revision c8c071cb
b/man/htools.rst | ||
---|---|---|
181 | 181 |
- node tags |
182 | 182 |
- exclusive storage value (``Y`` if active, ``N`` otherwise) |
183 | 183 |
- node free spindles |
184 |
- virtual CPUs used by the node OS |
|
184 | 185 |
|
185 | 186 |
The third section contains instance data, with the fields: |
186 | 187 |
|
b/src/Ganeti/HTools/Backend/IAlloc.hs | ||
---|---|---|
147 | 147 |
dtotal <- lvextract 0.0 "total_disk" |
148 | 148 |
dfree <- lvextract 0 "free_disk" |
149 | 149 |
ctotal <- lvextract 0.0 "total_cpus" |
150 |
let node = Node.create n mtotal mnode mfree dtotal dfree ctotal (not live) |
|
151 |
sptotal spfree gidx excl_stor |
|
150 |
cnos <- lvextract 0 "reserved_cpus" |
|
151 |
let node = Node.create n mtotal mnode mfree dtotal dfree ctotal cnos |
|
152 |
(not live) sptotal spfree gidx excl_stor |
|
152 | 153 |
return (n, node) |
153 | 154 |
|
154 | 155 |
-- | Parses a group as found in the cluster group list. |
b/src/Ganeti/HTools/Backend/Luxi.hs | ||
---|---|---|
117 | 117 |
queryNodesMsg = |
118 | 118 |
L.Query (Qlang.ItemTypeOpCode Qlang.QRNode) |
119 | 119 |
["name", "mtotal", "mnode", "mfree", "dtotal", "dfree", |
120 |
"ctotal", "offline", "drained", "vm_capable", |
|
120 |
"ctotal", "cnos", "offline", "drained", "vm_capable",
|
|
121 | 121 |
"ndp/spindle_count", "group.uuid", "tags", |
122 | 122 |
"ndp/exclusive_storage", "sptotal", "spfree"] Qlang.EmptyFilter |
123 | 123 |
|
... | ... | |
204 | 204 |
-- | Construct a node from a JSON object. |
205 | 205 |
parseNode :: NameAssoc -> [(JSValue, JSValue)] -> Result (String, Node.Node) |
206 | 206 |
parseNode ktg [ name, mtotal, mnode, mfree, dtotal, dfree |
207 |
, ctotal, offline, drained, vm_capable, spindles, g_uuid |
|
207 |
, ctotal, cnos, offline, drained, vm_capable, spindles, g_uuid
|
|
208 | 208 |
, tags, excl_stor, sptotal, spfree ] |
209 | 209 |
= do |
210 | 210 |
xname <- annotateResult "Parsing new node" (fromJValWithStatus name) |
... | ... | |
227 | 227 |
xdtotal <- lvconvert 0.0 "dtotal" dtotal |
228 | 228 |
xdfree <- lvconvert 0 "dfree" dfree |
229 | 229 |
xctotal <- lvconvert 0.0 "ctotal" ctotal |
230 |
xcnos <- lvconvert 0 "cnos" cnos |
|
230 | 231 |
let node = flip Node.setNodeTags xtags $ |
231 | 232 |
Node.create xname xmtotal xmnode xmfree xdtotal xdfree |
232 |
xctotal (not live) xsptotal xspfree xgdx xexcl_stor |
|
233 |
xctotal xcnos (not live) xsptotal xspfree xgdx xexcl_stor
|
|
233 | 234 |
return (xname, node) |
234 | 235 |
|
235 | 236 |
parseNode _ v = fail ("Invalid node query result: " ++ show v) |
b/src/Ganeti/HTools/Backend/Rapi.hs | ||
---|---|---|
171 | 171 |
dtotal <- lvextract 0.0 "dtotal" |
172 | 172 |
dfree <- lvextract 0 "dfree" |
173 | 173 |
ctotal <- lvextract 0.0 "ctotal" |
174 |
cnos <- lvextract 0 "cnos" |
|
174 | 175 |
tags <- extract "tags" |
175 | 176 |
let node = flip Node.setNodeTags tags $ |
176 |
Node.create name mtotal mnode mfree dtotal dfree ctotal (not live)
|
|
177 |
sptotal spfree guuid' excl_stor |
|
177 |
Node.create name mtotal mnode mfree dtotal dfree ctotal cnos
|
|
178 |
(not live) sptotal spfree guuid' excl_stor
|
|
178 | 179 |
return (name, node) |
179 | 180 |
|
180 | 181 |
-- | Construct a group from a JSON object. |
b/src/Ganeti/HTools/Backend/Simu.hs | ||
---|---|---|
85 | 85 |
Node.create (printf "node-%02d-%03d" grpIndex idx) |
86 | 86 |
(fromIntegral mem) 0 mem |
87 | 87 |
(fromIntegral disk) disk |
88 |
(fromIntegral cpu) False spindles 0 grpIndex excl_stor |
|
88 |
(fromIntegral cpu) 1 False spindles 0 grpIndex excl_stor
|
|
89 | 89 |
) [1..ncount] |
90 | 90 |
-- TODO: parse networks to which this group is connected |
91 | 91 |
grp = Group.create (printf "group-%02d" grpIndex) |
b/src/Ganeti/HTools/Backend/Text.hs | ||
---|---|---|
84 | 84 |
-> Node.Node -- ^ The node to be serialised |
85 | 85 |
-> String |
86 | 86 |
serializeNode gl node = |
87 |
printf "%s|%.0f|%d|%d|%.0f|%d|%.0f|%c|%s|%d|%s|%s|%d" (Node.name node) |
|
87 |
printf "%s|%.0f|%d|%d|%.0f|%d|%.0f|%c|%s|%d|%s|%s|%d|%d" (Node.name node)
|
|
88 | 88 |
(Node.tMem node) (Node.nMem node) (Node.fMem node) |
89 | 89 |
(Node.tDsk node) (Node.fDsk node) (Node.tCpu node) |
90 | 90 |
(if Node.offline node then 'Y' else |
... | ... | |
94 | 94 |
(intercalate "," (Node.nTags node)) |
95 | 95 |
(if Node.exclStorage node then "Y" else "N") |
96 | 96 |
(Node.fSpindles node) |
97 |
(Node.nCpu node) |
|
97 | 98 |
where grp = Container.find (Node.group node) gl |
98 | 99 |
|
99 | 100 |
-- | Generate node file data from node objects. |
... | ... | |
206 | 207 |
-> m (String, Node.Node) -- ^ The result, a tuple o node name |
207 | 208 |
-- and node object |
208 | 209 |
loadNode ktg [name, tm, nm, fm, td, fd, tc, fo, gu, spindles, tags, |
209 |
excl_stor, free_spindles] = do |
|
210 |
excl_stor, free_spindles, nos_cpu] = do
|
|
210 | 211 |
gdx <- lookupGroup ktg name gu |
211 | 212 |
new_node <- |
212 | 213 |
if "?" `elem` [tm,nm,fm,td,fd,tc] || fo == "Y" then |
213 |
return $ Node.create name 0 0 0 0 0 0 True 0 0 gdx False |
|
214 |
return $ Node.create name 0 0 0 0 0 0 0 True 0 0 gdx False
|
|
214 | 215 |
else do |
215 | 216 |
let vtags = commaSplit tags |
216 | 217 |
vtm <- tryRead name tm |
... | ... | |
219 | 220 |
vtd <- tryRead name td |
220 | 221 |
vfd <- tryRead name fd |
221 | 222 |
vtc <- tryRead name tc |
223 |
vnc <- tryRead name nos_cpu |
|
222 | 224 |
vspindles <- tryRead name spindles |
223 | 225 |
vfree_spindles <- tryRead name free_spindles |
224 | 226 |
vexcl_stor <- case excl_stor of |
... | ... | |
228 | 230 |
"Invalid exclusive_storage value for node '" ++ |
229 | 231 |
name ++ "': " ++ excl_stor |
230 | 232 |
return . flip Node.setMaster (fo == "M") . flip Node.setNodeTags vtags $ |
231 |
Node.create name vtm vnm vfm vtd vfd vtc False vspindles |
|
233 |
Node.create name vtm vnm vfm vtd vfd vtc vnc False vspindles
|
|
232 | 234 |
vfree_spindles gdx vexcl_stor |
233 | 235 |
return (name, new_node) |
234 | 236 |
|
... | ... | |
246 | 248 |
loadNode ktg [name, tm, nm, fm, td, fd, tc, fo, gu, spindles, tags, |
247 | 249 |
excl_stor, "0"] |
248 | 250 |
|
251 |
loadNode ktg [name, tm, nm, fm, td, fd, tc, fo, gu, spindles, tags, |
|
252 |
excl_stor, free_spindles] = |
|
253 |
loadNode ktg [name, tm, nm, fm, td, fd, tc, fo, gu, spindles, tags, |
|
254 |
excl_stor, free_spindles, "1"] |
|
255 |
|
|
249 | 256 |
loadNode _ s = fail $ "Invalid/incomplete node data: '" ++ show s ++ "'" |
250 | 257 |
|
251 | 258 |
-- | Load an instance from a field list. |
b/src/Ganeti/HTools/Node.hs | ||
---|---|---|
111 | 111 |
, tDsk :: Double -- ^ Total disk space (MiB) |
112 | 112 |
, fDsk :: Int -- ^ Free disk space (MiB) |
113 | 113 |
, tCpu :: Double -- ^ Total CPU count |
114 |
, nCpu :: Int -- ^ VCPUs used by the node OS |
|
114 | 115 |
, uCpu :: Int -- ^ Used VCPU count |
115 | 116 |
, tSpindles :: Int -- ^ Node spindles (spindle_count node parameter, |
116 | 117 |
-- or actual spindles, see note below) |
... | ... | |
234 | 235 |
-- |
235 | 236 |
-- The index and the peers maps are empty, and will be need to be |
236 | 237 |
-- update later via the 'setIdx' and 'buildPeers' functions. |
237 |
create :: String -> Double -> Int -> Int -> Double |
|
238 |
-> Int -> Double -> Bool -> Int -> Int -> T.Gdx -> Bool -> Node |
|
238 |
create :: String -> Double -> Int -> Int |
|
239 |
-> Double -> Int -> Double -> Int -> Bool |
|
240 |
-> Int -> Int -> T.Gdx -> Bool |
|
241 |
-> Node |
|
239 | 242 |
create name_init mem_t_init mem_n_init mem_f_init |
240 |
dsk_t_init dsk_f_init cpu_t_init offline_init spindles_t_init
|
|
241 |
spindles_f_init group_init excl_stor = |
|
243 |
dsk_t_init dsk_f_init cpu_t_init cpu_n_init offline_init
|
|
244 |
spindles_t_init spindles_f_init group_init excl_stor =
|
|
242 | 245 |
Node { name = name_init |
243 | 246 |
, alias = name_init |
244 | 247 |
, tMem = mem_t_init |
... | ... | |
247 | 250 |
, tDsk = dsk_t_init |
248 | 251 |
, fDsk = dsk_f_init |
249 | 252 |
, tCpu = cpu_t_init |
253 |
, nCpu = cpu_n_init |
|
254 |
, uCpu = cpu_n_init |
|
250 | 255 |
, tSpindles = spindles_t_init |
251 | 256 |
, fSpindles = spindles_f_init |
252 |
, uCpu = 0 |
|
253 | 257 |
, pList = [] |
254 | 258 |
, sList = [] |
255 | 259 |
, failN1 = True |
... | ... | |
261 | 265 |
then computePDsk spindles_f_init $ fromIntegral spindles_t_init |
262 | 266 |
else computePDsk dsk_f_init dsk_t_init |
263 | 267 |
, pRem = 0 |
264 |
, pCpu = 0
|
|
268 |
, pCpu = fromIntegral cpu_n_init / cpu_t_init
|
|
265 | 269 |
, offline = offline_init |
266 | 270 |
, isMaster = False |
267 | 271 |
, nTags = [] |
b/test/data/htools/hail-alloc-drbd.json | ||
---|---|---|
429 | 429 |
"exclusive_storage": false |
430 | 430 |
}, |
431 | 431 |
"reserved_memory": 1017, |
432 |
"reserved_cpus": 1, |
|
432 | 433 |
"master_capable": true, |
433 | 434 |
"free_disk": 1377280, |
434 | 435 |
"drained": false, |
... | ... | |
455 | 456 |
"exclusive_storage": false |
456 | 457 |
}, |
457 | 458 |
"reserved_memory": 1017, |
459 |
"reserved_cpus": 1, |
|
458 | 460 |
"master_capable": true, |
459 | 461 |
"free_disk": 1376640, |
460 | 462 |
"drained": false, |
... | ... | |
481 | 483 |
"exclusive_storage": false |
482 | 484 |
}, |
483 | 485 |
"reserved_memory": 1017, |
486 |
"reserved_cpus": 1, |
|
484 | 487 |
"master_capable": true, |
485 | 488 |
"free_disk": 1373336, |
486 | 489 |
"drained": false, |
... | ... | |
507 | 510 |
"exclusive_storage": false |
508 | 511 |
}, |
509 | 512 |
"reserved_memory": 1017, |
513 |
"reserved_cpus": 1, |
|
510 | 514 |
"master_capable": true, |
511 | 515 |
"free_disk": 1371520, |
512 | 516 |
"drained": false, |
b/test/data/htools/hail-alloc-invalid-network.json | ||
---|---|---|
106 | 106 |
}, |
107 | 107 |
"offline": false, |
108 | 108 |
"reserved_memory": 1017, |
109 |
"reserved_cpus": 1, |
|
109 | 110 |
"total_cpus": 4, |
110 | 111 |
"total_disk": 7168, |
111 | 112 |
"total_memory": 4096, |
... | ... | |
123 | 124 |
}, |
124 | 125 |
"offline": false, |
125 | 126 |
"reserved_memory": 1017, |
127 |
"reserved_cpus": 1, |
|
126 | 128 |
"total_cpus": 4, |
127 | 129 |
"total_disk": 7168, |
128 | 130 |
"total_memory": 4096, |
b/test/data/htools/hail-alloc-invalid-twodisks.json | ||
---|---|---|
65 | 65 |
}, |
66 | 66 |
"offline": false, |
67 | 67 |
"reserved_memory": 1017, |
68 |
"reserved_cpus": 1, |
|
68 | 69 |
"total_cpus": 4, |
69 | 70 |
"total_disk": 1377280, |
70 | 71 |
"total_memory": 32763, |
b/test/data/htools/hail-alloc-restricted-network.json | ||
---|---|---|
197 | 197 |
}, |
198 | 198 |
"offline": false, |
199 | 199 |
"reserved_memory": 1017, |
200 |
"reserved_cpus": 1, |
|
200 | 201 |
"total_cpus": 4, |
201 | 202 |
"total_disk": 7168, |
202 | 203 |
"total_memory": 4096, |
... | ... | |
214 | 215 |
}, |
215 | 216 |
"offline": false, |
216 | 217 |
"reserved_memory": 1017, |
218 |
"reserved_cpus": 1, |
|
217 | 219 |
"total_cpus": 4, |
218 | 220 |
"total_disk": 7168, |
219 | 221 |
"total_memory": 32763, |
... | ... | |
231 | 233 |
}, |
232 | 234 |
"offline": false, |
233 | 235 |
"reserved_memory": 1017, |
236 |
"reserved_cpus": 1, |
|
234 | 237 |
"total_cpus": 4, |
235 | 238 |
"total_disk": 7168, |
236 | 239 |
"total_memory": 4096, |
... | ... | |
248 | 251 |
}, |
249 | 252 |
"offline": false, |
250 | 253 |
"reserved_memory": 1017, |
254 |
"reserved_cpus": 1, |
|
251 | 255 |
"total_cpus": 4, |
252 | 256 |
"total_disk": 7168, |
253 | 257 |
"total_memory": 4096, |
b/test/data/htools/hail-alloc-spindles.json | ||
---|---|---|
284 | 284 |
"exclusive_storage": false |
285 | 285 |
}, |
286 | 286 |
"reserved_memory": 1017, |
287 |
"reserved_cpus": 1, |
|
287 | 288 |
"master_capable": true, |
288 | 289 |
"free_disk": 687280, |
289 | 290 |
"drained": false, |
... | ... | |
310 | 311 |
"exclusive_storage": false |
311 | 312 |
}, |
312 | 313 |
"reserved_memory": 1017, |
314 |
"reserved_cpus": 1, |
|
313 | 315 |
"master_capable": true, |
314 | 316 |
"free_disk": 1377024, |
315 | 317 |
"drained": false, |
... | ... | |
336 | 338 |
"exclusive_storage": false |
337 | 339 |
}, |
338 | 340 |
"reserved_memory": 1017, |
341 |
"reserved_cpus": 1, |
|
339 | 342 |
"master_capable": true, |
340 | 343 |
"free_disk": 687280, |
341 | 344 |
"drained": false, |
... | ... | |
362 | 365 |
"exclusive_storage": false |
363 | 366 |
}, |
364 | 367 |
"reserved_memory": 1017, |
368 |
"reserved_cpus": 1, |
|
365 | 369 |
"master_capable": true, |
366 | 370 |
"free_disk": 1377024, |
367 | 371 |
"drained": false, |
b/test/data/htools/hail-alloc-twodisks.json | ||
---|---|---|
65 | 65 |
}, |
66 | 66 |
"offline": false, |
67 | 67 |
"reserved_memory": 1017, |
68 |
"reserved_cpus": 1, |
|
68 | 69 |
"total_cpus": 4, |
69 | 70 |
"total_disk": 1377280, |
70 | 71 |
"total_memory": 32763, |
b/test/data/htools/hail-change-group.json | ||
---|---|---|
476 | 476 |
"exclusive_storage": false |
477 | 477 |
}, |
478 | 478 |
"reserved_memory": 1017, |
479 |
"reserved_cpus": 1, |
|
479 | 480 |
"master_capable": true, |
480 | 481 |
"free_disk": 1377280, |
481 | 482 |
"drained": false, |
... | ... | |
502 | 503 |
"exclusive_storage": false |
503 | 504 |
}, |
504 | 505 |
"reserved_memory": 1017, |
506 |
"reserved_cpus": 1, |
|
505 | 507 |
"master_capable": true, |
506 | 508 |
"free_disk": 1373336, |
507 | 509 |
"drained": false, |
... | ... | |
528 | 530 |
"exclusive_storage": false |
529 | 531 |
}, |
530 | 532 |
"reserved_memory": 1017, |
533 |
"reserved_cpus": 1, |
|
531 | 534 |
"master_capable": true, |
532 | 535 |
"free_disk": 1371520, |
533 | 536 |
"drained": false, |
... | ... | |
554 | 557 |
"exclusive_storage": false |
555 | 558 |
}, |
556 | 559 |
"reserved_memory": 1017, |
560 |
"reserved_cpus": 1, |
|
557 | 561 |
"master_capable": true, |
558 | 562 |
"free_disk": 1376640, |
559 | 563 |
"drained": false, |
... | ... | |
580 | 584 |
"exclusive_storage": false |
581 | 585 |
}, |
582 | 586 |
"reserved_memory": 1017, |
587 |
"reserved_cpus": 1, |
|
583 | 588 |
"master_capable": true, |
584 | 589 |
"free_disk": 1376640, |
585 | 590 |
"drained": false, |
b/test/data/htools/hail-node-evac.json | ||
---|---|---|
425 | 425 |
"exclusive_storage": false |
426 | 426 |
}, |
427 | 427 |
"reserved_memory": 1017, |
428 |
"reserved_cpus": 1, |
|
428 | 429 |
"master_capable": true, |
429 | 430 |
"free_disk": 1377280, |
430 | 431 |
"drained": false, |
... | ... | |
451 | 452 |
"exclusive_storage": false |
452 | 453 |
}, |
453 | 454 |
"reserved_memory": 1017, |
455 |
"reserved_cpus": 1, |
|
454 | 456 |
"master_capable": true, |
455 | 457 |
"free_disk": 1376640, |
456 | 458 |
"drained": false, |
... | ... | |
477 | 479 |
"exclusive_storage": false |
478 | 480 |
}, |
479 | 481 |
"reserved_memory": 1017, |
482 |
"reserved_cpus": 1, |
|
480 | 483 |
"master_capable": true, |
481 | 484 |
"free_disk": 1373336, |
482 | 485 |
"drained": false, |
... | ... | |
503 | 506 |
"exclusive_storage": false |
504 | 507 |
}, |
505 | 508 |
"reserved_memory": 1017, |
509 |
"reserved_cpus": 1, |
|
506 | 510 |
"master_capable": true, |
507 | 511 |
"free_disk": 1371520, |
508 | 512 |
"drained": false, |
b/test/data/htools/hail-reloc-drbd.json | ||
---|---|---|
429 | 429 |
"exclusive_storage": false |
430 | 430 |
}, |
431 | 431 |
"reserved_memory": 1017, |
432 |
"reserved_cpus": 1, |
|
432 | 433 |
"master_capable": true, |
433 | 434 |
"free_disk": 1377280, |
434 | 435 |
"drained": false, |
... | ... | |
455 | 456 |
"exclusive_storage": false |
456 | 457 |
}, |
457 | 458 |
"reserved_memory": 1017, |
459 |
"reserved_cpus": 1, |
|
458 | 460 |
"master_capable": true, |
459 | 461 |
"free_disk": 1376640, |
460 | 462 |
"drained": false, |
... | ... | |
481 | 483 |
"exclusive_storage": false |
482 | 484 |
}, |
483 | 485 |
"reserved_memory": 1017, |
486 |
"reserved_cpus": 1, |
|
484 | 487 |
"master_capable": true, |
485 | 488 |
"free_disk": 1373336, |
486 | 489 |
"drained": false, |
... | ... | |
507 | 510 |
"exclusive_storage": false |
508 | 511 |
}, |
509 | 512 |
"reserved_memory": 1017, |
513 |
"reserved_cpus": 1, |
|
510 | 514 |
"master_capable": true, |
511 | 515 |
"free_disk": 1371520, |
512 | 516 |
"drained": false, |
b/test/data/htools/rapi/nodes.json | ||
---|---|---|
1 | 1 |
[ |
2 | 2 |
{ |
3 | 3 |
"cnodes": 2, |
4 |
"cnos": 1, |
|
4 | 5 |
"csockets": 2, |
5 | 6 |
"ctime": 1324472016.2968869, |
6 | 7 |
"ctotal": 4, |
... | ... | |
37 | 38 |
}, |
38 | 39 |
{ |
39 | 40 |
"cnodes": 2, |
41 |
"cnos": 1, |
|
40 | 42 |
"csockets": 2, |
41 | 43 |
"ctime": 1324472016.2968869, |
42 | 44 |
"ctotal": 4, |
... | ... | |
73 | 75 |
}, |
74 | 76 |
{ |
75 | 77 |
"cnodes": 2, |
78 |
"cnos": 1, |
|
76 | 79 |
"dfree": 1373336, |
77 | 80 |
"drained": false, |
78 | 81 |
"dtotal": 1377304, |
... | ... | |
117 | 120 |
}, |
118 | 121 |
{ |
119 | 122 |
"cnodes": 2, |
123 |
"cnos": 1, |
|
120 | 124 |
"dfree": 1371520, |
121 | 125 |
"drained": false, |
122 | 126 |
"dtotal": 1377280, |
b/test/hs/Test/Ganeti/HTools/Cluster.hs | ||
---|---|---|
107 | 107 |
forAll (choose (1, 1024)) $ \count -> |
108 | 108 |
(not (Node.offline node) && not (Node.failN1 node) && (count > 0) && |
109 | 109 |
(Node.tDsk node > 0) && (Node.tMem node > 0) && |
110 |
(Node.tSpindles node > 0)) ==> |
|
110 |
(Node.tSpindles node > 0) && (Node.tCpu node > 0)) ==>
|
|
111 | 111 |
let fn = Node.buildPeers node Container.empty |
112 | 112 |
nlst = replicate count fn |
113 | 113 |
score = Cluster.compCVNodes nlst |
b/test/hs/Test/Ganeti/HTools/Node.hs | ||
---|---|---|
90 | 90 |
dsk_t <- choose (base_dsk, top_dsk) |
91 | 91 |
dsk_f <- choose (base_dsk, dsk_t) |
92 | 92 |
cpu_t <- choose (base_cpu, top_cpu) |
93 |
cpu_n <- choose (base_cpu, cpu_t) |
|
93 | 94 |
offl <- arbitrary |
94 | 95 |
spindles <- choose (base_spindles, top_spindles) |
95 | 96 |
let n = Node.create name (fromIntegral mem_t) mem_n mem_f |
96 |
(fromIntegral dsk_t) dsk_f (fromIntegral cpu_t) offl spindles |
|
97 |
(fromIntegral dsk_t) dsk_f (fromIntegral cpu_t) cpu_n offl spindles
|
|
97 | 98 |
0 0 False |
98 | 99 |
n' = Node.setPolicy nullIPolicy n |
99 | 100 |
return $ Node.buildPeers n' Container.empty |
Also available in: Unified diff