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 elif stats.is_in_resync:
659 ldisk_status = constants.LDS_SYNC
660 else:
661 ldisk_status = constants.LDS_UNKNOWN
662
663 return objects.BlockDevStatus(dev_path=self.dev_path,
664 major=self.major,
665 minor=self.minor,
666 sync_percent=stats.sync_percent,
667 estimated_time=stats.est_time,
668 is_degraded=is_degraded,
669 ldisk_status=ldisk_status)
670
671 - def Open(self, force=False, exclusive=True):
672 """Make the local state primary.
673
674 If the 'force' parameter is given, DRBD is instructed to switch the device
675 into primary mode. Since this is a potentially dangerous operation, the
676 force flag should be only given after creation, when it actually is
677 mandatory.
678
679 """
680 if self.minor is None and not self.Attach():
681 logging.error("DRBD cannot attach to a device during open")
682 return False
683
684 cmd = self._cmd_gen.GenPrimaryCmd(self.minor, force)
685
686 result = utils.RunCmd(cmd)
687 if result.failed:
688 base.ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
689 result.output)
690
692 """Make the local state secondary.
693
694 This will, of course, fail if the device is in use.
695
696 """
697 if self.minor is None and not self.Attach():
698 base.ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
699 cmd = self._cmd_gen.GenSecondaryCmd(self.minor)
700 result = utils.RunCmd(cmd)
701 if result.failed:
702 base.ThrowError("drbd%d: can't switch drbd device to secondary: %s",
703 self.minor, result.output)
704
706 """Removes network configuration.
707
708 This method shutdowns the network side of the device.
709
710 The method will wait up to a hardcoded timeout for the device to
711 go into standalone after the 'disconnect' command before
712 re-configuring it, as sometimes it takes a while for the
713 disconnect to actually propagate and thus we might issue a 'net'
714 command while the device is still connected. If the device will
715 still be attached to the network and we time out, we raise an
716 exception.
717
718 """
719 if self.minor is None:
720 base.ThrowError("drbd%d: disk not attached in re-attach net",
721 self._aminor)
722
723 if None in (self._lhost, self._lport, self._rhost, self._rport):
724 base.ThrowError("drbd%d: DRBD disk missing network info in"
725 " DisconnectNet()", self.minor)
726
727 class _DisconnectStatus(object):
728 def __init__(self, ever_disconnected):
729 self.ever_disconnected = ever_disconnected
730
731 dstatus = _DisconnectStatus(base.IgnoreError(self._ShutdownNet, self.minor))
732
733 def _WaitForDisconnect():
734 if self.GetProcStatus().is_standalone:
735 return
736
737
738
739
740 dstatus.ever_disconnected = \
741 base.IgnoreError(self._ShutdownNet, self.minor) or \
742 dstatus.ever_disconnected
743
744 raise utils.RetryAgain()
745
746
747 start_time = time.time()
748
749 try:
750
751 utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
752 self._NET_RECONFIG_TIMEOUT)
753 except utils.RetryTimeout:
754 if dstatus.ever_disconnected:
755 msg = ("drbd%d: device did not react to the"
756 " 'disconnect' command in a timely manner")
757 else:
758 msg = "drbd%d: can't shutdown network, even after multiple retries"
759
760 base.ThrowError(msg, self.minor)
761
762 reconfig_time = time.time() - start_time
763 if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
764 logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
765 self.minor, reconfig_time)
766
768 """Reconnects the network.
769
770 This method connects the network side of the device with a
771 specified multi-master flag. The device needs to be 'Standalone'
772 but have valid network configuration data.
773
774 @type multimaster: boolean
775 @param multimaster: init the network in dual-primary mode
776
777 """
778 if self.minor is None:
779 base.ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
780
781 if None in (self._lhost, self._lport, self._rhost, self._rport):
782 base.ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
783
784 status = self.GetProcStatus()
785
786 if not status.is_standalone:
787 base.ThrowError("drbd%d: device is not standalone in AttachNet",
788 self.minor)
789
790 self._AssembleNet(self.minor,
791 (self._lhost, self._lport, self._rhost, self._rport),
792 dual_pri=multimaster, hmac=constants.DRBD_HMAC_ALG,
793 secret=self._secret)
794
796 """Check if our minor is configured.
797
798 This doesn't do any device configurations - it only checks if the
799 minor is in a state different from Unconfigured.
800
801 Note that this function will not change the state of the system in
802 any way (except in case of side-effects caused by reading from
803 /proc).
804
805 """
806 used_devs = DRBD8.GetUsedDevs()
807 if self._aminor in used_devs:
808 minor = self._aminor
809 else:
810 minor = None
811
812 self._SetFromMinor(minor)
813 return minor is not None
814
816 """Assemble the drbd.
817
818 Method:
819 - if we have a configured device, we try to ensure that it matches
820 our config
821 - if not, we create it from zero
822 - anyway, set the device parameters
823
824 """
825 super(DRBD8Dev, self).Assemble()
826
827 self.Attach()
828 if self.minor is None:
829
830 self._FastAssemble()
831 else:
832
833
834 self._SlowAssemble()
835
837 """Assembles the DRBD device from a (partially) configured device.
838
839 In case of partially attached (local device matches but no network
840 setup), we perform the network attach. If successful, we re-test
841 the attach if can return success.
842
843 """
844
845
846 net_data = (self._lhost, self._lport, self._rhost, self._rport)
847 for minor in (self._aminor,):
848 info = self._GetShowInfo(minor)
849 match_l = self._MatchesLocal(info)
850 match_r = self._MatchesNet(info)
851
852 if match_l and match_r:
853
854 break
855
856 if match_l and not match_r and "local_addr" not in info:
857
858 self._AssembleNet(minor, net_data, hmac=constants.DRBD_HMAC_ALG,
859 secret=self._secret)
860 if self._MatchesNet(self._GetShowInfo(minor)):
861 break
862 else:
863 base.ThrowError("drbd%d: network attach successful, but 'drbdsetup"
864 " show' disagrees", minor)
865
866 if match_r and "local_dev" not in info:
867
868 self._AssembleLocal(minor, self._children[0].dev_path,
869 self._children[1].dev_path, self.size)
870 if self._MatchesLocal(self._GetShowInfo(minor)):
871 break
872 else:
873 base.ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
874 " show' disagrees", minor)
875
876
877
878
879
880 if (match_l and "local_dev" in info and
881 not match_r and "local_addr" in info):
882
883
884
885
886 try:
887 self._ShutdownNet(minor)
888 except errors.BlockDeviceError, err:
889 base.ThrowError("drbd%d: device has correct local storage, wrong"
890 " remote peer and is unable to disconnect in order"
891 " to attach to the correct peer: %s", minor, str(err))
892
893
894
895 self._AssembleNet(minor, net_data, hmac=constants.DRBD_HMAC_ALG,
896 secret=self._secret)
897 if self._MatchesNet(self._GetShowInfo(minor)):
898 break
899 else:
900 base.ThrowError("drbd%d: network attach successful, but 'drbdsetup"
901 " show' disagrees", minor)
902
903 else:
904 minor = None
905
906 self._SetFromMinor(minor)
907 if minor is None:
908 base.ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
909 self._aminor)
910
912 """Assemble the drbd device from zero.
913
914 This is run when in Assemble we detect our minor is unused.
915
916 """
917 minor = self._aminor
918 if self._children and self._children[0] and self._children[1]:
919 self._AssembleLocal(minor, self._children[0].dev_path,
920 self._children[1].dev_path, self.size)
921 if self._lhost and self._lport and self._rhost and self._rport:
922 self._AssembleNet(minor,
923 (self._lhost, self._lport, self._rhost, self._rport),
924 hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
925 self._SetFromMinor(minor)
926
928 """Detach from the local device.
929
930 I/Os will continue to be served from the remote device. If we
931 don't have a remote device, this operation will fail.
932
933 @type minor: int
934 @param minor: the device to detach from the local device
935
936 """
937 cmd = self._cmd_gen.GenDetachCmd(minor)
938 result = utils.RunCmd(cmd)
939 if result.failed:
940 base.ThrowError("drbd%d: can't detach local disk: %s",
941 minor, result.output)
942
944 """Disconnect from the remote peer.
945
946 This fails if we don't have a local device.
947
948 @type minor: boolean
949 @param minor: the device to disconnect from the remote peer
950
951 """
952 family = self._GetNetFamily(minor, self._lhost, self._rhost)
953 cmd = self._cmd_gen.GenDisconnectCmd(minor, family,
954 self._lhost, self._lport,
955 self._rhost, self._rport)
956 result = utils.RunCmd(cmd)
957 if result.failed:
958 base.ThrowError("drbd%d: can't shutdown network: %s",
959 minor, result.output)
960
962 """Shutdown the DRBD device.
963
964 """
965 if self.minor is None and not self.Attach():
966 logging.info("drbd%d: not attached during Shutdown()", self._aminor)
967 return
968
969 try:
970 DRBD8.ShutdownAll(self.minor)
971 finally:
972 self.minor = None
973 self.dev_path = None
974
976 """Stub remove for DRBD devices.
977
978 """
979 self.Shutdown()
980
982 """Rename a device.
983
984 This is not supported for drbd devices.
985
986 """
987 raise errors.ProgrammerError("Can't rename a drbd device")
988
989 - def Grow(self, amount, dryrun, backingstore, excl_stor):
990 """Resize the DRBD device and its backing storage.
991
992 See L{BlockDev.Grow} for parameter description.
993
994 """
995 if self.minor is None:
996 base.ThrowError("drbd%d: Grow called while not attached", self._aminor)
997 if len(self._children) != 2 or None in self._children:
998 base.ThrowError("drbd%d: cannot grow diskless device", self.minor)
999 self._children[0].Grow(amount, dryrun, backingstore, excl_stor)
1000 if dryrun or backingstore:
1001
1002
1003 return
1004 cmd = self._cmd_gen.GenResizeCmd(self.minor, self.size + amount)
1005 result = utils.RunCmd(cmd)
1006 if result.failed:
1007 base.ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1008
1009 @classmethod
1041
1042 @classmethod
1043 - def Create(cls, unique_id, children, size, spindles, params, excl_stor,
1044 dyn_params, *_):
1045 """Create a new DRBD8 device.
1046
1047 Since DRBD devices are not created per se, just assembled, this
1048 function only initializes the metadata.
1049
1050 """
1051 if len(children) != 2:
1052 raise errors.ProgrammerError("Invalid setup for the drbd device")
1053 if excl_stor:
1054 raise errors.ProgrammerError("DRBD device requested with"
1055 " exclusive_storage")
1056 if constants.DDP_LOCAL_MINOR not in dyn_params:
1057 raise errors.ProgrammerError("Invalid dynamic params for drbd device %s"
1058 % dyn_params)
1059
1060 aminor = dyn_params[constants.DDP_LOCAL_MINOR]
1061
1062 info = DRBD8.GetProcInfo()
1063 if info.HasMinorStatus(aminor):
1064 status = info.GetMinorStatus(aminor)
1065 in_use = status.is_in_use
1066 else:
1067 in_use = False
1068 if in_use:
1069 base.ThrowError("drbd%d: minor is already in use at Create() time",
1070 aminor)
1071 meta = children[1]
1072 meta.Assemble()
1073 if not meta.Attach():
1074 base.ThrowError("drbd%d: can't attach to meta device '%s'",
1075 aminor, meta)
1076 cls._CheckMetaSize(meta.dev_path)
1077 cls._InitMeta(aminor, meta.dev_path)
1078 return cls(unique_id, children, size, params, dyn_params)
1079
1082 """Check if we can read from the given device.
1083
1084 This tries to read the first 128k of the device.
1085
1086 @type path: string
1087
1088 """
1089 try:
1090 utils.ReadFile(path, size=_DEVICE_READ_SIZE)
1091 return True
1092 except EnvironmentError:
1093 logging.warning("Can't read from device %s", path, exc_info=True)
1094 return False
1095