summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSol Jerome <sol.jerome@gmail.com>2011-04-06 19:34:30 -0500
committerSol Jerome <sol.jerome@gmail.com>2011-04-06 19:35:21 -0500
commit742fc83dcdb82639b97723ce4cbfade75fb1aa71 (patch)
tree3c242e7029ab20f03300292d272e665c33c17943 /src
parente17608a61575ec9d50cfed34a80540257254840b (diff)
downloadbcfg2-742fc83dcdb82639b97723ce4cbfade75fb1aa71.tar.gz
bcfg2-742fc83dcdb82639b97723ce4cbfade75fb1aa71.tar.bz2
bcfg2-742fc83dcdb82639b97723ce4cbfade75fb1aa71.zip
bcfg2-admin: PY3K + PEP8 fixes
Signed-off-by: Sol Jerome <sol.jerome@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/lib/Server/Admin/Backup.py3
-rw-r--r--src/lib/Server/Admin/Bundle.py50
-rw-r--r--src/lib/Server/Admin/Client.py15
-rw-r--r--src/lib/Server/Admin/Compare.py59
-rw-r--r--src/lib/Server/Admin/Group.py15
-rw-r--r--src/lib/Server/Admin/Init.py116
-rw-r--r--src/lib/Server/Admin/Perf.py11
-rw-r--r--src/lib/Server/Admin/Pull.py46
-rw-r--r--src/lib/Server/Admin/Query.py15
-rw-r--r--src/lib/Server/Admin/Reports.py74
-rw-r--r--src/lib/Server/Admin/Tidy.py14
-rw-r--r--src/lib/Server/Admin/Viz.py13
-rw-r--r--src/lib/Server/Admin/Xcmd.py20
-rw-r--r--src/lib/Server/Admin/__init__.py25
14 files changed, 296 insertions, 180 deletions
diff --git a/src/lib/Server/Admin/Backup.py b/src/lib/Server/Admin/Backup.py
index fefc9fc9e..9bd644ff9 100644
--- a/src/lib/Server/Admin/Backup.py
+++ b/src/lib/Server/Admin/Backup.py
@@ -5,6 +5,7 @@ import tarfile
import Bcfg2.Server.Admin
import Bcfg2.Options
+
class Backup(Bcfg2.Server.Admin.MetadataCore):
__shorthelp__ = "Make a backup of the Bcfg2 repository"
__longhelp__ = (__shorthelp__ + "\n\nbcfg2-admin backup\n")
@@ -29,4 +30,4 @@ class Backup(Bcfg2.Server.Admin.MetadataCore):
out = tarfile.open(self.datastore + '/' + filename, mode=mode)
out.add(self.datastore, os.path.basename(self.datastore))
out.close()
- print "Archive %s was stored under %s" % (filename, self.datastore)
+ print("Archive %s was stored under %s" % (filename, self.datastore))
diff --git a/src/lib/Server/Admin/Bundle.py b/src/lib/Server/Admin/Bundle.py
index 96a7ba59d..9b2a71783 100644
--- a/src/lib/Server/Admin/Bundle.py
+++ b/src/lib/Server/Admin/Bundle.py
@@ -6,11 +6,11 @@ import Bcfg2.Server.Admin
import Bcfg2.Options
from Bcfg2.Server.Plugins.Metadata import MetadataConsistencyError
+
class Bundle(Bcfg2.Server.Admin.MetadataCore):
__shorthelp__ = "Create or delete bundle entries"
- __longhelp__ = (__shorthelp__ + #"\n\nbcfg2-admin bundle add <bundle> "
- #"\n\nbcfg2-admin bundle del <bundle>"
- "\n\nbcfg2-admin bundle list-xml"
+ # TODO: add/del functions
+ __longhelp__ = (__shorthelp__ + "\n\nbcfg2-admin bundle list-xml"
"\nbcfg2-admin bundle list-genshi"
"\nbcfg2-admin bundle show\n")
__usage__ = ("bcfg2-admin bundle [options] [add|del] [group]")
@@ -21,7 +21,7 @@ class Bundle(Bcfg2.Server.Admin.MetadataCore):
def __call__(self, args):
Bcfg2.Server.Admin.MetadataCore.__call__(self, args)
- reg='((?:[a-z][a-z\\.\\d\\-]+)\\.(?:[a-z][a-z\\-]+))(?![\\w\\.])'
+ reg = '((?:[a-z][a-z\\.\\d\\-]+)\\.(?:[a-z][a-z\\-]+))(?![\\w\\.])'
# Get all bundles out of the Bundle/ directory
opts = {'repo': Bcfg2.Options.SERVER_REPOSITORY}
@@ -38,31 +38,31 @@ class Bundle(Bcfg2.Server.Admin.MetadataCore):
# try:
# self.metadata.add_bundle(args[1])
# except MetadataConsistencyError:
-# print "Error in adding bundle."
+# print("Error in adding bundle.")
# raise SystemExit(1)
# elif args[0] in ['delete', 'remove', 'del', 'rm']:
# try:
# self.metadata.remove_bundle(args[1])
# except MetadataConsistencyError:
-# print "Error in deleting bundle."
+# print("Error in deleting bundle.")
# raise SystemExit(1)
# Lists all available xml bundles
elif args[0] in ['list-xml', 'ls-xml']:
bundle_name = []
for bundle_path in xml_list:
- rg = re.compile(reg,re.IGNORECASE|re.DOTALL)
+ rg = re.compile(reg, re.IGNORECASE | re.DOTALL)
bundle_name.append(rg.search(bundle_path).group(1))
for bundle in bundle_name:
- print bundle.split('.')[0]
+ print(bundle.split('.')[0])
# Lists all available genshi bundles
elif args[0] in ['list-genshi', 'ls-gen']:
bundle_name = []
for bundle_path in genshi_list:
- rg = re.compile(reg,re.IGNORECASE|re.DOTALL)
+ rg = re.compile(reg, re.IGNORECASE | re.DOTALL)
bundle_name.append(rg.search(bundle_path).group(1))
for bundle in bundle_name:
- print bundle.split('.')[0]
- # Shows a list of all available bundles and prints bundle
+ print(bundle.split('.')[0])
+ # Shows a list of all available bundles and prints bundle
# details after the user choose one bundle.
# FIXME: Add support for detailed output of genshi bundles
# FIXME: This functionality is almost identical with
@@ -71,32 +71,34 @@ class Bundle(Bcfg2.Server.Admin.MetadataCore):
bundle_name = []
bundle_list = xml_list + genshi_list
for bundle_path in bundle_list:
- rg = re.compile(reg,re.IGNORECASE|re.DOTALL)
+ rg = re.compile(reg, re.IGNORECASE | re.DOTALL)
bundle_name.append(rg.search(bundle_path).group(1))
text = "Available bundles (Number of bundles: %s)" % \
(len(bundle_list))
- print text
- print "%s" % (len(text) * "-")
+ print(text)
+ print("%s" % (len(text) * "-"))
for i in range(len(bundle_list)):
- print "[%i]\t%s" % (i, bundle_name[i])
- print "Enter the line number of a bundle for details:",
- lineno = raw_input()
+ print("[%i]\t%s" % (i, bundle_name[i]))
+ try:
+ lineno = raw_input("Enter the line number of a bundle for details: ")
+ except NameError:
+ lineno = input("Enter the line number of a bundle for details: ")
if int(lineno) >= int(len(bundle_list)):
- print "No line with this number."
+ print("No line with this number.")
else:
if '%s/Bundler/%s' % \
(repo, bundle_name[int(lineno)]) in genshi_list:
- print "Detailed output for *.genshi bundles is not supported."
+ print("Detailed output for *.genshi bundles is not supported.")
else:
- print 'Details for the "%s" bundle:' % \
- (bundle_name[int(lineno)].split('.')[0])
+ print('Details for the "%s" bundle:' % \
+ (bundle_name[int(lineno)].split('.')[0]))
tree = lxml.etree.parse(bundle_list[int(lineno)])
#Prints bundle content
- #print lxml.etree.tostring(tree)
+ #print(lxml.etree.tostring(tree))
names = ['Action', 'Package', 'Path', 'Service']
for name in names:
for node in tree.findall("//" + name):
- print "%s:\t%s" % (name, node.attrib["name"])
+ print("%s:\t%s" % (name, node.attrib["name"]))
else:
- print "No command specified"
+ print("No command specified")
raise SystemExit(1)
diff --git a/src/lib/Server/Admin/Client.py b/src/lib/Server/Admin/Client.py
index 08bd34151..3af25b15a 100644
--- a/src/lib/Server/Admin/Client.py
+++ b/src/lib/Server/Admin/Client.py
@@ -2,6 +2,7 @@ import lxml.etree
import Bcfg2.Server.Admin
from Bcfg2.Server.Plugins.Metadata import MetadataConsistencyError
+
class Client(Bcfg2.Server.Admin.MetadataCore):
__shorthelp__ = "Create, delete, or modify client entries"
__longhelp__ = (__shorthelp__ + "\n\nbcfg2-admin client add <client> "
@@ -27,13 +28,13 @@ class Client(Bcfg2.Server.Admin.MetadataCore):
attr, val = i.split('=', 1)
if attr not in ['profile', 'uuid', 'password',
'location', 'secure', 'address']:
- print "Attribute %s unknown" % attr
+ print("Attribute %s unknown" % attr)
raise SystemExit(1)
attr_d[attr] = val
try:
self.metadata.add_client(args[1], attr_d)
except MetadataConsistencyError:
- print "Error in adding client"
+ print("Error in adding client")
raise SystemExit(1)
elif args[0] in ['update', 'up']:
attr_d = {}
@@ -41,24 +42,24 @@ class Client(Bcfg2.Server.Admin.MetadataCore):
attr, val = i.split('=', 1)
if attr not in ['profile', 'uuid', 'password',
'location', 'secure', 'address']:
- print "Attribute %s unknown" % attr
+ print("Attribute %s unknown" % attr)
raise SystemExit(1)
attr_d[attr] = val
try:
self.metadata.update_client(args[1], attr_d)
except MetadataConsistencyError:
- print "Error in updating client"
+ print("Error in updating client")
raise SystemExit(1)
elif args[0] in ['delete', 'remove', 'del', 'rm']:
try:
self.metadata.remove_client(args[1])
except MetadataConsistencyError:
- print "Error in deleting client"
+ print("Error in deleting client")
raise SystemExit(1)
elif args[0] in ['list', 'ls']:
tree = lxml.etree.parse(self.metadata.data + "/clients.xml")
for node in tree.findall("//Client"):
- print node.attrib["name"]
+ print(node.attrib["name"])
else:
- print "No command specified"
+ print("No command specified")
raise SystemExit(1)
diff --git a/src/lib/Server/Admin/Compare.py b/src/lib/Server/Admin/Compare.py
index f97233b0e..4c751b55a 100644
--- a/src/lib/Server/Admin/Compare.py
+++ b/src/lib/Server/Admin/Compare.py
@@ -1,6 +1,9 @@
-import lxml.etree, os
+import lxml.etree
+import os
+
import Bcfg2.Server.Admin
+
class Compare(Bcfg2.Server.Admin.Mode):
__shorthelp__ = ("Determine differences between files or "
"directories of client specification instances")
@@ -11,30 +14,30 @@ class Compare(Bcfg2.Server.Admin.Mode):
def __init__(self, configfile):
Bcfg2.Server.Admin.Mode.__init__(self, configfile)
- self.important = {'Package':['name', 'version'],
- 'Service':['name', 'status'],
- 'Directory':['name', 'owner', 'group', 'perms'],
- 'SymLink':['name', 'to'],
- 'ConfigFile':['name', 'owner', 'group', 'perms'],
- 'Permissions':['name', 'perms'],
- 'PostInstall':['name']}
+ self.important = {'Package': ['name', 'version'],
+ 'Service': ['name', 'status'],
+ 'Directory': ['name', 'owner', 'group', 'perms'],
+ 'SymLink': ['name', 'to'],
+ 'ConfigFile': ['name', 'owner', 'group', 'perms'],
+ 'Permissions': ['name', 'perms'],
+ 'PostInstall': ['name']}
def compareStructures(self, new, old):
for child in new.getchildren():
equiv = old.xpath('%s[@name="%s"]' %
(child.tag, child.get('name')))
if child.tag in self.important:
- print "tag type %s not handled" % (child.tag)
+ print("tag type %s not handled" % (child.tag))
continue
if len(equiv) == 0:
- print ("didn't find matching %s %s" %
- (child.tag, child.get('name')))
+ print("didn't find matching %s %s" %
+ (child.tag, child.get('name')))
continue
elif len(equiv) >= 1:
if child.tag == 'ConfigFile':
if child.text != equiv[0].text:
- print " %s %s contents differ" \
- % (child.tag, child.get('name'))
+ print(" %s %s contents differ" \
+ % (child.tag, child.get('name')))
continue
noattrmatch = [field for field in self.important[child.tag] if \
child.get(field) != equiv[0].get(field)]
@@ -42,8 +45,8 @@ class Compare(Bcfg2.Server.Admin.Mode):
new.remove(child)
old.remove(equiv[0])
else:
- print " %s %s attributes %s do not match" % \
- (child.tag, child.get('name'), noattrmatch)
+ print(" %s %s attributes %s do not match" % \
+ (child.tag, child.get('name'), noattrmatch))
if len(old.getchildren()) == 0 and len(new.getchildren()) == 0:
return True
if new.tag == 'Independent':
@@ -59,24 +62,26 @@ class Compare(Bcfg2.Server.Admin.Mode):
newl.remove(entry)
oldl.remove(entry)
for entry in both:
- print " %s differs (in bundle %s)" % (entry, name)
+ print(" %s differs (in bundle %s)" % (entry, name))
for entry in oldl:
- print " %s only in old configuration (in bundle %s)" % (entry, name)
+ print(" %s only in old configuration (in bundle %s)" % (entry,
+ name))
for entry in newl:
- print " %s only in new configuration (in bundle %s)" % (entry, name)
+ print(" %s only in new configuration (in bundle %s)" % (entry,
+ name))
return False
def compareSpecifications(self, path1, path2):
try:
new = lxml.etree.parse(path1).getroot()
except IOError:
- print "Failed to read %s" % (path1)
+ print("Failed to read %s" % (path1))
raise SystemExit(1)
try:
old = lxml.etree.parse(path2).getroot()
except IOError:
- print "Failed to read %s" % (path2)
+ print("Failed to read %s" % (path2))
raise SystemExit(1)
for src in [new, old]:
@@ -88,7 +93,7 @@ class Compare(Bcfg2.Server.Admin.Mode):
for bundle in new.findall('./Bundle'):
equiv = old.xpath('Bundle[@name="%s"]' % (bundle.get('name')))
if len(equiv) == 0:
- print "couldnt find matching bundle for %s" % bundle.get('name')
+ print("couldnt find matching bundle for %s" % bundle.get('name'))
continue
if len(equiv) == 1:
if self.compareStructures(bundle, equiv[0]):
@@ -98,7 +103,7 @@ class Compare(Bcfg2.Server.Admin.Mode):
else:
rcs.append(False)
else:
- print "Unmatched bundle %s" % (bundle.get('name'))
+ print("Unmatched bundle %s" % (bundle.get('name')))
rcs.append(False)
i1 = new.find('./Independent')
i2 = old.find('./Independent')
@@ -120,18 +125,18 @@ class Compare(Bcfg2.Server.Admin.Mode):
(oldd, newd) = args
(old, new) = [os.listdir(spot) for spot in args]
for item in old:
- print "Entry:", item
+ print("Entry:", item)
state = self.__call__([oldd + '/' + item, newd + '/' + item])
new.remove(item)
if state:
- print "Entry:", item, "good"
+ print("Entry:", item, "good")
else:
- print "Entry:", item, "bad"
+ print("Entry:", item, "bad")
if new:
- print "new has extra entries", new
+ print("new has extra entries", new)
return
try:
(old, new) = args
except IndexError:
- print self.__call__.__doc__
+ print(self.__call__.__doc__)
raise SystemExit(1)
diff --git a/src/lib/Server/Admin/Group.py b/src/lib/Server/Admin/Group.py
index 4b2db28ec..1c5d0c12f 100644
--- a/src/lib/Server/Admin/Group.py
+++ b/src/lib/Server/Admin/Group.py
@@ -2,6 +2,7 @@ import lxml.etree
import Bcfg2.Server.Admin
from Bcfg2.Server.Plugins.Metadata import MetadataConsistencyError
+
class Group(Bcfg2.Server.Admin.MetadataCore):
__shorthelp__ = "Create, delete, or modify group entries"
__longhelp__ = (__shorthelp__ + "\n\nbcfg2-admin group add <group> "
@@ -28,13 +29,13 @@ class Group(Bcfg2.Server.Admin.MetadataCore):
if attr not in ['profile', 'public', 'default',
'name', 'auth', 'toolset', 'category',
'comment']:
- print "Attribute %s unknown" % attr
+ print("Attribute %s unknown" % attr)
raise SystemExit(1)
attr_d[attr] = val
try:
self.metadata.add_group(args[1], attr_d)
except MetadataConsistencyError:
- print "Error in adding group"
+ print("Error in adding group")
raise SystemExit(1)
elif args[0] in ['update', 'up']:
attr_d = {}
@@ -43,24 +44,24 @@ class Group(Bcfg2.Server.Admin.MetadataCore):
if attr not in ['profile', 'public', 'default',
'name', 'auth', 'toolset', 'category',
'comment']:
- print "Attribute %s unknown" % attr
+ print("Attribute %s unknown" % attr)
raise SystemExit(1)
attr_d[attr] = val
try:
self.metadata.update_group(args[1], attr_d)
except MetadataConsistencyError:
- print "Error in updating group"
+ print("Error in updating group")
raise SystemExit(1)
elif args[0] in ['delete', 'remove', 'del', 'rm']:
try:
self.metadata.remove_group(args[1])
except MetadataConsistencyError:
- print "Error in deleting group"
+ print("Error in deleting group")
raise SystemExit(1)
elif args[0] in ['list', 'ls']:
tree = lxml.etree.parse(self.metadata.data + "/groups.xml")
for node in tree.findall("//Group"):
- print node.attrib["name"]
+ print(node.attrib["name"])
else:
- print "No command specified"
+ print("No command specified")
raise SystemExit(1)
diff --git a/src/lib/Server/Admin/Init.py b/src/lib/Server/Admin/Init.py
index 8f54b836e..eddbd732a 100644
--- a/src/lib/Server/Admin/Init.py
+++ b/src/lib/Server/Admin/Init.py
@@ -137,20 +137,33 @@ def create_key(hostname, keypath, certpath, country, state, location):
keypath,
certpath))
subprocess.call((ccstr), shell=True)
- os.chmod(keypath, 0600)
+ # py3k compatibility
+ try:
+ os.chmod(keypath, 0600)
+ except SyntaxError:
+ os.chmod(keypath, 0o600)
def create_conf(confpath, confdata):
# Don't overwrite existing bcfg2.conf file
if os.path.exists(confpath):
- result = raw_input("\nWarning: %s already exists. "
- "Overwrite? [y/N]: " % confpath)
+ # py3k compatibility
+ try:
+ result = raw_input("\nWarning: %s already exists. "
+ "Overwrite? [y/N]: " % confpath)
+ except NameError:
+ result = input("\nWarning: %s already exists. "
+ "Overwrite? [y/N]: " % confpath)
if result not in ['Y', 'y']:
print("Leaving %s unchanged" % confpath)
return
try:
open(confpath, "w").write(confdata)
- os.chmod(confpath, 0600)
+ # py3k compatibility
+ try:
+ os.chmod(keypath, 0600)
+ except SyntaxError:
+ os.chmod(keypath, 0o600)
except Exception, e:
print("Error %s occured while trying to write configuration "
"file to '%s'.\n" %
@@ -204,7 +217,12 @@ class Init(Bcfg2.Server.Admin.Mode):
def _prompt_hostname(self):
"""Ask for the server hostname."""
- data = raw_input("What is the server's hostname [%s]: " %
+ # py3k compatibility
+ try:
+ data = raw_input("What is the server's hostname [%s]: " %
+ socket.getfqdn())
+ except NameError:
+ data = input("What is the server's hostname [%s]: " %
socket.getfqdn())
if data != '':
self.shostname = data
@@ -213,21 +231,36 @@ class Init(Bcfg2.Server.Admin.Mode):
def _prompt_config(self):
"""Ask for the configuration file path."""
- newconfig = raw_input("Store Bcfg2 configuration in [%s]: " %
- self.configfile)
+ # py3k compatibility
+ try:
+ newconfig = raw_input("Store Bcfg2 configuration in [%s]: " %
+ self.configfile)
+ except NameError:
+ newconfig = input("Store Bcfg2 configuration in [%s]: " %
+ self.configfile)
if newconfig != '':
self.configfile = newconfig
def _prompt_repopath(self):
"""Ask for the repository path."""
while True:
- newrepo = raw_input("Location of Bcfg2 repository [%s]: " %
- self.repopath)
+ # py3k compatibility
+ try:
+ newrepo = raw_input("Location of Bcfg2 repository [%s]: " %
+ self.repopath)
+ except NameError:
+ newrepo = input("Location of Bcfg2 repository [%s]: " %
+ self.repopath)
if newrepo != '':
self.repopath = newrepo
if os.path.isdir(self.repopath):
- response = raw_input("Directory %s exists. Overwrite? [y/N]:" \
- % self.repopath)
+ # py3k compatibility
+ try:
+ response = raw_input("Directory %s exists. Overwrite? [y/N]:" \
+ % self.repopath)
+ except NameError:
+ response = input("Directory %s exists. Overwrite? [y/N]:" \
+ % self.repopath)
if response.lower().strip() == 'y':
break
else:
@@ -243,8 +276,13 @@ class Init(Bcfg2.Server.Admin.Mode):
def _prompt_server(self):
"""Ask for the server name."""
- newserver = raw_input("Input the server location [%s]: " %
- self.server_uri)
+ # py3k compatibility
+ try:
+ newserver = raw_input("Input the server location [%s]: " %
+ self.server_uri)
+ except NameError:
+ newserver = input("Input the server location [%s]: " %
+ self.server_uri)
if newserver != '':
self.server_uri = newserver
@@ -256,51 +294,81 @@ class Init(Bcfg2.Server.Admin.Mode):
prompt += ': '
while True:
try:
- self.os_sel = os_list[int(raw_input(prompt))-1][1]
+ # py3k compatibility
+ try:
+ osidx = int(raw_input(prompt))
+ except NameError:
+ osidx = int(input(prompt))
+ self.os_sel = os_list[osidx - 1][1]
break
except ValueError:
continue
def _prompt_plugins(self):
- default = raw_input("Use default plugins? (%s) [Y/n]: " %
+ # py3k compatibility
+ try:
+ default = raw_input("Use default plugins? (%s) [Y/n]: " %
+ ''.join(default_plugins)).lower()
+ except NameError:
+ default = input("Use default plugins? (%s) [Y/n]: " %
''.join(default_plugins)).lower()
if default != 'y' or default != '':
while True:
plugins_are_valid = True
- plug_str = raw_input("Specify plugins: ")
+ # py3k compatibility
+ try:
+ plug_str = raw_input("Specify plugins: ")
+ except NameError:
+ plug_str = input("Specify plugins: ")
plugins = plug_str.split(',')
for plugin in plugins:
plugin = plugin.strip()
if not plugin in plugin_list:
plugins_are_valid = False
- print "ERROR: Plugin %s not recognized" % plugin
+ print("ERROR: Plugin %s not recognized" % plugin)
if plugins_are_valid:
break
def _prompt_certificate(self):
"""Ask for the key details (country, state, and location)."""
- print "The following questions affect SSL certificate generation."
- print "If no data is provided, the default values are used."
- newcountry = raw_input("Country name (2 letter code) for certificate: ")
+ print("The following questions affect SSL certificate generation.")
+ print("If no data is provided, the default values are used.")
+ # py3k compatibility
+ try:
+ newcountry = raw_input("Country name (2 letter code) for certificate: ")
+ except NameError:
+ newcountry = input("Country name (2 letter code) for certificate: ")
if newcountry != '':
if len(newcountry) == 2:
self.country = newcountry
else:
while len(newcountry) != 2:
- newcountry = raw_input("2 letter country code (eg. US): ")
+ # py3k compatibility
+ try:
+ newcountry = raw_input("2 letter country code (eg. US): ")
+ except NameError:
+ newcountry = input("2 letter country code (eg. US): ")
if len(newcountry) == 2:
self.country = newcountry
break
else:
self.country = 'US'
- newstate = raw_input("State or Province Name (full name) for certificate: ")
+ # py3k compatibility
+ try:
+ newstate = raw_input("State or Province Name (full name) for certificate: ")
+ except NameError:
+ newstate = input("State or Province Name (full name) for certificate: ")
if newstate != '':
self.state = newstate
else:
self.state = 'Illinois'
- newlocation = raw_input("Locality Name (eg, city) for certificate: ")
+ # py3k compatibility
+ try:
+ newlocation = raw_input("Locality Name (eg, city) for certificate: ")
+ except NameError:
+ newlocation = input("Locality Name (eg, city) for certificate: ")
if newlocation != '':
self.location = newlocation
else:
@@ -349,6 +417,6 @@ class Init(Bcfg2.Server.Admin.Mode):
try:
os.makedirs(path)
self._init_plugins()
- print "Repository created successfuly in %s" % (self.repopath)
+ print("Repository created successfuly in %s" % (self.repopath))
except OSError:
print("Failed to create %s." % path)
diff --git a/src/lib/Server/Admin/Perf.py b/src/lib/Server/Admin/Perf.py
index 095180592..af1c83072 100644
--- a/src/lib/Server/Admin/Perf.py
+++ b/src/lib/Server/Admin/Perf.py
@@ -1,8 +1,9 @@
+import sys
+
import Bcfg2.Options
import Bcfg2.Proxy
import Bcfg2.Server.Admin
-import sys
class Perf(Bcfg2.Server.Admin.Mode):
__shorthelp__ = ("Query server for performance data")
@@ -27,11 +28,11 @@ class Perf(Bcfg2.Server.Admin.Mode):
proxy = Bcfg2.Proxy.ComponentProxy(setup['server'],
setup['user'],
setup['password'],
- key = setup['key'],
- cert = setup['certificate'],
- ca = setup['ca'])
+ key=setup['key'],
+ cert=setup['certificate'],
+ ca=setup['ca'])
data = proxy.get_statistics()
- for key, value in data.iteritems():
+ for key, value in list(data.items()):
data = tuple(["%.06f" % (item) for item in value[:-1]] + [value[-1]])
output.append((key, ) + data)
self.print_table(output)
diff --git a/src/lib/Server/Admin/Pull.py b/src/lib/Server/Admin/Pull.py
index 926eda1b3..47a8be253 100644
--- a/src/lib/Server/Admin/Pull.py
+++ b/src/lib/Server/Admin/Pull.py
@@ -1,7 +1,9 @@
import getopt
import sys
+
import Bcfg2.Server.Admin
+
class Pull(Bcfg2.Server.Admin.MetadataCore):
"""Pull mode retrieves entries from clients and
integrates the information into the repository.
@@ -38,7 +40,7 @@ class Pull(Bcfg2.Server.Admin.MetadataCore):
try:
opts, gargs = getopt.getopt(args, 'vfIs')
except:
- print self.__shorthelp__
+ print(self.__shorthelp__)
raise SystemExit(1)
for opt in opts:
if opt[0] == '-v':
@@ -55,18 +57,20 @@ class Pull(Bcfg2.Server.Admin.MetadataCore):
try:
self.PullEntry(*line.split(None, 3))
except SystemExit:
- print " for %s" % line
+ print(" for %s" % line)
except:
- print "Bad entry: %s" % line.strip()
+ print("Bad entry: %s" % line.strip())
elif len(gargs) < 3:
- print self.__longhelp__
+ print(self.__longhelp__)
raise SystemExit(1)
else:
self.PullEntry(gargs[0], gargs[1], gargs[2])
def BuildNewEntry(self, client, etype, ename):
- """Construct a new full entry for given client/entry from statistics."""
- new_entry = {'type':etype, 'name':ename}
+ """Construct a new full entry for
+ given client/entry from statistics.
+ """
+ new_entry = {'type': etype, 'name': ename}
for plugin in self.bcore.pull_sources:
try:
(owner, group, perms, contents) = \
@@ -74,16 +78,19 @@ class Pull(Bcfg2.Server.Admin.MetadataCore):
break
except Bcfg2.Server.Plugin.PluginExecutionError:
if plugin == self.bcore.pull_sources[-1]:
- print "Pull Source failure; could not fetch current state"
+ print("Pull Source failure; could not fetch current state")
raise SystemExit(1)
try:
- data = {'owner':owner, 'group':group, 'perms':perms, 'text':contents}
+ data = {'owner': owner,
+ 'group': group,
+ 'perms': perms,
+ 'text': contents}
except UnboundLocalError:
print("Unable to build entry. "
"Do you have a statistics plugin enabled?")
raise SystemExit(1)
- for k, v in data.iteritems():
+ for k, v in list(data.items()):
if v:
new_entry[k] = v
#print new_entry
@@ -93,17 +100,22 @@ class Pull(Bcfg2.Server.Admin.MetadataCore):
"""Determine where to put pull data."""
if self.mode == 'interactive':
for choice in choices:
- print "Plugin returned choice:"
+ print("Plugin returned choice:")
if id(choice) == id(choices[0]):
- print "(current entry)",
+ print("(current entry) ")
if choice.all:
- print " => global entry"
+ print(" => global entry")
elif choice.group:
- print (" => group entry: %s (prio %d)" %
- (choice.group, choice.prio))
+ print(" => group entry: %s (prio %d)" %
+ (choice.group, choice.prio))
else:
- print " => host entry: %s" % (choice.hostname)
- if raw_input("Use this entry? [yN]: ") in ['y', 'Y']:
+ print(" => host entry: %s" % (choice.hostname))
+ # py3k compatibility
+ try:
+ ans = raw_input("Use this entry? [yN]: ") in ['y', 'Y']
+ except NameError:
+ ans = input("Use this entry? [yN]: ") in ['y', 'Y']
+ if ans:
return choice
return False
else:
@@ -136,7 +148,7 @@ class Pull(Bcfg2.Server.Admin.MetadataCore):
self.errExit("Configuration upload not supported by plugin %s" \
% (plugin.name))
# Commit if running under a VCS
- for vcsplugin in self.bcore.plugins.values():
+ for vcsplugin in list(self.bcore.plugins.values()):
if isinstance(vcsplugin, Bcfg2.Server.Plugin.Version):
files = "%s/%s" % (plugin.data, ename)
comment = 'file "%s" pulled from host %s' % (files, client)
diff --git a/src/lib/Server/Admin/Query.py b/src/lib/Server/Admin/Query.py
index b5af9bad2..207b65035 100644
--- a/src/lib/Server/Admin/Query.py
+++ b/src/lib/Server/Admin/Query.py
@@ -2,6 +2,7 @@ import logging
import Bcfg2.Logger
import Bcfg2.Server.Admin
+
class Query(Bcfg2.Server.Admin.Mode):
__shorthelp__ = "Query clients"
__longhelp__ = (__shorthelp__ + "\n\nbcfg2-admin query [-n] [-c] "
@@ -32,7 +33,7 @@ class Query(Bcfg2.Server.Admin.Mode):
def __call__(self, args):
Bcfg2.Server.Admin.Mode.__call__(self, args)
- clients = self.meta.clients.keys()
+ clients = list(self.meta.clients.keys())
filename_arg = False
filename = None
for arg in args:
@@ -48,7 +49,7 @@ class Query(Bcfg2.Server.Admin.Mode):
try:
k, v = arg.split('=')
except:
- print "Unknown argument %s" % arg
+ print("Unknown argument %s" % arg)
continue
if k == 'p':
nc = self.meta.get_client_names_by_profiles(v.split(','))
@@ -57,22 +58,22 @@ class Query(Bcfg2.Server.Admin.Mode):
# add probed groups (if present)
for conn in self.bcore.connectors:
if isinstance(conn, Bcfg2.Server.Plugins.Probes.Probes):
- for c, glist in conn.cgroups.items():
+ for c, glist in list(conn.cgroups.items()):
for g in glist:
if g in v.split(','):
nc.append(c)
else:
- print "One of g= or p= must be specified"
+ print("One of g= or p= must be specified")
raise SystemExit(1)
clients = [c for c in clients if c in nc]
if '-n' in args:
for client in clients:
- print client
+ print(client)
else:
- print ','.join(clients)
+ print(','.join(clients))
if '-f' in args:
f = open(filename, "w")
for client in clients:
f.write(client + "\n")
f.close()
- print "Wrote results to %s" % (filename)
+ print("Wrote results to %s" % (filename))
diff --git a/src/lib/Server/Admin/Reports.py b/src/lib/Server/Admin/Reports.py
index 1ac94e5e7..39c9eb71e 100644
--- a/src/lib/Server/Admin/Reports.py
+++ b/src/lib/Server/Admin/Reports.py
@@ -42,7 +42,8 @@ from django.db import connection, transaction
from Bcfg2.Server.Reports.reports.models import Client, Interaction, Entries, \
Entries_interactions, Performance, \
- Reason, Ping, TYPE_CHOICES, InternalDatabaseVersion
+ Reason, Ping
+
def printStats(fn):
"""
@@ -72,6 +73,7 @@ def printStats(fn):
return print_stats
+
class Reports(Bcfg2.Server.Admin.Mode):
'''Admin interface for dynamic reports'''
__shorthelp__ = "Manage dynamic reports"
@@ -97,7 +99,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
def __init__(self, cfile):
Bcfg2.Server.Admin.Mode.__init__(self, cfile)
self.log.setLevel(logging.INFO)
- self.django_commands = [ 'syncdb', 'sqlall', 'validate' ]
+ self.django_commands = ['syncdb', 'sqlall', 'validate']
self.__usage__ = self.__usage__ + " Django commands:\n " + \
"\n ".join(self.django_commands)
@@ -127,54 +129,54 @@ class Reports(Bcfg2.Server.Admin.Mode):
update_database()
elif args[0] == 'load_stats':
quick = '-O3' in args
- stats_file=None
- clients_file=None
- i=1
+ stats_file = None
+ clients_file = None
+ i = 1
while i < len(args):
if args[i] == '-s' or args[i] == '--stats':
- stats_file = args[i+1]
+ stats_file = args[i + 1]
if stats_file[0] == '-':
self.errExit("Invalid statistics file: %s" % stats_file)
elif args[i] == '-c' or args[i] == '--clients-file':
- clients_file = args[i+1]
+ clients_file = args[i + 1]
if clients_file[0] == '-':
self.errExit("Invalid clients file: %s" % clients_file)
i = i + 1
self.load_stats(stats_file, clients_file, verb, quick)
elif args[0] == 'purge':
- expired=False
- client=None
- maxdate=None
- state=None
- i=1
+ expired = False
+ client = None
+ maxdate = None
+ state = None
+ i = 1
while i < len(args):
if args[i] == '-c' or args[i] == '--client':
if client:
self.errExit("Only one client per run")
- client = args[i+1]
- print client
+ client = args[i + 1]
+ print(client)
i = i + 1
elif args[i] == '--days':
if maxdate:
self.errExit("Max date specified multiple times")
try:
- maxdate = datetime.datetime.now() - datetime.timedelta(days=int(args[i+1]))
+ maxdate = datetime.datetime.now() - datetime.timedelta(days=int(args[i + 1]))
except:
- self.log.error("Invalid number of days: %s" % args[i+1])
- raise SystemExit, -1
+ self.log.error("Invalid number of days: %s" % args[i + 1])
+ raise SystemExit(-1)
i = i + 1
elif args[i] == '--expired':
- expired=True
+ expired = True
i = i + 1
if expired:
if state:
self.log.error("--state is not valid with --expired")
- raise SystemExit, -1
+ raise SystemExit(-1)
self.purge_expired(maxdate)
else:
self.purge(client, maxdate, state)
else:
- print "Unknown command: %s" % args[0]
+ print("Unknown command: %s" % args[0])
@transaction.commit_on_success
def scrub(self):
@@ -187,7 +189,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
self.log.error("Failed to load reason objects: %s" % e)
return
dup_reasons = []
-
+
cmp_reasons = dict()
batch_update = []
for reason in BatchFetch(Reason.objects):
@@ -196,7 +198,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
comparisons '''
id = reason.id
reason.id = None
- key=md5(pickle.dumps(reason)).hexdigest()
+ key = md5(pickle.dumps(reason)).hexdigest()
reason.id = id
if key in cmp_reasons:
@@ -207,7 +209,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
else:
cmp_reasons[key] = reason.id
self.log.debug("key %d" % reason.id)
-
+
self.log.debug("Done with updates, deleting dupes")
try:
cursor = connection.cursor()
@@ -248,7 +250,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
try:
statsdata = XML(open(stats_file).read())
except (IOError, XMLSyntaxError):
- self.errExit("StatReports: Failed to parse %s"%(stats_file))
+ self.errExit("StatReports: Failed to parse %s" % (stats_file))
if not clientspath:
try:
@@ -259,10 +261,15 @@ class Reports(Bcfg2.Server.Admin.Mode):
try:
clientsdata = XML(open(clientspath).read())
except (IOError, XMLSyntaxError):
- self.errExit("StatReports: Failed to parse %s"%(clientspath))
+ self.errExit("StatReports: Failed to parse %s" % (clientspath))
try:
- load_stats(clientsdata, statsdata, verb, self.log, quick=quick, location=platform.node())
+ load_stats(clientsdata,
+ statsdata,
+ verb,
+ self.log,
+ quick=quick,
+ location=platform.node())
except:
pass
@@ -270,7 +277,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
def purge(self, client=None, maxdate=None, state=None):
'''Purge historical data from the database'''
- filtered = False # indicates whether or not a client should be deleted
+ filtered = False # indicates whether or not a client should be deleted
if not client and not maxdate and not state:
self.errExit("Reports.prune: Refusing to prune all data")
@@ -282,13 +289,13 @@ class Reports(Bcfg2.Server.Admin.Mode):
ipurge = ipurge.filter(client=cobj)
except Client.DoesNotExist:
self.log.error("Client %s not in database" % client)
- raise SystemExit, -1
+ raise SystemExit(-1)
self.log.debug("Filtering by client: %s" % client)
if maxdate:
filtered = True
if not isinstance(maxdate, datetime.datetime):
- raise TypeError, "maxdate is not a DateTime object"
+ raise TypeError("maxdate is not a DateTime object")
self.log.debug("Filtering by maxdate: %s" % maxdate)
ipurge = ipurge.filter(timestamp__lt=maxdate)
@@ -300,9 +307,9 @@ class Reports(Bcfg2.Server.Admin.Mode):
if state:
filtered = True
- if state not in ('dirty','clean','modified'):
- raise TypeError, "state is not one of the following values " + \
- "('dirty','clean','modified')"
+ if state not in ('dirty', 'clean', 'modified'):
+ raise TypeError("state is not one of the following values " + \
+ "('dirty','clean','modified')")
self.log.debug("Filtering by state: %s" % state)
ipurge = ipurge.filter(state=state)
@@ -346,7 +353,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
if maxdate:
if not isinstance(maxdate, datetime.datetime):
- raise TypeError, "maxdate is not a DateTime object"
+ raise TypeError("maxdate is not a DateTime object")
self.log.debug("Filtering by maxdate: %s" % maxdate)
clients = Client.objects.filter(expiration__lt=maxdate)
else:
@@ -358,4 +365,3 @@ class Reports(Bcfg2.Server.Admin.Mode):
client.delete()
self.log.debug("Pruning orphan Performance objects")
Performance.prune_orphans()
-
diff --git a/src/lib/Server/Admin/Tidy.py b/src/lib/Server/Admin/Tidy.py
index cc8ab4f5e..f79991fd9 100644
--- a/src/lib/Server/Admin/Tidy.py
+++ b/src/lib/Server/Admin/Tidy.py
@@ -4,6 +4,7 @@ import socket
import Bcfg2.Server.Admin
+
class Tidy(Bcfg2.Server.Admin.Mode):
__shorthelp__ = "Clean up useless files in the repo"
__longhelp__ = __shorthelp__ + "\n\nbcfg2-admin tidy [-f] [-I]\n"
@@ -24,17 +25,21 @@ class Tidy(Bcfg2.Server.Admin.Mode):
if '-f' in args or '-I' in args:
if '-I' in args:
for name in badfiles[:]:
- answer = raw_input("Unlink file %s? [yN] " % name)
+ # py3k compatibility
+ try:
+ answer = raw_input("Unlink file %s? [yN] " % name)
+ except NameError:
+ answer = input("Unlink file %s? [yN] " % name)
if answer not in ['y', 'Y']:
badfiles.remove(name)
for name in badfiles:
try:
os.unlink(name)
except IOError:
- print "Failed to unlink %s" % name
+ print("Failed to unlink %s" % name)
else:
for name in badfiles:
- print name
+ print(name)
def buildTidyList(self):
"""Clean up unused or unusable files from the repository."""
@@ -56,7 +61,8 @@ class Tidy(Bcfg2.Server.Admin.Mode):
bad.append(hostname)
for name in os.listdir("%s/SSHbase" % (self.get_repo_path())):
if not hostmatcher.match(name):
- to_remove.append("%s/SSHbase/%s" % (self.get_repo_path(), name))
+ to_remove.append("%s/SSHbase/%s" % (self.get_repo_path(),
+ name))
else:
if hostmatcher.match(name).group(1) in bad:
to_remove.append("%s/SSHbase/%s" %
diff --git a/src/lib/Server/Admin/Viz.py b/src/lib/Server/Admin/Viz.py
index e3daea84b..a77502b5d 100644
--- a/src/lib/Server/Admin/Viz.py
+++ b/src/lib/Server/Admin/Viz.py
@@ -1,7 +1,9 @@
import getopt
from subprocess import Popen, PIPE
+
import Bcfg2.Server.Admin
+
class Viz(Bcfg2.Server.Admin.MetadataCore):
__shorthelp__ = "Produce graphviz diagrams of metadata structures"
__longhelp__ = (__shorthelp__ + "\n\nbcfg2-admin viz [--includehosts] "
@@ -27,7 +29,8 @@ class Viz(Bcfg2.Server.Admin.MetadataCore):
plugin_blacklist = ['DBStats', 'Snapshots', 'Cfg', 'Pkgmgr', 'Packages',
'Rules', 'Account', 'Decisions', 'Deps', 'Git', 'Svn',
- 'Fossil', 'Bzr', 'Bundler', 'TGenshi', 'SGenshi', 'Base']
+ 'Fossil', 'Bzr', 'Bundler', 'TGenshi', 'SGenshi',
+ 'Base']
def __init__(self, cfile):
@@ -43,7 +46,7 @@ class Viz(Bcfg2.Server.Admin.MetadataCore):
['includehosts', 'includebundles',
'includekey', 'outfile='])
except getopt.GetoptError, msg:
- print msg
+ print(msg)
#FIXME: is this for --raw?
#rset = False
@@ -63,8 +66,8 @@ class Viz(Bcfg2.Server.Admin.MetadataCore):
data = self.Visualize(self.get_repo_path(), hset, bset,
kset, outputfile)
- print data
- raise SystemExit, 0
+ print(data)
+ raise SystemExit(0)
def Visualize(self, repopath, hosts=False,
bundles=False, key=False, output=False):
@@ -82,7 +85,7 @@ class Viz(Bcfg2.Server.Admin.MetadataCore):
try:
dotpipe.stdin.write("digraph groups {\n")
except:
- print "write to dot process failed. Is graphviz installed?"
+ print("write to dot process failed. Is graphviz installed?")
raise SystemExit(1)
dotpipe.stdin.write('\trankdir="LR";\n')
dotpipe.stdin.write(self.metadata.viz(hosts, bundles,
diff --git a/src/lib/Server/Admin/Xcmd.py b/src/lib/Server/Admin/Xcmd.py
index 8ea98b79c..e761a5e3d 100644
--- a/src/lib/Server/Admin/Xcmd.py
+++ b/src/lib/Server/Admin/Xcmd.py
@@ -1,9 +1,10 @@
+import sys
+import xmlrpclib
+
import Bcfg2.Options
import Bcfg2.Proxy
import Bcfg2.Server.Admin
-import sys
-import xmlrpclib
class Xcmd(Bcfg2.Server.Admin.Mode):
__shorthelp__ = ("XML-RPC Command Interface")
@@ -16,8 +17,8 @@ class Xcmd(Bcfg2.Server.Admin.Mode):
'user': Bcfg2.Options.CLIENT_USER,
'password': Bcfg2.Options.SERVER_PASSWORD,
'key': Bcfg2.Options.SERVER_KEY,
- 'certificate' : Bcfg2.Options.CLIENT_CERT,
- 'ca' : Bcfg2.Options.CLIENT_CA
+ 'certificate': Bcfg2.Options.CLIENT_CERT,
+ 'ca': Bcfg2.Options.CLIENT_CA
}
setup = Bcfg2.Options.OptionParser(optinfo)
setup.parse(sys.argv[2:])
@@ -25,9 +26,10 @@ class Xcmd(Bcfg2.Server.Admin.Mode):
proxy = Bcfg2.Proxy.ComponentProxy(setup['server'],
setup['user'],
setup['password'],
- key = setup['key'],
- cert = setup['certificate'],
- ca = setup['ca'], timeout=180)
+ key=setup['key'],
+ cert=setup['certificate'],
+ ca=setup['ca'],
+ timeout=180)
if len(setup['args']) == 0:
print("Usage: xcmd <xmlrpc method> <optional arguments>")
return
@@ -36,7 +38,7 @@ class Xcmd(Bcfg2.Server.Admin.Mode):
if len(setup['args']) > 1:
args = tuple(setup['args'][1:])
try:
- data = apply(getattr(proxy, cmd), args)
+ data = getattr(proxy, cmd)(*args)
except xmlrpclib.Fault, flt:
if flt.faultCode == 7:
print("Unknown method %s" % cmd)
@@ -46,4 +48,4 @@ class Xcmd(Bcfg2.Server.Admin.Mode):
else:
raise
if data != None:
- print data
+ print(data)
diff --git a/src/lib/Server/Admin/__init__.py b/src/lib/Server/Admin/__init__.py
index dc3dc8c01..411f909ee 100644
--- a/src/lib/Server/Admin/__init__.py
+++ b/src/lib/Server/Admin/__init__.py
@@ -27,14 +27,17 @@ import sys
import Bcfg2.Server.Core
import Bcfg2.Options
+
class ModeOperationError(Exception):
pass
+
class Mode(object):
"""Help message has not yet been added for mode."""
__shorthelp__ = 'Shorthelp not defined yet'
__longhelp__ = 'Longhelp not defined yet'
__args__ = []
+
def __init__(self, configfile):
self.configfile = configfile
self.__cfp = False
@@ -50,11 +53,11 @@ class Mode(object):
def __call__(self, args):
if len(args) > 0 and args[0] == 'help':
- print self.__longhelp__
+ print(self.__longhelp__)
raise SystemExit(0)
def errExit(self, emsg):
- print emsg
+ print(emsg)
raise SystemExit(1)
def get_repo_path(self):
@@ -80,9 +83,9 @@ class Mode(object):
"""
hdelim = "="
- justify = {'left':str.ljust,
- 'center':str.center,
- 'right':str.rjust}[justify.lower()]
+ justify = {'left': str.ljust,
+ 'center': str.center,
+ 'right': str.rjust}[justify.lower()]
"""
Calculate column widths (longest item in each column
@@ -90,9 +93,9 @@ class Mode(object):
"""
cols = list(zip(*rows))
- colWidths = [max([len(str(item))+2*padding for \
+ colWidths = [max([len(str(item)) + 2 * padding for \
item in col]) for col in cols]
- borderline = vdelim.join([w*hdelim for w in colWidths])
+ borderline = vdelim.join([w * hdelim for w in colWidths])
# Print out the table
print(borderline)
@@ -103,6 +106,7 @@ class Mode(object):
print(borderline)
hdr = False
+
class MetadataCore(Mode):
"""Base class for admin-modes that handle metadata."""
def __init__(self, configfile, usage, pwhitelist=None, pblacklist=None):
@@ -113,9 +117,11 @@ class MetadataCore(Mode):
setup.hm = usage
setup.parse(sys.argv[1:])
if pwhitelist is not None:
- setup['plugins'] = [x for x in setup['plugins'] if x in pwhitelist]
+ setup['plugins'] = [x for x in setup['plugins']
+ if x in pwhitelist]
elif pblacklist is not None:
- setup['plugins'] = [x for x in setup['plugins'] if x not in pblacklist]
+ setup['plugins'] = [x for x in setup['plugins']
+ if x not in pblacklist]
try:
self.bcore = Bcfg2.Server.Core.Core(self.get_repo_path(),
setup['plugins'],
@@ -125,5 +131,6 @@ class MetadataCore(Mode):
self.bcore.fam.handle_events_in_interval(5)
self.metadata = self.bcore.metadata
+
class StructureMode(MetadataCore):
pass