afs_xg.c
afs_xg.h
*.so
+__pycache__
--- /dev/null
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+# -*- python-mode -*-
+
+"""AFS Toolkit Volume management command set
+"""
+
+__copyright__ = """
+Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+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 General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+import sys, os
+
+# Try to detect where it is run from and set prefix and the search path.
+# It is assumed that the user installed Afs using the --prefix= option
+prefix, bin = os.path.split(sys.path[0])
+
+if bin == 'bin' and prefix != sys.prefix:
+ sys.prefix = prefix
+ sys.exec_prefix = prefix
+
+ major, minor = sys.version_info[0:2]
+ local_path = [os.path.join(prefix, 'lib', 'python'),
+ os.path.join(prefix, 'lib', 'python%s.%s' % (major, minor)),
+ os.path.join(prefix, 'lib', 'python%s.%s' % (major, minor),
+ 'site-packages')]
+ sys.path = local_path + sys.path
+
+from afs.main import main
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+#
+# Parse an argument list for a subcommand
+#
+__copyright__ = """
+Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public Licence
+as published by the Free Software Foundation; either version
+2 of the Licence, or (at your option) any later version.
+"""
+
+def get_cell(switch, params):
+ from afs.lib.cell import cell
+ return cell(params[0])
+
+def get_vlserver(switch, params):
+ from afs.lib.vlserver import vlserver
+ return vlserver(params[0])
+
+def get_volume_name(switch, params):
+ return params
+
+def get_machine_name(switch, params):
+ return params
+
+def get_path_name(switch, params):
+ return params
+
+def get_file_name(switch, params):
+ return params
+
+def get_partition_id(switch, params):
+ part = params[0]
+ if part.startswith("/vice"):
+ part = part[5:]
+ elif part.startswith("vice"):
+ part = part[4:]
+
+ if part.isnumeric():
+ part = int(part)
+ elif len(part) == 1 and part >= "a" and part <= "z":
+ part = bytes(part)[0] - 97
+ elif len(part) == 2 and part[0] >= "a" and part[0] <= "z" and part[1] >= "a" and part[1] <= "z":
+ part = (bytes(part)[0] - 97) * 26 | bytes(part)[1] - 97
+ else:
+ raise RuntimeError("Unparseable partition ID '" + params[0] + "'")
+
+ if part > 255:
+ raise RuntimeError("Partition ID '" + params[0] + "' out of range")
+ return part
+
+def get_auth(switch, params):
+ return params
+
+def get_dummy(switch, params):
+ return params
+
+###############################################################################
+#
+# Parse an argument list according to a defined set of switches.
+#
+# The following is an excerpt from the AFS Reference Manual, Introduction to
+# AFS commands:
+#
+# CONDITIONS FOR OMITTING SWITCHES
+#
+# It is always acceptable to type the switch part of an argument, but in
+# many cases it is not necessary. Specifically, switches can be omitted if
+# the following conditions are met.
+#
+# (*) All of the command's required arguments appear in the order prescribed
+# by the syntax statement.
+#
+# (*) No switch is provided for any argument.
+#
+# (*) There is only one value for each argument (but note the important
+# exception discussed in the following paragraph).
+#
+# Omitting switches is possible only because there is a prescribed order for
+# each command's arguments. When the issuer does not include switches, the
+# command interpreter relies instead on the order of arguments; it assumes
+# that the first element after the operation code is the command's first
+# argument, the next element is the command's second argument, and so
+# on. The important exception is when a command's final required argument
+# accepts multiple values. In this case, the command interpreter assumes
+# that the issuer has correctly provided one value for each argument up
+# through the final one, so any additional values at the end belong to the
+# final argument.
+#
+# The following list describes the rules for omitting switches from the
+# opposite perspective: an argument's switch must be provided when any of
+# the following conditions apply.
+#
+# (*) The command's arguments do not appear in the prescribed order.
+#
+# (*) An optional argument is omitted but a subsequent optional argument is
+# provided.
+#
+# (*) A switch is provided for a preceding argument.
+#
+# (*) More than one value is supplied for a preceding argument (which must
+# take multiple values, of course); without a switch on the current
+# argument, the command interpreter assumes that the current argument is
+# another value for the preceding argument.
+#
+###############################################################################
+def parse_arguments(args, available_arguments):
+ result = {}
+ need_switch = False
+ i = 0 # Given arguments index
+ av = 0 # Available arguments index
+
+ #print args
+
+ if len(args) == 0:
+ if len(available_arguments) > 0 and available_arguments[0][0] == "r":
+ raise RuntimeError("Missing required parameters")
+ return result
+
+ # Process all the optional arguments or switch-based required arguments
+ while i < len(args):
+ match = False
+ params = []
+
+ if args[i][0] != "-":
+ # Deal with positional arguments
+ if need_switch:
+ raise RuntimeError("Need switch before argument " + i)
+ if av >= len(available_arguments):
+ raise RuntimeError("Unexpected positional argument")
+ match = available_arguments[av]
+ pattern = match[2]
+ if pattern[0] == "f":
+ raise RuntimeError("Unexpected positional argument")
+ av = av + 1
+
+ params.append(args[i])
+ i = i + 1
+
+ if match[2][1] == "m":
+ # All remaining arguments up to the next switch belong to this
+ while i < len(args):
+ if args[i][0] == "-":
+ break
+ params.append(args[i])
+ i = i + 1
+ need_switch = True
+
+ else:
+ # Deal with tagged arguments
+ switch = args[i][1:]
+ i = i + 1
+
+ if switch == "help":
+ raise RuntimeError("Print help message")
+ if switch == "":
+ raise RuntimeError("Missing switch name")
+
+ # Look up the switch in the table of possible arguments and flags
+ for j in available_arguments:
+ if j[0] == switch:
+ match = j
+ break
+ if j[0].startswith(switch):
+ if match:
+ raise RuntimeError("Ambiguous switch name abbreviation '-" + switch + "'")
+ match = j
+ if not match:
+ raise RuntimeError("Unsupported switch '-" + switch + "'")
+
+ # Reject repeat flags
+ if match[0] in result:
+ raise RuntimeError("Duplicate switch '-" + switch + "' not permitted")
+
+ # Arrange the parameters associated with the switch into a list
+ while i < len(args):
+ if args[i][0] == "-":
+ break
+ params.append(args[i])
+ i = i + 1
+
+ # Check that we have the number of arguments we're expecting
+ pattern = match[2]
+ if pattern[1] == "n" and len(params) != 0:
+ raise RuntimeError("Switch '-" + switch + "' expects no arguments")
+ if pattern[1] == "s" and len(params) != 1:
+ raise RuntimeError("Switch '-" + switch + "' expects one argument")
+ if pattern[1] == "m" and len(params) < 1:
+ raise RuntimeError("Switch '-" + switch + "' expects one or more arguments")
+
+ # Call the syntax checker
+ syntax = match[1]
+ result[match[0]] = syntax(match[0], params)
+
+ # Check for missing required arguments
+ for j in available_arguments:
+ switch = j[0]
+ pattern = j[2]
+ if j[2][0] != "r":
+ break
+ if switch not in result:
+ raise RuntimeError("Missing '-" + switch + "' argument")
+
+ return result
+
+
+#if len(sys.argv) < 2:
+# raise RuntimeError("Need command name")
+#
+#print(parse_arguments(sys.argv[2:], fs_mkmount_arguments))
--- /dev/null
+# -*- coding: utf-8 -*-
+
+__copyright__ = """
+Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+Derived from StGIT:
+
+Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
+Copyright (C) 2008, Karl Hasselström <kha@treskal.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+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 General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+import os
+import stat
+
+def get_command_sets():
+ sets = []
+ for p in __path__:
+ for fn in os.listdir(p):
+ if fn[0] == "_" or fn[-1] == "~" or not stat.S_ISDIR(os.stat(p + "/" + fn).st_mode):
+ continue
+ sets.append(fn)
+ return sets
+
+def import_command_set(cmdset):
+ return __import__(__name__ + '.' + cmdset, globals(), locals(), ['*'])
+
--- /dev/null
+# -*- coding: utf-8 -*-
+
+__copyright__ = """
+Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+Derived from StGIT:
+
+Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
+Copyright (C) 2008, Karl Hasselström <kha@treskal.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+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 General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+import os
+
+def get_command(mod):
+ """Import and return the given command module."""
+ return __import__(__name__ + '.' + mod, globals(), locals(), ['*'])
+
+def get_command_list():
+ commands = []
+ for p in __path__:
+ for fn in os.listdir(p):
+ if not fn.startswith("_") and fn.endswith('.py'):
+ commands.append(fn[:-3])
+ return commands
+
+def py_commands(commands, f):
+ f.write('command_list = {\n')
+ for key, val in sorted(commands.iteritems()):
+ f.write(' %r: %r,\n' % (key, val))
+ f.write(' }\n')
+
+#def _command_list(commands):
+# for cmd, (mod, help) in commands.iteritems():
+
+def pretty_command_list(commands, f):
+ cmd_len = max(len(cmd) for cmd in commands.iterkeys())
+ for cmd, help in cmds:
+ f.write(' %*s %s\n' % (-cmd_len, cmd, help))
+
+def _write_underlined(s, u, f):
+ f.write(s + '\n')
+ f.write(u*len(s) + '\n')
+
+def asciidoc_command_list(commands, f):
+ for cmd, help in commands:
+ f.write('linkstgsub:%s[]::\n' % cmd)
+ f.write(' %s\n' % help)
+ f.write('\n')
--- /dev/null
+#
+# AFS Volume management toolkit: Volume location database query
+# -*- coding: utf-8 -*-
+#
+
+__copyright__ = """
+Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+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 General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+from afs.argparse import *
+import afs.lib.addrcache as addrcache
+import kafs
+from socket import inet_ntoa
+
+help = "Query the VLDB"
+
+command_arguments = [
+ [ "name", get_volume_name, "os", "<volume name or ID>" ],
+ [ "server", get_vlserver, "os", "<machine name>" ],
+ [ "partition", get_partition_id, "os", "<partition name>" ],
+ [ "locked", get_dummy, "fn" ],
+ [ "quiet", get_dummy, "fn" ],
+ [ "nosort", get_dummy, "fn" ],
+ [ "cell", get_cell, "os", "<cell name>" ],
+ [ "noauth", get_auth, "fn" ],
+ [ "localauth", get_auth, "fn" ],
+ [ "verbose", get_dummy, "fn" ],
+ [ "encrypt", get_dummy, "fn" ],
+ [ "noresolve", get_dummy, "fn" ],
+]
+
+description = r"""
+Displays a volume's VLDB entry
+"""
+
+def print_record(params, vldb):
+ """Display a single VLDB record"""
+ print(vldb.name)
+ print(" RWrite: {:<12d} ROnly: {:<12d} Backup: {:<12d}".format(vldb.volumeId[0],
+ vldb.volumeId[1],
+ vldb.volumeId[2]))
+ print(" number of sites ->", vldb.nServers)
+ for i in range(0, vldb.nServers):
+ inaddr = bytearray(4)
+ inaddr[0] = (vldb.serverNumber[i] >> 24) & 0xff
+ inaddr[1] = (vldb.serverNumber[i] >> 16) & 0xff
+ inaddr[2] = (vldb.serverNumber[i] >> 8) & 0xff
+ inaddr[3] = (vldb.serverNumber[i] >> 0) & 0xff
+ addr = inet_ntoa(bytes(inaddr))
+
+ if "noresolve" not in params:
+ name = addrcache.addr2name(addr)
+ if name != None:
+ addr = name
+
+ partnum = vldb.serverPartition[i]
+ if partnum < 26:
+ part = "{:c}".format(97 + partnum)
+ else:
+ partnum -= 26
+ part = "{:c}{:c}".format(97 + partnum / 26, 97 + partnum % 26)
+
+ flags = vldb.serverFlags[i]
+ if flags & kafs.VLSF_ROVOL:
+ ptype = "RO"
+ elif flags & kafs.VLSF_RWVOL:
+ ptype = "RW"
+ else:
+ ptype = "Back"
+
+ print(" server {:s} partition /vicep{:s} {:s} Site".format(addr, part, ptype))
+
+ if vldb.flags & (kafs.VLOP_MOVE | kafs.VLOP_RELEASE | kafs.VLOP_BACKUP | kafs.VLOP_DELETE | kafs.VLOP_DUMP):
+ print(" Volume is currently LOCKED")
+ if vldb.flags & kafs.VLOP_MOVE:
+ print("Volume is locked for a move operation")
+ if vldb.flags & kafs.VLOP_RELEASE:
+ print("Volume is locked for a release operation")
+ if vldb.flags & kafs.VLOP_BACKUP:
+ print("Volume is locked for a backup operation")
+ if vldb.flags & kafs.VLOP_DELETE:
+ print("Volume is locked for a delete/misc operation")
+ if vldb.flags & kafs.VLOP_DUMP:
+ print("Volume is locked for a dump/restore operation")
+
+def main(params):
+ # Get a list of VLDB servers to query
+ cell = params["cell"]
+ z_conn = cell.open_vl_server()
+ quiet = "quiet" in params
+
+ if "name" in params:
+ if "server" in params or "partition" in params or "locked" in params:
+ raise RuntimeError("Can't combine -name with -server, -partition or -locked")
+ ret = kafs.VL_GetEntryByName(z_conn, params["name"][0])
+ vldb = ret.entry
+ print_record(params, vldb)
+ return
+
+ if "name" in params:
+ raise RuntimeError("Can't combine -server, -partition or -locked with -name")
+
+ attributes = kafs.VldbListByAttributes()
+ attributes.Mask = 0
+
+ if "server" in params:
+ attributes.Mask |= kafs.VLLIST_SERVER
+ attributes.server = addrcache.name2addr_int(str(params["server"]))
+ if "partition" in params:
+ attributes.Mask |= kafs.VLLIST_PARTITION
+ attributes.partition = params["partition"]
+ if "locked" in params:
+ attributes.Mask |= kafs.VLLIST_FLAG
+ attributes.flag = kafs.VLOP_MOVE | kafs.VLOP_RELEASE | kafs.VLOP_BACKUP | kafs.VLOP_DELETE | kafs.VLOP_DUMP
+
+ ret = kafs.VL_ListAttributes(z_conn, attributes)
+ blkentries = ret.blkentries
+
+ if not quiet and "server" in params:
+ print("VLDB entries for server", params["server"])
+
+ if "nosort" not in params:
+ blkentries.sort(key=lambda vldb: vldb.name)
+
+ for vldb in blkentries:
+ print_record(params, vldb)
+
+ if not quiet:
+ print("Total entries:", len(blkentries))
--- /dev/null
+class AFSException(Exception):
+ """Base class for all AFS Toolkit exceptions."""
+ pass
--- /dev/null
+#
+# AFS Volume management toolkit: IP Address cache
+# -*- coding: utf-8 -*-
+#
+
+__copyright__ = """
+Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+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 General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+from afs import exception
+from socket import inet_pton, AF_INET, AF_INET6;
+import dns.reversename
+import dns.resolver
+
+cache_n2a = dict()
+cache_a2n = dict()
+
+class NetAddressError(exception.AFSException):
+ """Error raised by L{address cache}."""
+
+def add(name, addr):
+ name = name.lower().rstrip(".")
+ if name not in cache_n2a:
+ cache_n2a[name] = []
+ if addr not in cache_n2a[name]:
+ cache_n2a[name].append(addr)
+ cache_a2n[addr] = name
+
+def name2addr(name):
+ # Try parsing as a numeric IPv4 address
+ try:
+ addr = inet_pton(AF_INET, name)
+ return name
+ except OSError:
+ pass
+
+ # Try parsing as a numeric IPv6 address
+ try:
+ addr = inet_pton(AF_INET6, name)
+ raise NetAddressError("IPv6 is not currently supported")
+ except OSError:
+ pass
+
+ if name in cache_n2a:
+ return cache_n2a[name]
+
+ try:
+ for A in dns.resolver.query(name, 'A'):
+ addrcache.add(name, A.address)
+ except dns.resolver.NoAnswer:
+ pass
+
+ return cache_n2a[name]
+
+def name2addr_int(name):
+ # Try parsing as a numeric IPv4 address
+ try:
+ addr = inet_pton(AF_INET, name)
+ inaddr = addr[0] << 24
+ inaddr |= addr[1] << 16
+ inaddr |= addr[2] << 8
+ inaddr |= addr[3] << 0
+ return inaddr
+ except OSError:
+ pass
+
+ # Try parsing as a numeric IPv6 address
+ try:
+ addr = inet_pton(AF_INET6, name)
+ raise NetAddressError("IPv6 is not currently supported")
+ except OSError:
+ pass
+
+ if name not in cache_n2a:
+ return None
+ addr = inet_pton(AF_INET, cache_n2a[name][0])
+ inaddr = addr[0] << 24
+ inaddr |= addr[1] << 16
+ inaddr |= addr[2] << 8
+ inaddr |= addr[3] << 0
+ return inaddr
+
+def addr2name(addr):
+ if addr not in cache_a2n:
+ try:
+ revname = dns.reversename.from_address(addr)
+ for PTR in dns.resolver.query(revname, 'PTR'):
+ name = str(PTR).lower().rstrip(".")
+ add(name, addr)
+ return name
+
+ return None
+ except dns.resolver.NoAnswer:
+ return None
+ return cache_a2n[addr]
--- /dev/null
+#
+# AFS Volume management toolkit: Cell record
+# -*- coding: utf-8 -*-
+#
+
+__copyright__ = """
+Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+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 General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+from afs import exception
+from afs.lib.vlserver import vlserver, VLServerError
+from afs.lib.debug import debug
+import dns.resolver
+import linecache
+import kafs
+
+class CellError(exception.AFSException):
+ """Error raised by L{cell} objects."""
+
+class cell:
+ """Represents an AFS cell. We hold the cell name here and the list of
+ VL servers once we've looked it up"""
+ def __init__(self, name = None):
+ if name == None:
+ name = linecache.getline("/etc/afs/ThisCell", 1)
+ name = name.strip()
+ debug("Default Cell:", name)
+ if name != "":
+ debug("Found", name)
+ else:
+ raise CellError("Couldn't determine default cell")
+
+ debug("New Cell", name)
+ self.__name = name
+ self.__looked_up = False
+ self.__vlserver_names = dict()
+ self.__vlservers = []
+ self.__vlconn = None
+
+ def __repr__(self):
+ return "<" + "AFS:" + self.__name + ">"
+
+ def __str__(self):
+ return self.__name
+
+ def add_server(self, dnstype, name):
+ n = str(name)
+ if n not in self.__vlserver_names:
+ s = vlserver(n)
+ self.__vlserver_names[n] = s
+ self.__vlservers.append(s)
+
+ # Look up the servers
+ def look_up_vl_servers(self):
+ debug("-- Find VL servers for cell:", self.__name, "--")
+
+ # Start by looking for SRV records in the DNS
+ try:
+ for SRV in dns.resolver.query("_afs3-vlserver._udp." + self.__name, "SRV"):
+ self.add_server("SRV", SRV.target)
+ except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
+ debug("Couldn't find any SRV records")
+
+ # Then we look for AFSDB records in the DNS
+ try:
+ for AFSDB in dns.resolver.query(self.__name, "AFSDB"):
+ self.add_server("AFSDB", AFSDB.hostname)
+ except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
+ debug("Couldn't find any AFSDB records")
+
+ # Then parse the local afsdb file
+ # ... TODO ...
+
+ self.__looked_up = True
+
+ # Get the VLDB database server list for the cell
+ def query_vl_servers(self):
+ if not self.__looked_up:
+ self.look_up_vl_servers()
+ if not self.__vlservers:
+ raise CellError("Couldn't find any VL servers")
+ return self.__vlservers
+
+ # Force a particular set of servers
+ def override_vlserver_list(self, servers):
+ self.__vlservers = servers
+ for i in servers:
+ self.__vlserver_names[str(i)] = i
+ self.__looked_up = True
+
+ # Get the VLDB database server addresses for the cell
+ def query_vl_addrs(self):
+ addrs = []
+ for i in self.query_vl_servers():
+ try:
+ for j in i.addrs():
+ if j not in addrs:
+ addrs.append(j)
+ except VLServerError:
+ pass
+
+ if len(addrs) == 0:
+ raise CellError("Couldn't find any VL servers in cell")
+
+ return addrs
+
+ # Open a VL Server connection
+ def open_vl_server(self):
+ if self.__vlconn:
+ return
+
+ for vlserver in self.query_vl_addrs():
+ debug("Trying", vlserver)
+
+ z_conn = kafs.rx_new_connection(vlserver, kafs.VL_PORT, kafs.VL_SERVICE)
+ try:
+ ret = kafs.VL_Probe(z_conn)
+ self.__vlconn = z_conn
+ break
+ except ConnectionRefusedError:
+ pass
+ del z_conn
+ else:
+ raise CellError("Couldn't connect to a VL server")
+
+ return self.__vlconn
--- /dev/null
+#
+# AFS Volume management toolkit: Debugging
+# -*- coding: utf-8 -*-
+#
+
+__copyright__ = """
+Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+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 General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+import sys
+
+debugging_level = 0
+
+def debug(*args, **kwargs):
+ if debugging_level > 0:
+ print(*args, file=sys.stdout)
--- /dev/null
+#
+# AFS Volume management toolkit: Volume Location server record
+# -*- coding: utf-8 -*-
+#
+
+__copyright__ = """
+Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+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 General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+from afs import exception
+import afs.lib.addrcache as addrcache
+from afs.lib.debug import debug
+from socket import inet_pton, AF_INET, AF_INET6;
+import dns.resolver
+
+class VLServerError(exception.AFSException):
+ """Error raised by L{vlserver} objects."""
+
+class vlserver:
+ """Represents an AFS Volume Location server. We hold the server address here."""
+ def __init__(self, name):
+ debug("New VLServer", name)
+ self.__name = name
+ self.__looked_up = False
+
+ # We want to maintain the record order provided by the DNS to entry
+ # force rotation, so we don't want to use an unordered set here.
+ #
+ # Note that the addrs are stored as text strings of the form "a.b.c.d"
+ self.__addrs = []
+
+ def __repr__(self):
+ return "<" + "AFSVL:" + self.__name + ">"
+
+ def __str__(self):
+ return self.__name
+
+ def look_up_addresses(self):
+ addr = False
+
+ # Try parsing as a numeric IPv4 address
+ try:
+ addr = inet_pton(AF_INET, self.__name)
+ self.__addrs.append(self.__name)
+ except OSError:
+ pass
+
+ # Try parsing as a numeric IPv6 address
+ if not addr:
+ try:
+ addr = inet_pton(AF_INET6, self.__name)
+ raise VLServerError("IPv6 is not currently supported")
+ except OSError:
+ pass
+
+ # Try looking up in the DNS
+ if not addr:
+ try:
+ for A in dns.resolver.query(self.__name, 'A'):
+ if A.address not in self.__addrs:
+ self.__addrs.append(A.address)
+ addrcache.add(self.__name, A.address)
+ except dns.resolver.NoAnswer:
+ pass
+
+ self.__looked_up = True
+
+ def look_up(self):
+ if not self.__looked_up:
+ self.look_up_addresses()
+ if len(self.__addrs) == 0:
+ raise VLServerError("No VLServer addresses available")
+ return self.__addrs
+
+ def addrs(self):
+ return self.look_up()
--- /dev/null
+#
+# AFS Toolset command switcher
+# -*- coding: utf-8 -*-
+#
+
+__copyright__ = """
+Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+Derived from StGIT:
+
+Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+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 General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+import sys, os, traceback
+
+import afs.commands
+from afs.lib.debug import debug, debugging_level
+
+#
+# The commands map
+#
+class Commands(dict):
+ """Commands class. It performs on-demand module loading
+ """
+ def canonical_cmd(self, key):
+ """Return the canonical name for a possibly-shortened
+ command name.
+ """
+ candidates = [cmd for cmd in self.keys() if cmd.startswith(key)]
+
+ if not candidates:
+ out.error('Unknown command: %s' % key,
+ 'Try "%s help" for a list of supported commands' % prog)
+ sys.exit(2)
+
+ if len(candidates) > 1:
+ out.error('Ambiguous command: %s' % key,
+ 'Candidates are: %s' % ', '.join(candidates))
+ sys.exit(2)
+
+ return candidates[0]
+
+ def __getitem__(self, key):
+ cmd_mod = self.get(key) or self.get(self.canonical_cmd(key))
+ return afs.commands.get_command(cmd_mod)
+
+#cmd_list = afs.commands.get_commands()
+#print(cmd_list);
+
+#commands = Commands((cmd, mod) for cmd, (mod, kind, help)
+# in cmd_list.iteritems())
+
+def print_help():
+ print('usage: %s <command> [options]' % os.path.basename(sys.argv[0]))
+ print()
+ print('Generic commands:')
+ print(' help print the detailed command usage')
+ print(' version display version information')
+ print(' copyright display copyright information')
+ print()
+ afs.commands.pretty_command_list(cmd_list, sys.stdout)
+
+#
+# The main function (command dispatcher)
+#
+def _main():
+ """The main function
+ """
+ global prog
+
+ prog = os.path.basename(sys.argv[0])
+
+ cmdsets = afs.commands.get_command_sets()
+ #print("CMDSETS:", cmdsets)
+
+ if prog == "afs":
+ if len(sys.argv) < 3:
+ print('usage: {:s} <cmdset> <command>'.format(prog), file=sys.stderr)
+ print(' Try "{:s} help" for a list of supported command sets'.format(prog), file=sys.stderr)
+ sys.exit(2)
+ cmdset = sys.argv[1]
+ cmd = sys.argv[2]
+ del sys.argv[1:3]
+ else:
+ if len(sys.argv) < 2:
+ print('usage: {:s} <command>'.format(prog), file=sys.stderr)
+ print(' Try "{:s} help" for a list of supported commands'.format(prog), file=sys.stderr)
+ sys.exit(2)
+ cmdset = prog
+ cmd = sys.argv[1]
+ del sys.argv[1:2]
+
+ if cmdset not in cmdsets:
+ raise RuntimeError("Unsupported command set '" + cmdset + "'")
+
+ if cmd in ['-v', '--version', 'version']:
+ from afs.version import version
+ print('AFS Toolkit %s' % version)
+ print('Python version %s' % sys.version)
+ sys.exit(0)
+
+ if cmd in ['copyright']:
+ print(__copyright__)
+ sys.exit(0)
+
+ # Import the command set
+ cmdsetmod = afs.commands.import_command_set(cmdset)
+ commands = cmdsetmod.get_command_list()
+ #print("CMDS:", commands)
+
+ # See if the command is in the set
+ found = False
+ for i in commands:
+ if i == cmd:
+ found = cmd
+ break
+ if i.startswith(cmd):
+ if found:
+ raise RuntimeError("Command '" + cmd + "' is ambiguous")
+ found = cmd
+ if not found:
+ raise RuntimeError("Command '" + cmd + "' is unknown")
+ cmd = found
+
+ # Rebuild the command line arguments
+ if prog == "afs":
+ sys.argv[0] += ' {:s} {:s}'.format(cmdset, cmd)
+ else:
+ sys.argv[0] += ' {:s}'.format(cmd)
+
+ command = cmdsetmod.get_command(cmd)
+ #print("MOD:", command)
+
+ # If it's an alias, then switch to the real module
+ if hasattr(command, "alias"):
+ cmd = command.alias
+ command = cmdsetmod.get_command(cmd)
+ #print("MOD:", command)
+
+ # Parse the parameters
+ params = afs.argparse.parse_arguments(sys.argv[1:], command.command_arguments)
+
+ # Stick in the default cell if there isn't one
+ if "cell" not in params:
+ from afs.lib.cell import cell
+ params["cell"] = cell()
+
+ # These modules are only used from this point onwards and do not
+ # need to be imported earlier
+ from afs.exception import AFSException
+
+ try:
+ debug_level = int(os.environ.get('AFS_DEBUG_LEVEL', 0))
+ except ValueError:
+ print('Invalid AFS_DEBUG_LEVEL environment variable', file=sys.stderr)
+ sys.exit(2)
+
+ try:
+ ret = command.main(params)
+ except (AFSException, IOError) as err:
+ print(str(err), file=sys.stderr)
+ if debug_level > 0:
+ traceback.print_exc()
+ sys.exit(1)
+ except SystemExit:
+ # Triggered by the option parser when it finds bad commandline
+ # parameters.
+ sys.exit(1)
+ except KeyboardInterrupt:
+ sys.exit(1)
+ except:
+ print('Unhandled exception:', file=sys.stderr)
+ traceback.print_exc()
+ sys.exit(3)
+
+ sys.exit(ret or 0)
+
+def main():
+ try:
+ _main()
+ finally:
+ pass