Revision 4c6e8e1a
b/NEWS | ||
---|---|---|
79 | 79 |
New features |
80 | 80 |
~~~~~~~~~~~~ |
81 | 81 |
|
82 |
- Hotplug support. Introduce new option --hotplug to gnt-instance modify
|
|
82 |
- Hotplug support. Introduce new option '--hotplug' to ``gnt-instance modify``
|
|
83 | 83 |
so that disk and NIC modifications take effect without the need of actual |
84 |
reboot. This feature is currently supported only for KVM hypervisor with |
|
85 |
version greater than 1.0. |
|
84 |
reboot. There are a couple of constrains currently for this feature: |
|
85 |
|
|
86 |
- only KVM hypervisor (versions >= 1.0) supports it, |
|
87 |
- one can not (yet) hotplug a disk using userspace access mode for RBD |
|
88 |
- in case of a downgrade instances should suffer a reboot in order to |
|
89 |
be migratable (due to core change of runtime files) |
|
86 | 90 |
- The :doc:`Remote API <rapi>` daemon now supports a command line flag |
87 | 91 |
to always require authentication, ``--require-authentication``. It can |
88 | 92 |
be specified in ``$sysconfdir/default/ganeti``. |
b/lib/cli.py | ||
---|---|---|
1641 | 1641 |
|
1642 | 1642 |
HOTPLUG_OPT = cli_option("--hotplug", dest="hotplug", |
1643 | 1643 |
action="store_true", default=False, |
1644 |
help="Try to hotplug device")
|
|
1644 |
help="Hotplug supported devices (NICs and Disks)")
|
|
1645 | 1645 |
|
1646 | 1646 |
#: Options provided by all commands |
1647 | 1647 |
COMMON_OPTS = [DEBUG_OPT, REASON_OPT] |
b/lib/client/gnt_instance.py | ||
---|---|---|
1314 | 1314 |
|
1315 | 1315 |
nics = _ConvertNicDiskModifications(opts.nics) |
1316 | 1316 |
for action, _, __ in nics: |
1317 |
if action == constants.DDM_MODIFY and opts.hotplug: |
|
1317 |
if action == constants.DDM_MODIFY and opts.hotplug and not opts.force:
|
|
1318 | 1318 |
usertext = ("You are about to hot-modify a NIC. This will be done" |
1319 | 1319 |
" by removing the exisiting and then adding a new one." |
1320 | 1320 |
" Network connection might be lost. Continue?") |
... | ... | |
1363 | 1363 |
ToStdout("Modified instance %s", args[0]) |
1364 | 1364 |
for param, data in result: |
1365 | 1365 |
ToStdout(" - %-5s -> %s", param, data) |
1366 |
if not opts.hotplug: |
|
1367 |
ToStdout("Please don't forget that most parameters take effect" |
|
1368 |
" only at the next (re)start of the instance initiated by" |
|
1369 |
" ganeti; restarting from within the instance will" |
|
1370 |
" not be enough.") |
|
1366 |
ToStdout("Please don't forget that most parameters take effect" |
|
1367 |
" only at the next (re)start of the instance initiated by" |
|
1368 |
" ganeti; restarting from within the instance will" |
|
1369 |
" not be enough.") |
|
1371 | 1370 |
return 0 |
1372 | 1371 |
|
1373 | 1372 |
|
b/lib/cmdlib/instance.py | ||
---|---|---|
2190 | 2190 |
if op == constants.DDM_REMOVE: |
2191 | 2191 |
assert not params |
2192 | 2192 |
|
2193 |
if remove_fn is not None: |
|
2194 |
remove_fn(absidx, item, private) |
|
2195 |
|
|
2196 | 2193 |
changes = [("%s/%s" % (kind, absidx), "remove")] |
2197 | 2194 |
|
2195 |
if remove_fn is not None: |
|
2196 |
msg = remove_fn(absidx, item, private) |
|
2197 |
if msg: |
|
2198 |
changes.append(("%s/%s" % (kind, absidx), msg)) |
|
2199 |
|
|
2198 | 2200 |
assert container[absidx] == item |
2199 | 2201 |
del container[absidx] |
2200 | 2202 |
elif op == constants.DDM_MODIFY: |
... | ... | |
3155 | 3157 |
|
3156 | 3158 |
def _HotplugDevice(self, action, dev_type, device, extra, seq): |
3157 | 3159 |
self.LogInfo("Trying to hotplug device...") |
3160 |
msg = "hotplug:" |
|
3158 | 3161 |
result = self.rpc.call_hotplug_device(self.instance.primary_node, |
3159 | 3162 |
self.instance, action, dev_type, |
3160 | 3163 |
(device, self.instance), |
... | ... | |
3162 | 3165 |
if result.fail_msg: |
3163 | 3166 |
self.LogWarning("Could not hotplug device: %s" % result.fail_msg) |
3164 | 3167 |
self.LogInfo("Continuing execution..") |
3168 |
msg += "failed" |
|
3165 | 3169 |
else: |
3166 | 3170 |
self.LogInfo("Hotplug done.") |
3171 |
msg += "done" |
|
3172 |
return msg |
|
3167 | 3173 |
|
3168 | 3174 |
def _CreateNewDisk(self, idx, params, _): |
3169 | 3175 |
"""Creates a new disk. |
... | ... | |
3192 | 3198 |
disks=[(idx, disk, 0)], |
3193 | 3199 |
cleanup=new_disks) |
3194 | 3200 |
|
3201 |
changes = [ |
|
3202 |
("disk/%d" % idx, |
|
3203 |
"add:size=%s,mode=%s" % (disk.size, disk.mode)), |
|
3204 |
] |
|
3195 | 3205 |
if self.op.hotplug: |
3196 | 3206 |
self.cfg.SetDiskID(disk, self.instance.primary_node) |
3197 | 3207 |
result = self.rpc.call_blockdev_assemble(self.instance.primary_node, |
3198 | 3208 |
(disk, self.instance), |
3199 | 3209 |
self.instance.name, True, idx) |
3200 | 3210 |
if result.fail_msg: |
3211 |
changes.append(("disk/%d" % idx, "assemble:failed")) |
|
3201 | 3212 |
self.LogWarning("Can't assemble newly created disk %d: %s", |
3202 | 3213 |
idx, result.fail_msg) |
3203 | 3214 |
else: |
3204 | 3215 |
_, link_name = result.payload |
3205 |
self._HotplugDevice(constants.HOTPLUG_ACTION_ADD, |
|
3206 |
constants.HOTPLUG_TARGET_DISK, |
|
3207 |
disk, link_name, idx) |
|
3216 |
msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD, |
|
3217 |
constants.HOTPLUG_TARGET_DISK, |
|
3218 |
disk, link_name, idx) |
|
3219 |
changes.append(("disk/%d" % idx, msg)) |
|
3208 | 3220 |
|
3209 |
return (disk, [ |
|
3210 |
("disk/%d" % idx, "add:size=%s,mode=%s" % (disk.size, disk.mode)), |
|
3211 |
]) |
|
3221 |
return (disk, changes) |
|
3212 | 3222 |
|
3213 | 3223 |
def _ModifyDisk(self, idx, disk, params, _): |
3214 | 3224 |
"""Modifies a disk. |
... | ... | |
3243 | 3253 |
"""Removes a disk. |
3244 | 3254 |
|
3245 | 3255 |
""" |
3256 |
hotmsg = "" |
|
3246 | 3257 |
if self.op.hotplug: |
3247 |
self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE, |
|
3248 |
constants.HOTPLUG_TARGET_DISK, |
|
3249 |
root, None, idx) |
|
3258 |
hotmsg = self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
|
|
3259 |
constants.HOTPLUG_TARGET_DISK,
|
|
3260 |
root, None, idx)
|
|
3250 | 3261 |
ShutdownInstanceDisks(self, self.instance, [root]) |
3251 | 3262 |
|
3252 | 3263 |
(anno_disk,) = AnnotateDiskParams(self.instance, [root], self.cfg) |
... | ... | |
3261 | 3272 |
if root.dev_type in constants.LDS_DRBD: |
3262 | 3273 |
self.cfg.AddTcpUdpPort(root.logical_id[2]) |
3263 | 3274 |
|
3275 |
return hotmsg |
|
3276 |
|
|
3264 | 3277 |
def _CreateNewNic(self, idx, params, private): |
3265 | 3278 |
"""Creates data structure for a new network interface. |
3266 | 3279 |
|
... | ... | |
3276 | 3289 |
nicparams=nicparams) |
3277 | 3290 |
nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId()) |
3278 | 3291 |
|
3279 |
if self.op.hotplug: |
|
3280 |
self._HotplugDevice(constants.HOTPLUG_ACTION_ADD, |
|
3281 |
constants.HOTPLUG_TARGET_NIC, |
|
3282 |
nobj, None, idx) |
|
3283 |
|
|
3284 |
desc = [ |
|
3292 |
changes = [ |
|
3285 | 3293 |
("nic.%d" % idx, |
3286 | 3294 |
"add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" % |
3287 | 3295 |
(mac, ip, private.filled[constants.NIC_MODE], |
3288 | 3296 |
private.filled[constants.NIC_LINK], net)), |
3289 | 3297 |
] |
3290 | 3298 |
|
3291 |
return (nobj, desc) |
|
3299 |
if self.op.hotplug: |
|
3300 |
msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD, |
|
3301 |
constants.HOTPLUG_TARGET_NIC, |
|
3302 |
nobj, None, idx) |
|
3303 |
changes.append(("nic.%d" % idx, msg)) |
|
3304 |
|
|
3305 |
return (nobj, changes) |
|
3292 | 3306 |
|
3293 | 3307 |
def _ApplyNicMods(self, idx, nic, params, private): |
3294 | 3308 |
"""Modifies a network interface. |
... | ... | |
3314 | 3328 |
changes.append(("nic.%s/%d" % (key, idx), val)) |
3315 | 3329 |
|
3316 | 3330 |
if self.op.hotplug: |
3317 |
self._HotplugDevice(constants.HOTPLUG_ACTION_MODIFY, |
|
3318 |
constants.HOTPLUG_TARGET_NIC, |
|
3319 |
nic, None, idx) |
|
3331 |
msg = self._HotplugDevice(constants.HOTPLUG_ACTION_MODIFY, |
|
3332 |
constants.HOTPLUG_TARGET_NIC, |
|
3333 |
nic, None, idx) |
|
3334 |
changes.append(("nic/%d" % idx, msg)) |
|
3320 | 3335 |
|
3321 | 3336 |
return changes |
3322 | 3337 |
|
3323 | 3338 |
def _RemoveNic(self, idx, nic, _): |
3324 | 3339 |
if self.op.hotplug: |
3325 |
self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE, |
|
3326 |
constants.HOTPLUG_TARGET_NIC, |
|
3327 |
nic, None, idx) |
|
3340 |
return self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
|
|
3341 |
constants.HOTPLUG_TARGET_NIC,
|
|
3342 |
nic, None, idx)
|
|
3328 | 3343 |
|
3329 | 3344 |
def Exec(self, feedback_fn): |
3330 | 3345 |
"""Modifies an instance. |
b/man/gnt-instance.rst | ||
---|---|---|
1184 | 1184 |
If ``--ignore-ipolicy`` is given any instance policy violations occuring |
1185 | 1185 |
during this operation are ignored. |
1186 | 1186 |
|
1187 |
If ``--hotplug`` is given any disk and nic modifications will take
|
|
1187 |
If ``--hotplug`` is given any disk and NIC modifications will take
|
|
1188 | 1188 |
effect without the need of actual reboot. Please note that this feature |
1189 |
is currently supported only for KVM hypervisor and for versions greater |
|
1190 |
than 1.0. |
|
1189 |
is currently supported only for KVM hypervisor and there are some |
|
1190 |
restrictions: a) KVM versions >= 1.0 support it b) instances with chroot |
|
1191 |
or uid pool security model do not support disk hotplug c) RBD disks with |
|
1192 |
userspace access mode can not be hotplugged (yet) d) if hotplug fails |
|
1193 |
(for any reason) a warning is printed but execution is continued e) |
|
1194 |
for existing NIC modification interactive verification is needed unless |
|
1195 |
``--force`` option is passed. |
|
1191 | 1196 |
|
1192 | 1197 |
See **ganeti**\(7) for a description of ``--submit`` and other common |
1193 | 1198 |
options. |
b/qa/qa_instance.py | ||
---|---|---|
684 | 684 |
qa_config.TestEnabled("instance-device-hotplug"): |
685 | 685 |
args.extend([ |
686 | 686 |
["--net", "-1:add", "--hotplug"], |
687 |
["--net", "-1:modify,mac=aa:bb:cc:dd:ee:ff", "--hotplug"], |
|
687 |
["--net", "-1:modify,mac=aa:bb:cc:dd:ee:ff", "--hotplug", "--force"],
|
|
688 | 688 |
["--net", "-1:remove", "--hotplug"], |
689 | 689 |
["--disk", "-1:add,size=1G", "--hotplug"], |
690 | 690 |
["--disk", "-1:remove", "--hotplug"], |
Also available in: Unified diff