Revision ee788eff snf-image-helper/disklabel.py

b/snf-image-helper/disklabel.py
26 26
import os
27 27
import cStringIO
28 28
import optparse
29
import abc
29 30

  
30 31
from collections import namedtuple
31 32
from collections import OrderedDict
......
45 46
    """Represents a Master Boot Record."""
46 47
    class Partition(object):
47 48
        """Represents a partition entry in MBR"""
48
        format = "<B3sB3sLL"
49
        fmt = "<B3sB3sLL"
49 50

  
50 51
        def __init__(self, raw_part):
51 52
            """Create a Partition instance"""
......
55 56
             self.end,
56 57
             self.first_sector,
57 58
             self.sector_count
58
             ) = struct.unpack(self.format, raw_part)
59
             ) = struct.unpack(self.fmt, raw_part)
59 60

  
60 61
        def pack(self):
61 62
            """Pack the partition values into a binary string"""
62
            return struct.pack(self.format,
63
            return struct.pack(self.fmt,
63 64
                               self.status,
64 65
                               self.start,
65 66
                               self.type,
......
70 71
        @staticmethod
71 72
        def size():
72 73
            """Returns the size of an MBR partition entry"""
73
            return struct.calcsize(MBR.Partition.format)
74
            return struct.calcsize(MBR.Partition.fmt)
74 75

  
75 76
        def __str__(self):
76 77
            start = self.unpack_chs(self.start)
......
115 116
    def __init__(self, block):
116 117
        """Create an MBR instance"""
117 118

  
118
        self.format = "<444s2x16s16s16s16s2s"
119
        self.fmt = "<444s2x16s16s16s16s2s"
119 120
        raw_part = {}     # Offset  Length          Contents
120 121
        (self.code_area,  # 0       440(max. 446)   code area
121 122
                          # 440     2(optional)     disk signature
......
125 126
         raw_part[2],     # 478     16              Partition 2
126 127
         raw_part[3],     # 494     16              Partition 3
127 128
         self.signature   # 510     2               MBR signature
128
         ) = struct.unpack(self.format, block)
129
         ) = struct.unpack(self.fmt, block)
129 130

  
130 131
        self.part = {}
131 132
        for i in range(4):
......
133 134

  
134 135
    def size(self):
135 136
        """Return the size of a Master Boot Record."""
136
        return struct.calcsize(self.format)
137
        return struct.calcsize(self.fmt)
137 138

  
138 139
    def pack(self):
139 140
        """Pack an MBR to a binary string."""
140
        return struct.pack(self.format,
141
        return struct.pack(self.fmt,
141 142
                           self.code_area,
142 143
                           self.part[0].pack(),
143 144
                           self.part[1].pack(),
......
233 234

  
234 235
class DisklabelBase(object):
235 236
    """Disklabel base class"""
237
    __metaclass__ = abc.ABCMeta
236 238

  
237 239
    def __init__(self, device):
238 240
        """Create a Disklabel instance"""
239
        pass
240 241

  
241
    @property
242
    def format(self):
243
        """Fields format string"""
244
        raise NotImplementedError
245

  
246
    @property
247
    def field(self):
248
        """Diskalabel Fields data structure"""
249
        raise NotImplementedError
242
        # Subclasses need to overwrite this
243
        self.field = None
244
        self.ptable = None
250 245

  
251
    @property
252
    def ptable(self):
253
        """Partition Table data structure"""
254
        raise NotImplementedError
246
    @abc.abstractproperty
247
    def fmt(self):
248
        """Fields format string for the disklabel fields"""
249
        pass
255 250

  
256 251
    def pack(self, checksum=None):
257 252
        """Return a binary copy of the Disklabel block"""
......
263 258
        if checksum is not None:
264 259
            out['checksum'] = checksum
265 260

  
266
        return struct.pack(self.format, * out.values() + [self.ptable.pack()])
261
        return struct.pack(self.fmt, * out.values() + [self.ptable.pack()])
267 262

  
268 263
    def compute_checksum(self):
269 264
        """Compute the checksum of the disklabel"""
......
280 275

  
281 276
        return checksum
282 277

  
278
    @abc.abstractmethod
283 279
    def enlarge(self, new_size):
284 280
        """Enlarge the disk and return the last usable sector"""
285
        raise NotImplementedError
281
        pass
286 282

  
287 283
    def write_to(self, device):
288 284
        """Write the disklabel to a device"""
......
291 287
        device.seek(BLOCKSIZE, os.SEEK_CUR)
292 288
        device.write(self.pack())
293 289

  
290
    @abc.abstractmethod
294 291
    def enlarge_last_partition(self):
295 292
        """Enlarge the last partition to consume all the usable space"""
296
        raise NotImplementedError
293
        pass
297 294

  
295
    @abc.abstractmethod
298 296
    def get_last_partition_id(self):
299 297
        """Get the ID of the last partition"""
300
        raise NotImplementedError
298
        pass
301 299

  
300
    @abc.abstractmethod
302 301
    def __str__(self):
303 302
        """Print the Disklabel"""
304
        raise NotImplementedError
303
        pass
305 304

  
306 305

  
307 306
class PartitionTableBase(object):
308 307
    """Base Class for disklabel partition tables"""
308
    __metaclass__ = abc.ABCMeta
309 309

  
310
    @property
311
    def format(self):
312
        """Partition table format string"""
313
        raise NotImplementedError
310
    @abc.abstractproperty
311
    def fmt(self):
312
        """Partition fields format string"""
313
        pass
314 314

  
315
    Partition = namedtuple('Partition', '')
315
    @abc.abstractproperty
316
    def fields(self):
317
        """The partition fields"""
318
        pass
316 319

  
317 320
    def __init__(self, ptable, pnumber):
318 321
        """Create a Partition Table instance"""
319
        self.part = []
320 322

  
321
        size = struct.calcsize(self.format)
323
        self.Partition = namedtuple('Partition', self.fields)
324
        self.part = []
322 325

  
326
        size = struct.calcsize(self.fmt)
323 327
        raw = cStringIO.StringIO(ptable)
324 328
        try:
325 329
            for _ in xrange(pnumber):
326 330
                self.part.append(
327
                    self.Partition(*struct.unpack(self.format, raw.read(size)))
331
                    self.Partition(*struct.unpack(self.fmt, raw.read(size)))
328 332
                    )
329 333
        finally:
330 334
            raw.close()
......
340 344
        """Packs the partition table into a binary string."""
341 345
        ret = ""
342 346
        for i in xrange(len(self.part)):
343
            ret += struct.pack(self.format, *self.part[i])
347
            ret += struct.pack(self.fmt, *self.part[i])
344 348
        return ret + ((364 - len(self.part) * 16) * '\x00')
345 349

  
346 350

  
......
350 354
    class PartitionTable(PartitionTableBase):
351 355
        """Represents a BSD Partition Table"""
352 356

  
353
        format = "<IIIBBH"
354
        Partition = namedtuple(
355
            'Partition',  # Offset  Length Contents
356
            ['size',      # 0       4      Number of sectors in partition
357
             'offset',    # 4       4      Starting sector
358
             'fsize',     # 8       4      Filesystem basic fragment size
359
             'fstype',    # 12      1      Filesystem type
360
             'frag',      # 13      1      Filesystem fragments per block
361
             'cpg'        # 14      2      Filesystem cylinders per group
362
             ])
357
        @property
358
        def fmt(self):
359
            """Partition fields format string"""
360
            return "<IIIBBH"
361

  
362
        @property
363
        def fields(self):
364
            """The partition fields"""
365
            return [    # Offset  Length Contents
366
                'size',     # 0       4      Number of sectors in partition
367
                'offset',   # 4       4      Starting sector of the partition
368
                'fsize',    # 8       4      File system basic fragment size
369
                'fstype',   # 12      1      File system type
370
                'frag',     # 13      1      File system fragments per block
371
                'cpg'       # 14      2      File system cylinders per group
372
                ]
373

  
374
    @property
375
    def fmt(self):
376
        return "<IHH16s16sIIIIIIHHIHHHHIII20s20sIHHII364s"
363 377

  
364 378
    def __init__(self, device):
365 379
        """Create a BSD DiskLabel instance"""
......
369 383
        device.seek(BLOCKSIZE, os.SEEK_CUR)
370 384
        sector1 = device.read(BLOCKSIZE)
371 385

  
372
        self.format = "<IHH16s16sIIIIIIHHIHHHHIII20s20sIHHII364s"
373 386
        d_ = OrderedDict()      # Off  Len    Content
374 387
        (d_["magic"],           # 0    4      Magic
375 388
         d_["dtype"],           # 4    2      Drive Type
......
400 413
         d_["bbsize"],          # 132  4      size of boot area at sn0, bytes
401 414
         d_["sbsize"],          # 136  4      Max size of fs superblock, bytes
402 415
         ptable_raw             # 140  16*16  Partition Table
403
         ) = struct.unpack(self.format, sector1)
416
         ) = struct.unpack(self.fmt, sector1)
404 417

  
405 418
        assert d_['magic'] == d_['magic2'] == DISKMAGIC, "Disklabel not valid"
406 419
        self.ptable = self.PartitionTable(ptable_raw, d_['npartitions'])
407 420
        self.field = d_
408 421

  
422
    def enlarge(self, new_size):
423
        raise NotImplementedError
424

  
425
    def enlarge_last_partition(self):
426
        raise NotImplementedError
427

  
428
    def get_last_partition_id(self):
429
        raise NotImplementedError
430

  
409 431
    def __str__(self):
410 432
        """Print the Disklabel"""
411 433

  
......
453 475

  
454 476
    class PartitionTable(PartitionTableBase):
455 477
        """Reprepsents an OpenBSD Partition Table"""
456
        format = "<IIHHBBH"
457
        Partition = namedtuple(
458
            'Partition',  # Offset  Length Contents
459
            ['size',      # 0       4      Number of sectors in the partition
460
             'offset',    # 4       4      Starting sector
461
             'offseth',   # 8       2      Starting sector (high part)
462
             'sizeh',     # 10      2      Number of sectors (high part)
463
             'fstype',    # 12      1      Filesystem type
464
             'frag',      # 13      1      Filesystem Fragments per block
465
             'cpg'        # 14      2      FS cylinders per group
466
             ])
478

  
479
        @property
480
        def fmt(self):
481
            return "<IIHHBBH"
482

  
483
        @property
484
        def fields(self):
485
            return [    # Offset  Length Contents
486
                'size',     # 0       4      Number of sectors in the partition
487
                'offset',   # 4       4      Starting sector of the partition
488
                'offseth',  # 8       2      Starting sector (high part)
489
                'sizeh',    # 10      2      Number of sectors (high part)
490
                'fstype',   # 12      1      Filesystem type
491
                'frag',     # 13      1      Filesystem Fragments per block
492
                'cpg'       # 14      2      FS cylinders per group
493
                ]
467 494

  
468 495
        def setpsize(self, i, size):
469 496
            """Set size for partition i"""
......
487 514
            """Get offset for partition i"""
488 515
            return (self.part[i].offseth << 32) + self.part[i].offset
489 516

  
517
    @property
518
    def fmt(self):
519
        return "<IHH16s16sIIIIII8sIHHIII20sHH16sIHHII364s"
520

  
490 521
    def __init__(self, device):
491 522
        """Create a DiskLabel instance"""
523

  
492 524
        super(OpenBSD_Disklabel, self).__init__(device)
493 525

  
494 526
        # Disklabel starts at offset one
495 527
        device.seek(BLOCKSIZE, os.SEEK_CUR)
496 528
        sector1 = device.read(BLOCKSIZE)
497 529

  
498
        self.format = "<IHH16s16sIIIIII8sIHHIII20sHH16sIHHII364s"
499 530
        d_ = OrderedDict()   # Off  Len    Content
500 531
        (d_["magic"],        # 0    4      Magic
501 532
         d_["dtype"],        # 4    2      Drive Type
......
525 556
         d_["bbsize"],       # 132  4      size of boot area at sn0, bytes
526 557
         d_["sbsize"],       # 136  4      Max size of fs superblock, bytes
527 558
         ptable_raw          # 140  16*16  Partition Table
528
         ) = struct.unpack(self.format, sector1)
559
         ) = struct.unpack(self.fmt, sector1)
529 560

  
530 561
        assert d_['magic'] == d_['magic2'] == DISKMAGIC, "Disklabel not valid"
531 562
        self.ptable = self.PartitionTable(ptable_raw, d_['npartitions'])

Also available in: Unified diff