Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion afs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# you type "import afs"
import afs.fs
import afs.acl
import afs.pts
import afs.pts # noqa

# Basic logging support for this package.
# Add a NullHandler to avoid "No handlers could be found" error
Expand Down
2 changes: 1 addition & 1 deletion afs/_fs.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def _whereis(char* path):
_whereis(path) -> list()

Low-level implementation of the "whereis" command. Raises
OSError, and EINVAL usually indicates the path isn't in AFS.
OSError, and EINVAL or ENOSYS usually indicates the path isn't in AFS.
Returns a list of IP addresses. It is the caller's responsibility
to get hostnames if that's desired. If anything goes wrong converting
the 32-bit network numbers into IP addresses, that network number is
Expand Down
30 changes: 21 additions & 9 deletions afs/acl.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from afs import _acl
from afs._acl import READ, WRITE, INSERT, LOOKUP, DELETE, LOCK, ADMINISTER, \
USR0, USR1, USR2, USR3, USR4, USR5, USR6, USR7
from afs._acl import getCallerAccess

_canonical = {
"read": "rl",
Expand Down Expand Up @@ -41,18 +40,23 @@ def rightsToEnglish(s):
else:
return ''


def readRights(s):
"""Canonicalizes string rights to bitmask"""
if s in _canonical: s = _canonical[s]
if s in _canonical:
s = _canonical[s]
return _parseRights(s)


def showRights(r):
"""Takes a bitmask and returns a rwlidka string"""
s = ""
for char,mask in _charBitAssoc:
if r & mask == mask: s += char
for char, mask in _charBitAssoc:
if r & mask == mask:
s += char
return s


def _parseRights(s):
"""Parses a rwlid... rights tring to bitmask"""
r = 0
Expand All @@ -63,13 +67,15 @@ def _parseRights(s):
raise ValueError
return r


def _parseAcl(inp):
lines = inp.split("\n")
npos = int(lines[0].split(" ")[0])
pos = {}
neg = {}
for l in lines[2:]:
if l == "": continue
if l == "":
continue
name, acl = l.split()
if npos:
npos -= 1
Expand All @@ -79,6 +85,7 @@ def _parseAcl(inp):
neg[name] = int(acl)
return (pos, neg)


def _unparseAcl(pos, neg):
npos = len(pos)
nneg = len(neg)
Expand All @@ -89,6 +96,7 @@ def _unparseAcl(pos, neg):
acl += "%s\t%d\n" % n
return acl


class ACL(object):
def __init__(self, pos, neg):
"""
Expand All @@ -99,32 +107,36 @@ def __init__(self, pos, neg):
"""
self.pos = pos
self.neg = neg

@staticmethod
def retrieve(dir, follow=True):
"""Retrieve the ACL for an AFS directory"""
pos, neg = _parseAcl(_acl.getAcl(dir, follow))
return ACL(pos, neg)

def apply(self, dir, follow=True):
"""Apply the ACL to a directory"""
self._clean()
_acl.setAcl(dir, _unparseAcl(self.pos, self.neg), follow)

def _clean(self):
"""Clean an ACL by removing any entries whose bitmask is 0"""
for n,a in self.pos.items():
for n, a in self.pos.items():
if a == 0:
del self.pos[n]
for n,a in self.neg.items():
for n, a in self.neg.items():
if a == 0:
del self.neg[n]

def set(self, user, bitmask, negative=False):
"""Set the bitmask for a given user"""
if bitmask < 0 or bitmask > max(_char2bit.values()):
raise ValueError, "Invalid bitmask"
raise ValueError("Invalid bitmask")
if negative:
self.neg[user] = bitmask
else:
self.pos[user] = bitmask

def remove(self, user, negative=False):
"""Convenience function to removeSet the bitmask for a given user"""
self.set(user, 0, negative)

23 changes: 14 additions & 9 deletions afs/fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,48 @@
import os.path
import socket
import logging
import math
import sys

from afs import _fs
log = logging.getLogger('afs.fs')


class AFSVolumeStatus(object):
def __init__(self, volstat):
if type(volstat) is not tuple or len(volstat) != 3:
raise TypeError("AFSVolumeStatus takes a tuple of (str,str,dict)")
if [type(x) for x in volstat] != [str,str,dict]:
if [type(x) for x in volstat] != [str, str, dict]:
raise TypeError("AFSVolumeStatus takes a tuple of (str,str,dict)")
self.name = volstat[0]
self.offline_message = volstat[1]
for k,v in volstat[2].items():
for k, v in volstat[2].items():
setattr(self, k, v)

def __repr__(self):
return self.__class__.__name__ + ': ' + repr(self.__dict__)


def whichcell(path):
"""Return the cell name or None if the path is not in AFS"""
try:
return _fs.whichcell(path)
except OSError as e:
if e.errno == errno.EINVAL:
if e.errno in (errno.EINVAL, errno.ENOSYS):
return None
else:
raise


def inafs(path):
"""Return True if a path is in AFS."""
try:
_fs.whichcell(path)
except OSError as e:
if e.errno in (errno.EINVAL, errno.ENOENT):
if e.errno in (errno.EINVAL, errno.ENOENT, errno.ENOSYS):
return False

return True


def lsmount(path):
"""Return a volume name for a mountpoint."""
# os.path.realpath will take care of ensuring we don't
Expand All @@ -51,11 +53,12 @@ def lsmount(path):
try:
return _fs._lsmount(dirname, basename)
except OSError as e:
if e.errno == errno.EINVAL:
if e.errno in (errno.EINVAL, errno.ENOSYS):
return None
else:
raise


def examine(path):
"""
Given a path in AFS, tells you about the path and volume that
Expand All @@ -69,7 +72,7 @@ def examine(path):
try:
_volstat = _fs._volume_status(path)
except OSError as e:
if e.errno == errno.EINVAL:
if e.errno in (errno.EINVAL, errno.ENOSYS):
return None
else:
raise
Expand All @@ -80,6 +83,7 @@ def examine(path):
pass
return (AFSVolumeStatus(_volstat), _fid)


def _reverse_lookup(ip):
"""
Convenience function to provide best-effort reverse-resolution
Expand All @@ -93,6 +97,7 @@ def _reverse_lookup(ip):
log.warning("IndexError while reverse-resolving IP, shouldn't happen")
return ip


def whereis(path, dns=True):
"""
Return a list of hostnames and/or IP addresses representing
Expand All @@ -108,7 +113,7 @@ def whereis(path, dns=True):
else:
return addrs
except OSError as e:
if e.errno == errno.EINVAL:
if e.errno in (errno.EINVAL, errno.ENOSYS):
return None
else:
raise
15 changes: 13 additions & 2 deletions afs/pts.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
except AttributeError:
SetMixin = object


class PTRelationSet(SetMixin):
"""Collection class for the groups/members of a PTEntry.

Expand Down Expand Up @@ -251,6 +252,7 @@ def __repr__(self):

def _get_id(self):
return self._id

def _set_id(self, val):
del self._pts._cache[self._id]
self._pts._ChangeEntry(self.id, newid=val)
Expand All @@ -260,13 +262,15 @@ def _set_id(self, val):

def _get_name(self):
return self._name

def _set_name(self, val):
self._pts._ChangeEntry(self.id, newname=val)
self._name = val
name = property(_get_name, _set_name)

def _get_krbname(self):
return self._pts._AfsToKrb5(self.name)

def _set_krbname(self, val):
self.name = self._pts._Krb5ToAfs(val)
krbname = property(_get_krbname, _set_krbname)
Expand All @@ -279,6 +283,7 @@ def _get_count(self):
def _get_flags(self):
self._loadEntry()
return self._flags

def _set_flags(self, val):
self._pts._SetFields(self.id, access=val)
self._flags = val
Expand All @@ -287,6 +292,7 @@ def _set_flags(self, val):
def _get_ngroups(self):
self._loadEntry()
return self._ngroups

def _set_ngroups(self, val):
self._pts._SetFields(self.id, groups=val)
self._ngroups = val
Expand All @@ -295,6 +301,7 @@ def _set_ngroups(self, val):
def _get_nusers(self):
self._loadEntry()
return self._nusers

def _set_nusers(self, val):
self._pts._SetFields(self.id, users=val)
self._nusers = val
Expand All @@ -303,6 +310,7 @@ def _set_nusers(self, val):
def _get_owner(self):
self._loadEntry()
return self._owner

def _set_owner(self, val):
self._pts._ChangeEntry(self.id, newoid=self._pts.getEntry(val).id)
self._owner = val
Expand All @@ -319,7 +327,8 @@ def _loadEntry(self):
for field in self._attrs:
setattr(self, '_%s' % field, getattr(info, field))
for field in self._entry_attrs:
setattr(self, '_%s' % field, self._pts.getEntry(getattr(info, field)))
setattr(self, '_%s' % field, self._pts.getEntry(
getattr(info, field)))


PTS_UNAUTH = 0
Expand Down Expand Up @@ -378,7 +387,7 @@ def getEntry(self, ident):
if isinstance(ident, PTEntry):
if ident._pts is not self:
raise TypeError("Entry '%s' is from a different cell." %
elt)
ident)
return ident

elif isinstance(ident, basestring):
Expand Down Expand Up @@ -406,12 +415,14 @@ def expire(self):

def _get_umax(self):
return self._ListMax()[0]

def _set_umax(self, val):
self._SetMaxUserId(val)
umax = property(_get_umax, _set_umax)

def _get_gmax(self):
return self._ListMax()[1]

def _set_gmax(self, val):
self._SetMaxGroupId(val)
gmax = property(_get_gmax, _set_gmax)
10 changes: 9 additions & 1 deletion afs/tests/test__pts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from afs._pts import PTS
import nose


def get_this_cell():
# Feel free to add more places ThisCell might show up
to_try = ['/private/var/db/openafs/etc/ThisCell',
Expand All @@ -11,35 +12,42 @@ def get_this_cell():
if os.path.isfile(f):
return open(f).read().strip()


def test_init_home_cell():
p = PTS()
assert p.cell == get_this_cell(), "PTS doesn't initialize to ThisCell when none specified."
assert p.cell == get_this_cell(), \
"PTS doesn't initialize to ThisCell when none specified."


def test_init_other_cell():
cell = 'zone.mit.edu'
p = PTS('zone.mit.edu')
assert p.cell == cell, "PTS doesn't initialize to provided cell."


def test_user_name_to_id():
p = PTS()
name = 'broder'
id = p._NameToId(name)
assert id == 41803, "PTS can't convert user name to ID."
assert p._IdToName(id) == name, "PTS can't convert user ID to name."


def test_group_name_to_id():
p = PTS()
name = 'system:administrators'
id = p._NameToId(name)
assert id == -204, "PTS can't convert group name to ID."
assert p._IdToName(id) == name, "PTS can't convert group ID to name."


def test_name_or_id():
p = PTS()
name = 'system:administrators'
id = -204
assert p._NameOrId(name) == id, "PTS._NameOrId can't identify name."
assert p._NameOrId(id) == id, "PTS._NameOrId can't identify ID."


if __name__ == '__main__':
nose.main()
Loading