1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 """DRBD block device related functionality"""
32
33 import errno
34 import logging
35 import time
36
37 from ganeti import constants
38 from ganeti import utils
39 from ganeti import errors
40 from ganeti import netutils
41 from ganeti import objects
42 from ganeti.storage import base
43 from ganeti.storage.drbd_info import DRBD8Info
44 from ganeti.storage import drbd_info
45 from ganeti.storage import drbd_cmdgen
46
47
48
49
50 _DEVICE_READ_SIZE = 128 * 1024
51
52
53 -class DRBD8(object):
54 """Various methods to deals with the DRBD system as a whole.
55
56 This class provides a set of methods to deal with the DRBD installation on
57 the node or with uninitialized devices as opposed to a DRBD device.
58
59 """
60 _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
61
62 _MAX_MINORS = 255
63
64 @staticmethod
66 """Returns DRBD usermode_helper currently set.
67
68 @type filename: string
69 @param filename: the filename to read the usermode helper from
70 @rtype: string
71 @return: the currently configured DRBD usermode helper
72
73 """
74 try:
75 helper = utils.ReadFile(filename).splitlines()[0]
76 except EnvironmentError, err:
77 if err.errno == errno.ENOENT:
78 base.ThrowError("The file %s cannot be opened, check if the module"
79 " is loaded (%s)", filename, str(err))
80 else:
81 base.ThrowError("Can't read DRBD helper file %s: %s",
82 filename, str(err))
83 if not helper:
84 base.ThrowError("Can't read any data from %s", filename)
85 return helper
86
87 @staticmethod
89 """Reads and parses information from /proc/drbd.
90
91 @rtype: DRBD8Info
92 @return: a L{DRBD8Info} instance containing the current /proc/drbd info
93
94 """
95 return DRBD8Info.CreateFromFile()
96
97 @staticmethod
99 """Compute the list of used DRBD minors.
100
101 @rtype: list of ints
102
103 """
104 info = DRBD8.GetProcInfo()
105 return filter(lambda m: not info.GetMinorStatus(m).is_unconfigured,
106 info.GetMinors())
107
108 @staticmethod
110 """Find an unused DRBD device.
111
112 This is specific to 8.x as the minors are allocated dynamically,
113 so non-existing numbers up to a max minor count are actually free.
114
115 @rtype: int
116
117 """
118 highest = None
119 info = DRBD8.GetProcInfo()
120 for minor in info.GetMinors():
121 status = info.GetMinorStatus(minor)
122 if not status.is_in_use:
123 return minor
124 highest = max(highest, minor)
125
126 if highest is None:
127 return 0
128 if highest >= DRBD8._MAX_MINORS:
129 logging.error("Error: no free drbd minors!")
130 raise errors.BlockDeviceError("Can't find a free DRBD minor")
131
132 return highest + 1
133
134 @staticmethod
147
148 @staticmethod
150 """Deactivate the device.
151
152 This will, of course, fail if the device is in use.
153
154 @type minor: int
155 @param minor: the minor to shut down
156
157 """
158 info = DRBD8.GetProcInfo()
159 cmd_gen = DRBD8.GetCmdGenerator(info)
160
161 cmd = cmd_gen.GenDownCmd(minor)
162 result = utils.RunCmd(cmd)
163 if result.failed:
164 base.ThrowError("drbd%d: can't shutdown drbd device: %s",
165 minor, result.output)
166
169 """DRBD v8.x block device.
170
171 This implements the local host part of the DRBD device, i.e. it
172 doesn't do anything to the supposed peer. If you need a fully
173 connected DRBD pair, you need to use this class on both hosts.
174
175 The unique_id for the drbd device is a (pnode_uuid, snode_uuid,
176 port, pnode_minor, lnode_minor, secret) tuple, and it must have
177 two children: the data device and the meta_device. The meta
178 device is checked for valid size and is zeroed on create.
179
180 """
181 _DRBD_MAJOR = 147
182
183
184 _NET_RECONFIG_TIMEOUT = 60
185
186 - def __init__(self, unique_id, children, size, params, dyn_params, *args):
187 if children and children.count(None) > 0:
188 children = []
189 if len(children) not in (0, 2):
190 raise ValueError("Invalid configuration data %s" % str(children))
191 if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
192 raise ValueError("Invalid configuration data %s" % str(unique_id))
193 if constants.DDP_LOCAL_IP not in dyn_params or \
194 constants.DDP_REMOTE_IP not in dyn_params or \
195 constants.DDP_LOCAL_MINOR not in dyn_params or \
196 constants.DDP_REMOTE_MINOR not in dyn_params:
197 raise ValueError("Invalid dynamic parameters %s" % str(dyn_params))
198
199 self._lhost = dyn_params[constants.DDP_LOCAL_IP]
200 self._lport = unique_id[2]
201 self._rhost = dyn_params[constants.DDP_REMOTE_IP]
202 self._rport = unique_id[2]
203 self._aminor = dyn_params[constants.DDP_LOCAL_MINOR]
204
205
206 self._secret = unique_id[5].Get()
207
208 if children:
209 if not _CanReadDevice(children[1].dev_path):
210 logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
211 children = []
212 super(DRBD8Dev, self).__init__(unique_id, children, size, params,
213 dyn_params, *args)
214 self.major = self._DRBD_MAJOR
215
216 info = DRBD8.GetProcInfo()
217 version = info.GetVersion()
218 if version["k_major"] != 8:
219 base.ThrowError("Mismatch in DRBD kernel version and requested ganeti"
220 " usage: kernel is %s.%s, ganeti wants 8.x",
221 version["k_major"], version["k_minor"])
222
223 if version["k_minor"] <= 3:
224 self._show_info_cls = drbd_info.DRBD83ShowInfo
225 else:
226 self._show_info_cls = drbd_info.DRBD84ShowInfo
227
228 self._cmd_gen = DRBD8.GetCmdGenerator(info)
229
230 if (self._lhost is not None and self._lhost == self._rhost and
231 self._lport == self._rport):
232 raise ValueError("Invalid configuration data, same local/remote %s, %s" %
233 (unique_id, dyn_params))
234 self.Attach()
235
236 @staticmethod
238 """Return the path to a drbd device for a given minor.
239
240 @type minor: int
241 @rtype: string
242
243 """
244 return "/dev/drbd%d" % minor
245
247 """Set our parameters based on the given minor.
248
249 This sets our minor variable and our dev_path.
250
251 @type minor: int
252
253 """
254 if minor is None:
255 self.minor = self.dev_path = None
256 self.attached = False
257 else:
258 self.minor = minor
259 self.dev_path = self._DevPath(minor)
260 self.attached = True
261
262 @staticmethod
294
296 """Return the `drbdsetup show` data.
297
298 @type minor: int
299 @param minor: the minor to collect show output for
300 @rtype: string
301
302 """
303 result = utils.RunCmd(self._cmd_gen.GenShowCmd(minor))
304 if result.failed:
305 logging.error("Can't display the drbd config: %s - %s",
306 result.fail_reason, result.output)
307 return None
308 return result.stdout
309
311 """Return parsed information from `drbdsetup show`.
312
313 @type minor: int
314 @param minor: the minor to return information for
315 @rtype: dict as described in L{drbd_info.BaseShowInfo.GetDevInfo}
316
317 """
318 return self._show_info_cls.GetDevInfo(self._GetShowData(minor))
319
321 """Test if our local config matches with an existing device.
322
323 The parameter should be as returned from `_GetShowInfo()`. This
324 method tests if our local backing device is the same as the one in
325 the info parameter, in effect testing if we look like the given
326 device.
327
328 @type info: dict as described in L{drbd_info.BaseShowInfo.GetDevInfo}
329 @rtype: boolean
330
331 """
332 if self._children:
333 backend, meta = self._children
334 else:
335 backend = meta = None
336
337 if backend is not None:
338 retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
339 else:
340 retval = ("local_dev" not in info)
341
342 if meta is not None:
343 retval = retval and ("meta_dev" in info and
344 info["meta_dev"] == meta.dev_path)
345 if "meta_index" in info:
346 retval = retval and info["meta_index"] == 0
347 else:
348 retval = retval and ("meta_dev" not in info and
349 "meta_index" not in info)
350 return retval
351
353 """Test if our network config matches with an existing device.
354
355 The parameter should be as returned from `_GetShowInfo()`. This
356 method tests if our network configuration is the same as the one
357 in the info parameter, in effect testing if we look like the given
358 device.
359
360 @type info: dict as described in L{drbd_info.BaseShowInfo.GetDevInfo}
361 @rtype: boolean
362
363 """
364 if (((self._lhost is None and not ("local_addr" in info)) and
365 (self._rhost is None and not ("remote_addr" in info)))):
366 return True
367
368 if self._lhost is None:
369 return False
370
371 if not ("local_addr" in info and
372 "remote_addr" in info):
373 return False
374
375 retval = (info["local_addr"] == (self._lhost, self._lport))
376 retval = (retval and
377 info["remote_addr"] == (self._rhost, self._rport))
378 return retval
379
381 """Configure the local part of a DRBD device.
382
383 @type minor: int
384 @param minor: the minor to assemble locally
385 @type backend: string
386 @param backend: path to the data device to use
387 @type meta: string
388 @param meta: path to the meta device to use
389 @type size: int
390 @param size: size in MiB
391
392 """
393 cmds = self._cmd_gen.GenLocalInitCmds(minor, backend, meta,
394 size, self.params)
395
396 for cmd in cmds:
397 result = utils.RunCmd(cmd)
398 if result.failed:
399 base.ThrowError("drbd%d: can't attach local disk: %s",
400 minor, result.output)
401
402 - def _AssembleNet(self, minor, net_info, dual_pri=False, hmac=None,
403 secret=None):
404 """Configure the network part of the device.
405
406 @type minor: int
407 @param minor: the minor to assemble the network for
408 @type net_info: (string, int, string, int)
409 @param net_info: tuple containing the local address, local port, remote
410 address and remote port
411 @type dual_pri: boolean
412 @param dual_pri: whether two primaries should be allowed or not
413 @type hmac: string
414 @param hmac: the HMAC algorithm to use
415 @type secret: string
416 @param secret: the shared secret to use
417
418 """
419 lhost, lport, rhost, rport = net_info
420 if None in net_info:
421
422
423 self._ShutdownNet(minor)
424 return
425
426 if dual_pri:
427 protocol = constants.DRBD_MIGRATION_NET_PROTOCOL
428 else:
429 protocol = self.params[constants.LDP_PROTOCOL]
430
431
432
433
434
435
436
437 sync_errors = self._SetMinorSyncParams(minor, self.params)
438 if sync_errors:
439 base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
440 (minor, utils.CommaJoin(sync_errors)))
441
442 family = self._GetNetFamily(minor, lhost, rhost)
443
444 cmd = self._cmd_gen.GenNetInitCmd(minor, family, lhost, lport,
445 rhost, rport, protocol,
446 dual_pri, hmac, secret, self.params)
447
448 result = utils.RunCmd(cmd)
449 if result.failed:
450 base.ThrowError("drbd%d: can't setup network: %s - %s",
451 minor, result.fail_reason, result.output)
452
453 def _CheckNetworkConfig():
454 info = self._GetShowInfo(minor)
455 if not "local_addr" in info or not "remote_addr" in info:
456 raise utils.RetryAgain()
457
458 if (info["local_addr"] != (lhost, lport) or
459 info["remote_addr"] != (rhost, rport)):
460 raise utils.RetryAgain()
461
462 try:
463 utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
464 except utils.RetryTimeout:
465 base.ThrowError("drbd%d: timeout while configuring network", minor)
466
467
468 try:
469
470
471 old_minor = self.minor
472 self._SetFromMinor(minor)
473 sync_errors = self.SetSyncParams(self.params)
474 if sync_errors:
475 base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
476 (self.minor, utils.CommaJoin(sync_errors)))
477 finally:
478
479
480 self._SetFromMinor(old_minor)
481
482 @staticmethod
496
498 """Add a disk to the DRBD device.
499
500 @type devices: list of L{BlockDev}
501 @param devices: a list of exactly two L{BlockDev} objects; the first
502 denotes the data device, the second the meta device for this DRBD device
503
504 """
505 if self.minor is None:
506 base.ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
507 self._aminor)
508 if len(devices) != 2:
509 base.ThrowError("drbd%d: need two devices for AddChildren", self.minor)
510 info = self._GetShowInfo(self.minor)
511 if "local_dev" in info:
512 base.ThrowError("drbd%d: already attached to a local disk", self.minor)
513 backend, meta = devices
514 if backend.dev_path is None or meta.dev_path is None:
515 base.ThrowError("drbd%d: children not ready during AddChildren",
516 self.minor)
517 backend.Open()
518 meta.Open()
519 self._CheckMetaSize(meta.dev_path)
520 self._InitMeta(DRBD8.FindUnusedMinor(), meta.dev_path)
521
522 self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
523 self._children = devices
524
526 """Detach the drbd device from local storage.
527
528 @type devices: list of L{BlockDev}
529 @param devices: a list of exactly two L{BlockDev} objects; the first
530 denotes the data device, the second the meta device for this DRBD device
531
532 """
533 if self.minor is None:
534 base.ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
535 self._aminor)
536
537 info = self._GetShowInfo(self.minor)
538 if "local_dev" not in info:
539 return
540 if len(self._children) != 2:
541 base.ThrowError("drbd%d: we don't have two children: %s", self.minor,
542 self._children)
543 if self._children.count(None) == 2:
544 logging.warning("drbd%d: requested detach while detached", self.minor)
545 return
546 if len(devices) != 2:
547 base.ThrowError("drbd%d: we need two children in RemoveChildren",
548 self.minor)
549 for child, dev in zip(self._children, devices):
550 if dev != child.dev_path:
551 base.ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
552 " RemoveChildren", self.minor, dev, child.dev_path)
553
554 self._ShutdownLocal(self.minor)
555 self._children = []
556
558 """Set the parameters of the DRBD syncer.
559
560 This is the low-level implementation.
561
562 @type minor: int
563 @param minor: the drbd minor whose settings we change
564 @type params: dict
565 @param params: LD level disk parameters related to the synchronization
566 @rtype: list
567 @return: a list of error messages
568
569 """
570 cmd = self._cmd_gen.GenSyncParamsCmd(minor, params)
571 result = utils.RunCmd(cmd)
572 if result.failed:
573 msg = ("Can't change syncer rate: %s - %s" %
574 (result.fail_reason, result.output))
575 logging.error(msg)
576 return [msg]
577
578 return []
579
581 """Set the synchronization parameters of the DRBD syncer.
582
583 See L{BlockDev.SetSyncParams} for parameter description.
584
585 """
586 if self.minor is None:
587 err = "Not attached during SetSyncParams"
588 logging.info(err)
589 return [err]
590
591 children_result = super(DRBD8Dev, self).SetSyncParams(params)
592 children_result.extend(self._SetMinorSyncParams(self.minor, params))
593 return children_result
594
596 """Pauses or resumes the sync of a DRBD device.
597
598 See L{BlockDev.PauseResumeSync} for parameter description.
599
600 """
601 if self.minor is None:
602 logging.info("Not attached during PauseSync")
603 return False
604
605 children_result = super(DRBD8Dev, self).PauseResumeSync(pause)
606
607 if pause:
608 cmd = self._cmd_gen.GenPauseSyncCmd(self.minor)
609 else:
610 cmd = self._cmd_gen.GenResumeSyncCmd(self.minor)
611
612 result = utils.RunCmd(cmd)
613 if result.failed:
614 logging.error("Can't %s: %s - %s", cmd,
615 result.fail_reason, result.output)
616 return not result.failed and children_result
617
619 """Return the current status data from /proc/drbd for this device.
620
621 @rtype: DRBD8Status
622
623 """
624 if self.minor is None:
625 base.ThrowError("drbd%d: GetStats() called while not attached",
626 self._aminor)
627 info = DRBD8.GetProcInfo()
628 if not info.HasMinorStatus(self.minor):
629 base.ThrowError("drbd%d: can't find myself in /proc", self.minor)
630 return info.GetMinorStatus(self.minor)
631
633 """Returns the sync status of the device.
634
635 If sync_percent is None, it means all is ok
636 If estimated_time is None, it means we can't estimate
637 the time needed, otherwise it's the time left in seconds.
638
639 We set the is_degraded parameter to True on two conditions:
640 network not connected or local disk missing.
641
642 We compute the ldisk parameter based on whether we have a local
643 disk or not.
644
645 @rtype: objects.BlockDevStatus
646
647 """
648 if self.minor is None and not self.Attach():
649 base.ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
650
651 stats = self.GetProcStatus()
652 is_degraded = not stats.is_connected or not stats.is_disk_uptodate
653
654 if stats.is_disk_uptodate:
655 ldisk_status = constants.LDS_OKAY
656 elif stats.is_diskless:
657 ldisk_status = constants.LDS_FAULTY
658 else:
659 ldisk_status = constants.LDS_UNKNOWN
660
661 return objects.BlockDevStatus(dev_path=self.dev_path,
662 major=self.major,
663 minor=self.minor,
664 sync_percent=stats.sync_percent,
665 estimated_time=stats.est_time,
666 is_degraded=is_degraded,
667 ldisk_status=ldisk_status)
668
669 - def Open(self, force=False):
670 """Make the local state primary.
671
672 If the 'force' parameter is given, DRBD is instructed to switch the device
673 into primary mode. Since this is a potentially dangerous operation, the
674 force flag should be only given after creation, when it actually is
675 mandatory.
676
677 """
678 if self.minor is None and not self.Attach():
679 logging.error("DRBD cannot attach to a device during open")
680 return False
681
682 cmd = self._cmd_gen.GenPrimaryCmd(self.minor, force)
683
684 result = utils.RunCmd(cmd)
685 if result.failed:
686 base.ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
687 result.output)
688
690 """Make the local state secondary.
691
692 This will, of course, fail if the device is in use.
693
694 """
695 if self.minor is None and not self.Attach():
696 base.ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
697 cmd = self._cmd_gen.GenSecondaryCmd(self.minor)
698 result = utils.RunCmd(cmd)
699 if result.failed:
700 base.ThrowError("drbd%d: can't switch drbd device to secondary: %s",
701 self.minor, result.output)
702
704 """Removes network configuration.
705
706 This method shutdowns the network side of the device.
707
708 The method will wait up to a hardcoded timeout for the device to
709 go into standalone after the 'disconnect' command before
710 re-configuring it, as sometimes it takes a while for the
711 disconnect to actually propagate and thus we might issue a 'net'
712 command while the device is still connected. If the device will
713 still be attached to the network and we time out, we raise an
714 exception.
715
716 """
717 if self.minor is None:
718 base.ThrowError("drbd%d: disk not attached in re-attach net",
719 self._aminor)
720
721 if None in (self._lhost, self._lport, self._rhost, self._rport):
722 base.ThrowError("drbd%d: DRBD disk missing network info in"
723 " DisconnectNet()", self.minor)
724
725 class _DisconnectStatus(object):
726 def __init__(self, ever_disconnected):
727 self.ever_disconnected = ever_disconnected
728
729 dstatus = _DisconnectStatus(base.IgnoreError(self._ShutdownNet, self.minor))
730
731 def _WaitForDisconnect():
732 if self.GetProcStatus().is_standalone:
733 return
734
735
736
737
738 dstatus.ever_disconnected = \
739 base.IgnoreError(self._ShutdownNet, self.minor) or \
740 dstatus.ever_disconnected
741
742 raise utils.RetryAgain()
743
744
745 start_time = time.time()
746
747 try:
748
749 utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
750 self._NET_RECONFIG_TIMEOUT)
751 except utils.RetryTimeout:
752 if dstatus.ever_disconnected:
753 msg = ("drbd%d: device did not react to the"
754 " 'disconnect' command in a timely manner")
755 else:
756 msg = "drbd%d: can't shutdown network, even after multiple retries"
757
758 base.ThrowError(msg, self.minor)
759
760 reconfig_time = time.time() - start_time
761 if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
762 logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
763 self.minor, reconfig_time)
764
766 """Reconnects the network.
767
768 This method connects the network side of the device with a
769 specified multi-master flag. The device needs to be 'Standalone'
770 but have valid network configuration data.
771
772 @type multimaster: boolean
773 @param multimaster: init the network in dual-primary mode
774
775 """
776 if self.minor is None:
777 base.ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
778
779 if None in (self._lhost, self._lport, self._rhost, self._rport):
780 base.ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
781
782 status = self.GetProcStatus()
783
784 if not status.is_standalone:
785 base.ThrowError("drbd%d: device is not standalone in AttachNet",
786 self.minor)
787
788 self._AssembleNet(self.minor,
789 (self._lhost, self._lport, self._rhost, self._rport),
790 dual_pri=multimaster, hmac=constants.DRBD_HMAC_ALG,
791 secret=self._secret)
792
794 """Check if our minor is configured.
795
796 This doesn't do any device configurations - it only checks if the
797 minor is in a state different from Unconfigured.
798
799 Note that this function will not change the state of the system in
800 any way (except in case of side-effects caused by reading from
801 /proc).
802
803 """
804 used_devs = DRBD8.GetUsedDevs()
805 if self._aminor in used_devs:
806 minor = self._aminor
807 else:
808 minor = None
809
810 self._SetFromMinor(minor)
811 return minor is not None
812
814 """Assemble the drbd.
815
816 Method:
817 - if we have a configured device, we try to ensure that it matches
818 our config
819 - if not, we create it from zero
820 - anyway, set the device parameters
821
822 """
823 super(DRBD8Dev, self).Assemble()
824
825 self.Attach()
826 if self.minor is None:
827
828 self._FastAssemble()
829 else:
830
831
832 self._SlowAssemble()
833
835 """Assembles the DRBD device from a (partially) configured device.
836
837 In case of partially attached (local device matches but no network
838 setup), we perform the network attach. If successful, we re-test
839 the attach if can return success.
840
841 """
842
843
844 net_data = (self._lhost, self._lport, self._rhost, self._rport)
845 for minor in (self._aminor,):
846 info = self._GetShowInfo(minor)
847 match_l = self._MatchesLocal(info)
848 match_r = self._MatchesNet(info)
849
850 if match_l and match_r:
851
852 break
853
854 if match_l and not match_r and "local_addr" not in info:
855
856 self._AssembleNet(minor, net_data, hmac=constants.DRBD_HMAC_ALG,
857 secret=self._secret)
858 if self._MatchesNet(self._GetShowInfo(minor)):
859 break
860 else:
861 base.ThrowError("drbd%d: network attach successful, but 'drbdsetup"
862 " show' disagrees", minor)
863
864 if match_r and "local_dev" not in info:
865
866 self._AssembleLocal(minor, self._children[0].dev_path,
867 self._children[1].dev_path, self.size)
868 if self._MatchesLocal(self._GetShowInfo(minor)):
869 break
870 else:
871 base.ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
872 " show' disagrees", minor)
873
874
875
876
877
878 if (match_l and "local_dev" in info and
879 not match_r and "local_addr" in info):
880
881
882
883
884 try:
885 self._ShutdownNet(minor)
886 except errors.BlockDeviceError, err:
887 base.ThrowError("drbd%d: device has correct local storage, wrong"
888 " remote peer and is unable to disconnect in order"
889 " to attach to the correct peer: %s", minor, str(err))
890
891
892
893 self._AssembleNet(minor, net_data, hmac=constants.DRBD_HMAC_ALG,
894 secret=self._secret)
895 if self._MatchesNet(self._GetShowInfo(minor)):
896 break
897 else:
898 base.ThrowError("drbd%d: network attach successful, but 'drbdsetup"
899 " show' disagrees", minor)
900
901 else:
902 minor = None
903
904 self._SetFromMinor(minor)
905 if minor is None:
906 base.ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
907 self._aminor)
908
910 """Assemble the drbd device from zero.
911
912 This is run when in Assemble we detect our minor is unused.
913
914 """
915 minor = self._aminor
916 if self._children and self._children[0] and self._children[1]:
917 self._AssembleLocal(minor, self._children[0].dev_path,
918 self._children[1].dev_path, self.size)
919 if self._lhost and self._lport and self._rhost and self._rport:
920 self._AssembleNet(minor,
921 (self._lhost, self._lport, self._rhost, self._rport),
922 hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
923 self._SetFromMinor(minor)
924
926 """Detach from the local device.
927
928 I/Os will continue to be served from the remote device. If we
929 don't have a remote device, this operation will fail.
930
931 @type minor: int
932 @param minor: the device to detach from the local device
933
934 """
935 cmd = self._cmd_gen.GenDetachCmd(minor)
936 result = utils.RunCmd(cmd)
937 if result.failed:
938 base.ThrowError("drbd%d: can't detach local disk: %s",
939 minor, result.output)
940
942 """Disconnect from the remote peer.
943
944 This fails if we don't have a local device.
945
946 @type minor: boolean
947 @param minor: the device to disconnect from the remote peer
948
949 """
950 family = self._GetNetFamily(minor, self._lhost, self._rhost)
951 cmd = self._cmd_gen.GenDisconnectCmd(minor, family,
952 self._lhost, self._lport,
953 self._rhost, self._rport)
954 result = utils.RunCmd(cmd)
955 if result.failed:
956 base.ThrowError("drbd%d: can't shutdown network: %s",
957 minor, result.output)
958
960 """Shutdown the DRBD device.
961
962 """
963 if self.minor is None and not self.Attach():
964 logging.info("drbd%d: not attached during Shutdown()", self._aminor)
965 return
966
967 try:
968 DRBD8.ShutdownAll(self.minor)
969 finally:
970 self.minor = None
971 self.dev_path = None
972
974 """Stub remove for DRBD devices.
975
976 """
977 self.Shutdown()
978
980 """Rename a device.
981
982 This is not supported for drbd devices.
983
984 """
985 raise errors.ProgrammerError("Can't rename a drbd device")
986
987 - def Grow(self, amount, dryrun, backingstore, excl_stor):
988 """Resize the DRBD device and its backing storage.
989
990 See L{BlockDev.Grow} for parameter description.
991
992 """
993 if self.minor is None:
994 base.ThrowError("drbd%d: Grow called while not attached", self._aminor)
995 if len(self._children) != 2 or None in self._children:
996 base.ThrowError("drbd%d: cannot grow diskless device", self.minor)
997 self._children[0].Grow(amount, dryrun, backingstore, excl_stor)
998 if dryrun or backingstore:
999
1000
1001 return
1002 cmd = self._cmd_gen.GenResizeCmd(self.minor, self.size + amount)
1003 result = utils.RunCmd(cmd)
1004 if result.failed:
1005 base.ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1006
1007 @classmethod
1038
1039 @classmethod
1040 - def Create(cls, unique_id, children, size, spindles, params, excl_stor,
1041 dyn_params, *_):
1042 """Create a new DRBD8 device.
1043
1044 Since DRBD devices are not created per se, just assembled, this
1045 function only initializes the metadata.
1046
1047 """
1048 if len(children) != 2:
1049 raise errors.ProgrammerError("Invalid setup for the drbd device")
1050 if excl_stor:
1051 raise errors.ProgrammerError("DRBD device requested with"
1052 " exclusive_storage")
1053 if constants.DDP_LOCAL_MINOR not in dyn_params:
1054 raise errors.ProgrammerError("Invalid dynamic params for drbd device %s"
1055 % dyn_params)
1056
1057 aminor = dyn_params[constants.DDP_LOCAL_MINOR]
1058
1059 info = DRBD8.GetProcInfo()
1060 if info.HasMinorStatus(aminor):
1061 status = info.GetMinorStatus(aminor)
1062 in_use = status.is_in_use
1063 else:
1064 in_use = False
1065 if in_use:
1066 base.ThrowError("drbd%d: minor is already in use at Create() time",
1067 aminor)
1068 meta = children[1]
1069 meta.Assemble()
1070 if not meta.Attach():
1071 base.ThrowError("drbd%d: can't attach to meta device '%s'",
1072 aminor, meta)
1073 cls._CheckMetaSize(meta.dev_path)
1074 cls._InitMeta(aminor, meta.dev_path)
1075 return cls(unique_id, children, size, params, dyn_params)
1076
1079 """Check if we can read from the given device.
1080
1081 This tries to read the first 128k of the device.
1082
1083 @type path: string
1084
1085 """
1086 try:
1087 utils.ReadFile(path, size=_DEVICE_READ_SIZE)
1088 return True
1089 except EnvironmentError:
1090 logging.warning("Can't read from device %s", path, exc_info=True)
1091 return False
1092