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 self._secret = unique_id[5]
205
206 if children:
207 if not _CanReadDevice(children[1].dev_path):
208 logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
209 children = []
210 super(DRBD8Dev, self).__init__(unique_id, children, size, params,
211 dyn_params, *args)
212 self.major = self._DRBD_MAJOR
213
214 info = DRBD8.GetProcInfo()
215 version = info.GetVersion()
216 if version["k_major"] != 8:
217 base.ThrowError("Mismatch in DRBD kernel version and requested ganeti"
218 " usage: kernel is %s.%s, ganeti wants 8.x",
219 version["k_major"], version["k_minor"])
220
221 if version["k_minor"] <= 3:
222 self._show_info_cls = drbd_info.DRBD83ShowInfo
223 else:
224 self._show_info_cls = drbd_info.DRBD84ShowInfo
225
226 self._cmd_gen = DRBD8.GetCmdGenerator(info)
227
228 if (self._lhost is not None and self._lhost == self._rhost and
229 self._lport == self._rport):
230 raise ValueError("Invalid configuration data, same local/remote %s, %s" %
231 (unique_id, dyn_params))
232 self.Attach()
233
234 @staticmethod
236 """Return the path to a drbd device for a given minor.
237
238 @type minor: int
239 @rtype: string
240
241 """
242 return "/dev/drbd%d" % minor
243
245 """Set our parameters based on the given minor.
246
247 This sets our minor variable and our dev_path.
248
249 @type minor: int
250
251 """
252 if minor is None:
253 self.minor = self.dev_path = None
254 self.attached = False
255 else:
256 self.minor = minor
257 self.dev_path = self._DevPath(minor)
258 self.attached = True
259
260 @staticmethod
292
294 """Return the `drbdsetup show` data.
295
296 @type minor: int
297 @param minor: the minor to collect show output for
298 @rtype: string
299
300 """
301 result = utils.RunCmd(self._cmd_gen.GenShowCmd(minor))
302 if result.failed:
303 logging.error("Can't display the drbd config: %s - %s",
304 result.fail_reason, result.output)
305 return None
306 return result.stdout
307
309 """Return parsed information from `drbdsetup show`.
310
311 @type minor: int
312 @param minor: the minor to return information for
313 @rtype: dict as described in L{drbd_info.BaseShowInfo.GetDevInfo}
314
315 """
316 return self._show_info_cls.GetDevInfo(self._GetShowData(minor))
317
319 """Test if our local config matches with an existing device.
320
321 The parameter should be as returned from `_GetShowInfo()`. This
322 method tests if our local backing device is the same as the one in
323 the info parameter, in effect testing if we look like the given
324 device.
325
326 @type info: dict as described in L{drbd_info.BaseShowInfo.GetDevInfo}
327 @rtype: boolean
328
329 """
330 if self._children:
331 backend, meta = self._children
332 else:
333 backend = meta = None
334
335 if backend is not None:
336 retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
337 else:
338 retval = ("local_dev" not in info)
339
340 if meta is not None:
341 retval = retval and ("meta_dev" in info and
342 info["meta_dev"] == meta.dev_path)
343 if "meta_index" in info:
344 retval = retval and info["meta_index"] == 0
345 else:
346 retval = retval and ("meta_dev" not in info and
347 "meta_index" not in info)
348 return retval
349
351 """Test if our network config matches with an existing device.
352
353 The parameter should be as returned from `_GetShowInfo()`. This
354 method tests if our network configuration is the same as the one
355 in the info parameter, in effect testing if we look like the given
356 device.
357
358 @type info: dict as described in L{drbd_info.BaseShowInfo.GetDevInfo}
359 @rtype: boolean
360
361 """
362 if (((self._lhost is None and not ("local_addr" in info)) and
363 (self._rhost is None and not ("remote_addr" in info)))):
364 return True
365
366 if self._lhost is None:
367 return False
368
369 if not ("local_addr" in info and
370 "remote_addr" in info):
371 return False
372
373 retval = (info["local_addr"] == (self._lhost, self._lport))
374 retval = (retval and
375 info["remote_addr"] == (self._rhost, self._rport))
376 return retval
377
379 """Configure the local part of a DRBD device.
380
381 @type minor: int
382 @param minor: the minor to assemble locally
383 @type backend: string
384 @param backend: path to the data device to use
385 @type meta: string
386 @param meta: path to the meta device to use
387 @type size: int
388 @param size: size in MiB
389
390 """
391 cmds = self._cmd_gen.GenLocalInitCmds(minor, backend, meta,
392 size, self.params)
393
394 for cmd in cmds:
395 result = utils.RunCmd(cmd)
396 if result.failed:
397 base.ThrowError("drbd%d: can't attach local disk: %s",
398 minor, result.output)
399
400 - def _AssembleNet(self, minor, net_info, dual_pri=False, hmac=None,
401 secret=None):
402 """Configure the network part of the device.
403
404 @type minor: int
405 @param minor: the minor to assemble the network for
406 @type net_info: (string, int, string, int)
407 @param net_info: tuple containing the local address, local port, remote
408 address and remote port
409 @type dual_pri: boolean
410 @param dual_pri: whether two primaries should be allowed or not
411 @type hmac: string
412 @param hmac: the HMAC algorithm to use
413 @type secret: string
414 @param secret: the shared secret to use
415
416 """
417 lhost, lport, rhost, rport = net_info
418 if None in net_info:
419
420
421 self._ShutdownNet(minor)
422 return
423
424 if dual_pri:
425 protocol = constants.DRBD_MIGRATION_NET_PROTOCOL
426 else:
427 protocol = self.params[constants.LDP_PROTOCOL]
428
429
430
431
432
433
434
435 sync_errors = self._SetMinorSyncParams(minor, self.params)
436 if sync_errors:
437 base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
438 (minor, utils.CommaJoin(sync_errors)))
439
440 family = self._GetNetFamily(minor, lhost, rhost)
441
442 cmd = self._cmd_gen.GenNetInitCmd(minor, family, lhost, lport,
443 rhost, rport, protocol,
444 dual_pri, hmac, secret, self.params)
445
446 result = utils.RunCmd(cmd)
447 if result.failed:
448 base.ThrowError("drbd%d: can't setup network: %s - %s",
449 minor, result.fail_reason, result.output)
450
451 def _CheckNetworkConfig():
452 info = self._GetShowInfo(minor)
453 if not "local_addr" in info or not "remote_addr" in info:
454 raise utils.RetryAgain()
455
456 if (info["local_addr"] != (lhost, lport) or
457 info["remote_addr"] != (rhost, rport)):
458 raise utils.RetryAgain()
459
460 try:
461 utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
462 except utils.RetryTimeout:
463 base.ThrowError("drbd%d: timeout while configuring network", minor)
464
465
466 try:
467
468
469 old_minor = self.minor
470 self._SetFromMinor(minor)
471 sync_errors = self.SetSyncParams(self.params)
472 if sync_errors:
473 base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
474 (self.minor, utils.CommaJoin(sync_errors)))
475 finally:
476
477
478 self._SetFromMinor(old_minor)
479
480 @staticmethod
494
496 """Add a disk to the DRBD device.
497
498 @type devices: list of L{BlockDev}
499 @param devices: a list of exactly two L{BlockDev} objects; the first
500 denotes the data device, the second the meta device for this DRBD device
501
502 """
503 if self.minor is None:
504 base.ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
505 self._aminor)
506 if len(devices) != 2:
507 base.ThrowError("drbd%d: need two devices for AddChildren", self.minor)
508 info = self._GetShowInfo(self.minor)
509 if "local_dev" in info:
510 base.ThrowError("drbd%d: already attached to a local disk", self.minor)
511 backend, meta = devices
512 if backend.dev_path is None or meta.dev_path is None:
513 base.ThrowError("drbd%d: children not ready during AddChildren",
514 self.minor)
515 backend.Open()
516 meta.Open()
517 self._CheckMetaSize(meta.dev_path)
518 self._InitMeta(DRBD8.FindUnusedMinor(), meta.dev_path)
519
520 self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
521 self._children = devices
522
524 """Detach the drbd device from local storage.
525
526 @type devices: list of L{BlockDev}
527 @param devices: a list of exactly two L{BlockDev} objects; the first
528 denotes the data device, the second the meta device for this DRBD device
529
530 """
531 if self.minor is None:
532 base.ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
533 self._aminor)
534
535 info = self._GetShowInfo(self.minor)
536 if "local_dev" not in info:
537 return
538 if len(self._children) != 2:
539 base.ThrowError("drbd%d: we don't have two children: %s", self.minor,
540 self._children)
541 if self._children.count(None) == 2:
542 logging.warning("drbd%d: requested detach while detached", self.minor)
543 return
544 if len(devices) != 2:
545 base.ThrowError("drbd%d: we need two children in RemoveChildren",
546 self.minor)
547 for child, dev in zip(self._children, devices):
548 if dev != child.dev_path:
549 base.ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
550 " RemoveChildren", self.minor, dev, child.dev_path)
551
552 self._ShutdownLocal(self.minor)
553 self._children = []
554
556 """Set the parameters of the DRBD syncer.
557
558 This is the low-level implementation.
559
560 @type minor: int
561 @param minor: the drbd minor whose settings we change
562 @type params: dict
563 @param params: LD level disk parameters related to the synchronization
564 @rtype: list
565 @return: a list of error messages
566
567 """
568 cmd = self._cmd_gen.GenSyncParamsCmd(minor, params)
569 result = utils.RunCmd(cmd)
570 if result.failed:
571 msg = ("Can't change syncer rate: %s - %s" %
572 (result.fail_reason, result.output))
573 logging.error(msg)
574 return [msg]
575
576 return []
577
579 """Set the synchronization parameters of the DRBD syncer.
580
581 See L{BlockDev.SetSyncParams} for parameter description.
582
583 """
584 if self.minor is None:
585 err = "Not attached during SetSyncParams"
586 logging.info(err)
587 return [err]
588
589 children_result = super(DRBD8Dev, self).SetSyncParams(params)
590 children_result.extend(self._SetMinorSyncParams(self.minor, params))
591 return children_result
592
594 """Pauses or resumes the sync of a DRBD device.
595
596 See L{BlockDev.PauseResumeSync} for parameter description.
597
598 """
599 if self.minor is None:
600 logging.info("Not attached during PauseSync")
601 return False
602
603 children_result = super(DRBD8Dev, self).PauseResumeSync(pause)
604
605 if pause:
606 cmd = self._cmd_gen.GenPauseSyncCmd(self.minor)
607 else:
608 cmd = self._cmd_gen.GenResumeSyncCmd(self.minor)
609
610 result = utils.RunCmd(cmd)
611 if result.failed:
612 logging.error("Can't %s: %s - %s", cmd,
613 result.fail_reason, result.output)
614 return not result.failed and children_result
615
617 """Return the current status data from /proc/drbd for this device.
618
619 @rtype: DRBD8Status
620
621 """
622 if self.minor is None:
623 base.ThrowError("drbd%d: GetStats() called while not attached",
624 self._aminor)
625 info = DRBD8.GetProcInfo()
626 if not info.HasMinorStatus(self.minor):
627 base.ThrowError("drbd%d: can't find myself in /proc", self.minor)
628 return info.GetMinorStatus(self.minor)
629
631 """Returns the sync status of the device.
632
633 If sync_percent is None, it means all is ok
634 If estimated_time is None, it means we can't estimate
635 the time needed, otherwise it's the time left in seconds.
636
637 We set the is_degraded parameter to True on two conditions:
638 network not connected or local disk missing.
639
640 We compute the ldisk parameter based on whether we have a local
641 disk or not.
642
643 @rtype: objects.BlockDevStatus
644
645 """
646 if self.minor is None and not self.Attach():
647 base.ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
648
649 stats = self.GetProcStatus()
650 is_degraded = not stats.is_connected or not stats.is_disk_uptodate
651
652 if stats.is_disk_uptodate:
653 ldisk_status = constants.LDS_OKAY
654 elif stats.is_diskless:
655 ldisk_status = constants.LDS_FAULTY
656 else:
657 ldisk_status = constants.LDS_UNKNOWN
658
659 return objects.BlockDevStatus(dev_path=self.dev_path,
660 major=self.major,
661 minor=self.minor,
662 sync_percent=stats.sync_percent,
663 estimated_time=stats.est_time,
664 is_degraded=is_degraded,
665 ldisk_status=ldisk_status)
666
667 - def Open(self, force=False):
668 """Make the local state primary.
669
670 If the 'force' parameter is given, DRBD is instructed to switch the device
671 into primary mode. Since this is a potentially dangerous operation, the
672 force flag should be only given after creation, when it actually is
673 mandatory.
674
675 """
676 if self.minor is None and not self.Attach():
677 logging.error("DRBD cannot attach to a device during open")
678 return False
679
680 cmd = self._cmd_gen.GenPrimaryCmd(self.minor, force)
681
682 result = utils.RunCmd(cmd)
683 if result.failed:
684 base.ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
685 result.output)
686
688 """Make the local state secondary.
689
690 This will, of course, fail if the device is in use.
691
692 """
693 if self.minor is None and not self.Attach():
694 base.ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
695 cmd = self._cmd_gen.GenSecondaryCmd(self.minor)
696 result = utils.RunCmd(cmd)
697 if result.failed:
698 base.ThrowError("drbd%d: can't switch drbd device to secondary: %s",
699 self.minor, result.output)
700
702 """Removes network configuration.
703
704 This method shutdowns the network side of the device.
705
706 The method will wait up to a hardcoded timeout for the device to
707 go into standalone after the 'disconnect' command before
708 re-configuring it, as sometimes it takes a while for the
709 disconnect to actually propagate and thus we might issue a 'net'
710 command while the device is still connected. If the device will
711 still be attached to the network and we time out, we raise an
712 exception.
713
714 """
715 if self.minor is None:
716 base.ThrowError("drbd%d: disk not attached in re-attach net",
717 self._aminor)
718
719 if None in (self._lhost, self._lport, self._rhost, self._rport):
720 base.ThrowError("drbd%d: DRBD disk missing network info in"
721 " DisconnectNet()", self.minor)
722
723 class _DisconnectStatus(object):
724 def __init__(self, ever_disconnected):
725 self.ever_disconnected = ever_disconnected
726
727 dstatus = _DisconnectStatus(base.IgnoreError(self._ShutdownNet, self.minor))
728
729 def _WaitForDisconnect():
730 if self.GetProcStatus().is_standalone:
731 return
732
733
734
735
736 dstatus.ever_disconnected = \
737 base.IgnoreError(self._ShutdownNet, self.minor) or \
738 dstatus.ever_disconnected
739
740 raise utils.RetryAgain()
741
742
743 start_time = time.time()
744
745 try:
746
747 utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
748 self._NET_RECONFIG_TIMEOUT)
749 except utils.RetryTimeout:
750 if dstatus.ever_disconnected:
751 msg = ("drbd%d: device did not react to the"
752 " 'disconnect' command in a timely manner")
753 else:
754 msg = "drbd%d: can't shutdown network, even after multiple retries"
755
756 base.ThrowError(msg, self.minor)
757
758 reconfig_time = time.time() - start_time
759 if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
760 logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
761 self.minor, reconfig_time)
762
764 """Reconnects the network.
765
766 This method connects the network side of the device with a
767 specified multi-master flag. The device needs to be 'Standalone'
768 but have valid network configuration data.
769
770 @type multimaster: boolean
771 @param multimaster: init the network in dual-primary mode
772
773 """
774 if self.minor is None:
775 base.ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
776
777 if None in (self._lhost, self._lport, self._rhost, self._rport):
778 base.ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
779
780 status = self.GetProcStatus()
781
782 if not status.is_standalone:
783 base.ThrowError("drbd%d: device is not standalone in AttachNet",
784 self.minor)
785
786 self._AssembleNet(self.minor,
787 (self._lhost, self._lport, self._rhost, self._rport),
788 dual_pri=multimaster, hmac=constants.DRBD_HMAC_ALG,
789 secret=self._secret)
790
792 """Check if our minor is configured.
793
794 This doesn't do any device configurations - it only checks if the
795 minor is in a state different from Unconfigured.
796
797 Note that this function will not change the state of the system in
798 any way (except in case of side-effects caused by reading from
799 /proc).
800
801 """
802 used_devs = DRBD8.GetUsedDevs()
803 if self._aminor in used_devs:
804 minor = self._aminor
805 else:
806 minor = None
807
808 self._SetFromMinor(minor)
809 return minor is not None
810
812 """Assemble the drbd.
813
814 Method:
815 - if we have a configured device, we try to ensure that it matches
816 our config
817 - if not, we create it from zero
818 - anyway, set the device parameters
819
820 """
821 super(DRBD8Dev, self).Assemble()
822
823 self.Attach()
824 if self.minor is None:
825
826 self._FastAssemble()
827 else:
828
829
830 self._SlowAssemble()
831
833 """Assembles the DRBD device from a (partially) configured device.
834
835 In case of partially attached (local device matches but no network
836 setup), we perform the network attach. If successful, we re-test
837 the attach if can return success.
838
839 """
840
841
842 net_data = (self._lhost, self._lport, self._rhost, self._rport)
843 for minor in (self._aminor,):
844 info = self._GetShowInfo(minor)
845 match_l = self._MatchesLocal(info)
846 match_r = self._MatchesNet(info)
847
848 if match_l and match_r:
849
850 break
851
852 if match_l and not match_r and "local_addr" not in info:
853
854 self._AssembleNet(minor, net_data, hmac=constants.DRBD_HMAC_ALG,
855 secret=self._secret)
856 if self._MatchesNet(self._GetShowInfo(minor)):
857 break
858 else:
859 base.ThrowError("drbd%d: network attach successful, but 'drbdsetup"
860 " show' disagrees", minor)
861
862 if match_r and "local_dev" not in info:
863
864 self._AssembleLocal(minor, self._children[0].dev_path,
865 self._children[1].dev_path, self.size)
866 if self._MatchesLocal(self._GetShowInfo(minor)):
867 break
868 else:
869 base.ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
870 " show' disagrees", minor)
871
872
873
874
875
876 if (match_l and "local_dev" in info and
877 not match_r and "local_addr" in info):
878
879
880
881
882 try:
883 self._ShutdownNet(minor)
884 except errors.BlockDeviceError, err:
885 base.ThrowError("drbd%d: device has correct local storage, wrong"
886 " remote peer and is unable to disconnect in order"
887 " to attach to the correct peer: %s", minor, str(err))
888
889
890
891 self._AssembleNet(minor, net_data, hmac=constants.DRBD_HMAC_ALG,
892 secret=self._secret)
893 if self._MatchesNet(self._GetShowInfo(minor)):
894 break
895 else:
896 base.ThrowError("drbd%d: network attach successful, but 'drbdsetup"
897 " show' disagrees", minor)
898
899 else:
900 minor = None
901
902 self._SetFromMinor(minor)
903 if minor is None:
904 base.ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
905 self._aminor)
906
908 """Assemble the drbd device from zero.
909
910 This is run when in Assemble we detect our minor is unused.
911
912 """
913 minor = self._aminor
914 if self._children and self._children[0] and self._children[1]:
915 self._AssembleLocal(minor, self._children[0].dev_path,
916 self._children[1].dev_path, self.size)
917 if self._lhost and self._lport and self._rhost and self._rport:
918 self._AssembleNet(minor,
919 (self._lhost, self._lport, self._rhost, self._rport),
920 hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
921 self._SetFromMinor(minor)
922
924 """Detach from the local device.
925
926 I/Os will continue to be served from the remote device. If we
927 don't have a remote device, this operation will fail.
928
929 @type minor: int
930 @param minor: the device to detach from the local device
931
932 """
933 cmd = self._cmd_gen.GenDetachCmd(minor)
934 result = utils.RunCmd(cmd)
935 if result.failed:
936 base.ThrowError("drbd%d: can't detach local disk: %s",
937 minor, result.output)
938
940 """Disconnect from the remote peer.
941
942 This fails if we don't have a local device.
943
944 @type minor: boolean
945 @param minor: the device to disconnect from the remote peer
946
947 """
948 family = self._GetNetFamily(minor, self._lhost, self._rhost)
949 cmd = self._cmd_gen.GenDisconnectCmd(minor, family,
950 self._lhost, self._lport,
951 self._rhost, self._rport)
952 result = utils.RunCmd(cmd)
953 if result.failed:
954 base.ThrowError("drbd%d: can't shutdown network: %s",
955 minor, result.output)
956
958 """Shutdown the DRBD device.
959
960 """
961 if self.minor is None and not self.Attach():
962 logging.info("drbd%d: not attached during Shutdown()", self._aminor)
963 return
964
965 try:
966 DRBD8.ShutdownAll(self.minor)
967 finally:
968 self.minor = None
969 self.dev_path = None
970
972 """Stub remove for DRBD devices.
973
974 """
975 self.Shutdown()
976
978 """Rename a device.
979
980 This is not supported for drbd devices.
981
982 """
983 raise errors.ProgrammerError("Can't rename a drbd device")
984
985 - def Grow(self, amount, dryrun, backingstore, excl_stor):
986 """Resize the DRBD device and its backing storage.
987
988 See L{BlockDev.Grow} for parameter description.
989
990 """
991 if self.minor is None:
992 base.ThrowError("drbd%d: Grow called while not attached", self._aminor)
993 if len(self._children) != 2 or None in self._children:
994 base.ThrowError("drbd%d: cannot grow diskless device", self.minor)
995 self._children[0].Grow(amount, dryrun, backingstore, excl_stor)
996 if dryrun or backingstore:
997
998
999 return
1000 cmd = self._cmd_gen.GenResizeCmd(self.minor, self.size + amount)
1001 result = utils.RunCmd(cmd)
1002 if result.failed:
1003 base.ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1004
1005 @classmethod
1036
1037 @classmethod
1038 - def Create(cls, unique_id, children, size, spindles, params, excl_stor,
1039 dyn_params, *_):
1040 """Create a new DRBD8 device.
1041
1042 Since DRBD devices are not created per se, just assembled, this
1043 function only initializes the metadata.
1044
1045 """
1046 if len(children) != 2:
1047 raise errors.ProgrammerError("Invalid setup for the drbd device")
1048 if excl_stor:
1049 raise errors.ProgrammerError("DRBD device requested with"
1050 " exclusive_storage")
1051 if constants.DDP_LOCAL_MINOR not in dyn_params:
1052 raise errors.ProgrammerError("Invalid dynamic params for drbd device %s"
1053 % dyn_params)
1054
1055 aminor = dyn_params[constants.DDP_LOCAL_MINOR]
1056
1057 info = DRBD8.GetProcInfo()
1058 if info.HasMinorStatus(aminor):
1059 status = info.GetMinorStatus(aminor)
1060 in_use = status.is_in_use
1061 else:
1062 in_use = False
1063 if in_use:
1064 base.ThrowError("drbd%d: minor is already in use at Create() time",
1065 aminor)
1066 meta = children[1]
1067 meta.Assemble()
1068 if not meta.Attach():
1069 base.ThrowError("drbd%d: can't attach to meta device '%s'",
1070 aminor, meta)
1071 cls._CheckMetaSize(meta.dev_path)
1072 cls._InitMeta(aminor, meta.dev_path)
1073 return cls(unique_id, children, size, params, dyn_params)
1074
1077 """Check if we can read from the given device.
1078
1079 This tries to read the first 128k of the device.
1080
1081 @type path: string
1082
1083 """
1084 try:
1085 utils.ReadFile(path, size=_DEVICE_READ_SIZE)
1086 return True
1087 except EnvironmentError:
1088 logging.warning("Can't read from device %s", path, exc_info=True)
1089 return False
1090