This file is indexed.

/usr/share/pyshared/rtslib/loop.py is in python-rtslib 2.1-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
'''
Implements the RTS SAS loopback classes.

This file is part of RTSLib Community Edition.
Copyright (c) 2011 by RisingTide Systems LLC

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, version 3 (AGPLv3).

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
'''

import re
import os
import glob
import uuid
import shutil

# rtslib modules
from root import RTSRoot
from node import CFSNode
from utils import RTSLibError, RTSLibBrokenLink
from utils import generate_wwn, fwrite, fread

class LUN(CFSNode):
    '''
    This is an interface to RTS Target LUNs in configFS.
    A LUN is identified by its parent Nexus and LUN index.
    '''

    # LUN private stuff

    def __init__(self, parent_nexus, lun, storage_object=None, alias=None):
        '''
        A LUN object can be instanciated in two ways:
            - B{Creation mode}: If I{storage_object} is specified, the
              underlying configFS object will be created with that parameter.
              No LUN with the same I{lun} index can pre-exist in the parent
              Nexus in that mode, or instanciation will fail.
            - B{Lookup mode}: If I{storage_object} is not set, then the LUN
              will be bound to the existing configFS LUN object of the parent
              Nexus having the specified I{lun} index. The underlying configFS
              object must already exist in that mode.

        @param parent_nexus: The parent Nexus object.
        @type parent_nexus: Nexus
        @param lun: The LUN index.
        @type lun: 0-255
        @param storage_object: The storage object to be exported as a LUN.
        @type storage_object: StorageObject subclass
        @param alias: An optional parameter to manually specify the LUN alias.
        You probably do not need this.
        @type alias: string
        @return: A LUN object.
        '''
        super(LUN, self).__init__()

        if isinstance(parent_nexus, Nexus):
            self._parent_nexus = parent_nexus
        else:
            raise RTSLibError("Invalid parent Nexus.")

        try:
            lun = int(lun)
        except ValueError:
            raise RTSLibError("Invalid LUN index: %s" % str(lun))
        else:
            if lun > 255 or lun < 0:
                raise RTSLibError("Invalid LUN index, it must be "
                                  + "between 0 and 255: %d" % lun)
            self._lun = lun

        self._path = "%s/lun/lun_%d" % (self.parent_nexus.path, self.lun)

        if storage_object is None and alias is not None:
            raise RTSLibError("The alias parameter has no meaning "
                              + "without the storage_object parameter.")

        if storage_object is not None:
            self._create_in_cfs_ine('create')
            try:
                self._configure(storage_object, alias)
            except:
                self.delete()
                raise
        else:
            self._create_in_cfs_ine('lookup')

    def __str__(self):
        try:
            storage_object = self.storage_object
        except RTSLibBrokenLink:
            desc = "[BROKEN STORAGE LINK]"
        else:
            backstore = storage_object.backstore
            soname = storage_object.name
            if backstore.plugin.startswith("rd"):
                path = "ramdisk"
            else:
                path = storage_object.udev_path
            desc = "-> %s%d '%s' (%s)" \
                    % (backstore.plugin, backstore.index, soname, path)
        return "LUN %d %s" % (self.lun, desc)

    def _create_in_cfs_ine(self, mode):
        super(LUN, self)._create_in_cfs_ine(mode)

    def _configure(self, storage_object, alias):
        self._check_self()
        if alias is None:
            alias = str(uuid.uuid4())[-10:]
        else:
            alias = str(alias).strip()
            if '/' in alias:
                raise RTSLibError("Invalid alias: %s", alias)
        destination = "%s/%s" % (self.path, alias)
        from tcm import StorageObject
        if isinstance(storage_object, StorageObject):
            if storage_object.exists:
                source = storage_object.path
            else:
                raise RTSLibError("The storage_object does not exist "
                                  + "in configFS.")
        else:
            raise RTSLibError("Invalid storage object.")

        os.symlink(source, destination)

    def _get_alias(self):
        self._check_self()
        alias = None
        for path in os.listdir(self.path):
            if os.path.islink("%s/%s" % (self.path, path)):
                alias = os.path.basename(path)
                break
        if alias is None:
            raise RTSLibBrokenLink("Broken LUN in configFS, no " \
                                         + "storage object attached.")
        else:
            return alias

    def _get_storage_object(self):
        self._check_self()
        alias_path = None
        for path in os.listdir(self.path):
            if os.path.islink("%s/%s" % (self.path, path)):
                alias_path = os.path.realpath("%s/%s" % (self.path, path))
                break
        if alias_path is None:
            raise RTSLibBrokenLink("Broken LUN in configFS, no " \
                                         + "storage object attached.")
        rtsroot = RTSRoot()
        for storage_object in rtsroot.storage_objects:
            if storage_object.path == alias_path:
                return storage_object
        raise RTSLibBrokenLink("Broken storage object link in LUN.")

    def _get_parent_nexus(self):
        return self._parent_nexus

    def _get_lun(self):
        return self._lun

    def _get_alua_metadata_path(self):
        return "%s/lun_%d" % (self.parent_nexus.alua_metadata_path, self.lun)

    # LUN public stuff

    def delete(self):
        '''
        If the underlying configFS object does not exists, this method does
        nothing. If the underlying configFS object exists, this method attempts
        to delete it.
        '''
        self._check_self()
        try:
            link = self.alias
        except RTSLibBrokenLink:
            pass
        else:
            if os.path.islink("%s/%s" % (self.path, link)):
                os.unlink("%s/%s" % (self.path, link))

        super(LUN, self).delete()
        if os.path.isdir(self.alua_metadata_path):
            shutil.rmtree(self.alua_metadata_path)

    alua_metadata_path = property(_get_alua_metadata_path,
            doc="Get the ALUA metadata directory path for the LUN.")
    parent_nexus = property(_get_parent_nexus,
            doc="Get the parent Nexus object.")
    lun = property(_get_lun,
            doc="Get the LUN index as an int.")
    storage_object = property(_get_storage_object,
            doc="Get the storage object attached to the LUN.")
    alias = property(_get_alias,
            doc="Get the LUN alias.")

class Nexus(CFSNode):
    '''
    This is a an interface to Target Portal Groups in configFS.
    A Nexus is identified by its parent Target object and its nexus Tag.
    To a Nexus object is attached a list of NetworkPortals.
    '''

    # Nexus private stuff

    def __init__(self, parent_target, tag, mode='any'):
        '''
        @param parent_target: The parent Target object of the Nexus.
        @type parent_target: Target
        @param tag: The Nexus Tag (TPGT).
        @type tag: int > 0
        @param mode:An optionnal string containing the object creation mode:
            - I{'any'} means the configFS object will be either looked up or
              created.
            - I{'lookup'} means the object MUST already exist configFS.
            - I{'create'} means the object must NOT already exist in configFS.
        @type mode:string
        @return: A Nexus object.
        '''

        super(Nexus, self).__init__()

        try:
            self._tag = int(tag)
        except ValueError:
            raise RTSLibError("Invalid Tag.")

        if tag < 1:
            raise RTSLibError("Invalig Tag, it must be >0.")

        if isinstance(parent_target, Target):
            self._parent_target = parent_target
        else:
            raise RTSLibError("Invalid parent Target.")

        self._path = "%s/tpgt_%d" % (self.parent_target.path, self.tag)
        self._create_in_cfs_ine(mode)

    def __str__(self):
        try:
            initiator = self.initiator
        except RTSLibError:
            initiator = "[BROKEN]"
        return "Nexus %d / initiator %s" % (self.tag, initiator)

    def _get_initiator(self):
        nexus_path = self._path + "/nexus"
        if os.path.isfile(nexus_path):
            try:
                initiator = fread(nexus_path)
            except IOError, msg:
                raise RTSLibError("Cannot read Nexus initiator address "
                                  + "(>=4.0 style, %s): %s."
                                  % (nexus_path, msg))
        else:
            try:
                initiator = os.listdir(nexus_path)[0]
            except IOError, msg:
                raise RTSLibError("Cannot read Nexus initiator address "
                                  + "(<4.0 style, %s): %s."
                                  % (nexus_path, msg))
        return initiator.strip()

    def _get_tag(self):
        return self._tag

    def _get_parent_target(self):
        return self._parent_target

    def _create_in_cfs_ine(self, mode):
        super(Nexus, self)._create_in_cfs_ine(mode)

        if not os.path.isdir(self.alua_metadata_path):
            os.makedirs(self.alua_metadata_path)

        if self._fresh:
            initiator = generate_wwn('naa')
            nexus_path = self._path + "/nexus"
            if os.path.isfile(nexus_path):
                try:
                    fwrite(nexus_path, initiator)
                except IOError, msg:
                    raise RTSLibError("Cannot create Nexus initiator "
                                      + "(>=4.0 style, %s): %s."
                                      % (nexus_path, msg))
            else:
                try:
                    os.makedirs(nexus_path + "/" + initiator)
                except IOError, msg:
                    raise RTSLibError("Cannot create Nexus initiator."
                                      + "(<4.0 style, %s): %s."
                                      % (nexus_path, msg))

    def _list_luns(self):
        self._check_self()
        luns = []
        lun_dirs = [os.path.basename(path)
                    for path in os.listdir("%s/lun" % self.path)]
        for lun_dir in lun_dirs:
            lun = lun_dir.split('_')[1]
            lun = int(lun)
            luns.append(LUN(self, lun))
        return luns

    def _control(self, command):
        self._check_self()
        path = "%s/control" % self.path
        fwrite(path, "%s\n" % str(command))

    def _get_alua_metadata_path(self):
        return "%s/%s+%d"  \
                % (self.alua_metadata_dir,
                   self.parent_target.naa, self.tag)

    # Nexus public stuff

    def delete(self):
        '''
        Recursively deletes a Nexus object.
        This will delete all attached LUN, and then the Nexus itself.
        '''
        self._check_self()
        for lun in self.luns:
            lun.delete()

        # TODO: check that ALUA MD removal works while removing Nexus
        if os.path.isdir(self.alua_metadata_path):
            shutil.rmtree(self.alua_metadata_path)

        nexus_path = self._path + "/nexus"
        if os.path.isfile(nexus_path):
            try:
                fwrite(nexus_path, "NULL")
            except IOError, msg:
                raise RTSLibError("Cannot delete Nexus initiator "
                                  + "(>=4.0 style, %s): %s."
                                  % (nexus_path, msg))
        else:
            try:
                os.rmdir(nexus_path + "/" + self.initiator)
            except IOError, msg:
                raise RTSLibError("Cannot delete Nexus initiator."
                                  + "(<4.0 style, %s): %s."
                                  % (nexus_path, msg))

        super(Nexus, self).delete()

    def lun(self, lun, storage_object=None, alias=None):
        '''
        Same as LUN() but without specifying the parent_nexus.
        '''
        self._check_self()
        return LUN(self, lun=lun, storage_object=storage_object, alias=alias)

    alua_metadata_path = property(_get_alua_metadata_path,
                                  doc="Get the ALUA metadata directory path " \
                                  + "for the Nexus.")
    tag = property(_get_tag,
            doc="Get the Nexus Tag as an int.")
    initiator = property(_get_initiator,
            doc="Get the Nexus initiator address as a string.")
    parent_target = property(_get_parent_target,
                             doc="Get the parent Target object to which the " \
                             + "Nexus is attached.")
    luns = property(_list_luns,
                    doc="Get the list of LUN objects currently attached " \
                    + "to the Nexus.")

class Target(CFSNode):
    '''
    This is an interface to loopback SAS Targets in configFS.
    A Target is identified by its naa SAS address.
    To a Target is attached a list of Nexus objects.
    '''

    # Target private stuff

    def __init__(self, naa=None, mode='any'):
        '''
        @param naa: The optionnal Target's address.
            If no address or an empty address is specified, one will be
            generated for you.
        @type naa: string
        @param mode:An optionnal string containing the object creation mode:
            - I{'any'} means the configFS object will be either looked up
              or created.
            - I{'lookup'} means the object MUST already exist configFS.
            - I{'create'} means the object must NOT already exist in configFS.
        @type mode:string
        @return: A Target object.
        '''

        super(Target, self).__init__()

        if naa is None:
            naa = generate_wwn('naa')
        else:
            naa = str(naa).lower().strip()
        self._naa = naa
        self._path = "%s/loopback/%s" % (self.configfs_dir, self._naa)
        if not self:
            if not re.match(
                "naa\.[0-9]+", naa) \
               or re.search(' ', naa) \
               or re.search('_', naa):
                raise RTSLibError("Invalid naa: %s"
                                  % naa)
        self._create_in_cfs_ine(mode)

    def __str__(self):
        return "SAS loopback %s" % self.naa

    def _list_nexuses(self):
        self._check_self()
        nexuses = []
        nexus_dirs = glob.glob("%s/tpgt*" % self.path)
        for nexus_dir in nexus_dirs:
            tag = os.path.basename(nexus_dir).split('_')[1]
            tag = int(tag)
            nexuses.append(Nexus(self, tag, 'lookup'))
        return nexuses

    def _get_naa(self):
        return self._naa

    # Target public stuff

    def delete(self):
        '''
        Recursively deletes a Target object.
        This will delete all attached Nexus objects and then the Target itself.
        '''
        self._check_self()
        for nexus in self.nexuses:
            nexus.delete()
        super(Target, self).delete()

    def nexus(self, tag, mode='any'):
        '''
        Same as Nexus() but without the parent_target parameter.
        '''
        self._check_self()
        return Nexus(self, tag=tag, mode=mode)

    naa = property(_get_naa,
                   doc="Get the naa of the Target object as a string.")
    nexuses = property(_list_nexuses,
                       doc="Get the list of Nexus objects currently "
                       + "attached to the Target.")

def _test():
    import doctest
    doctest.testmod()

if __name__ == "__main__":
    _test()