Commit f24a2ecc authored by Bryton Lacquement's avatar Bryton Lacquement 🚪

wip

parent fa583d3f
......@@ -27,7 +27,7 @@
#
##############################################################################
import ConfigParser
from six.moves import configparser
import os
from slapos.cli.command import Command
......@@ -77,7 +77,7 @@ class ConfigCommand(Command):
if not os.path.exists(cfg_path):
raise ConfigError('Configuration file does not exist: %s' % cfg_path)
configp = ConfigParser.SafeConfigParser()
configp = configparser.SafeConfigParser()
if configp.read(cfg_path) != [cfg_path]:
# bad permission, etc.
raise ConfigError('Cannot parse configuration file: %s' % cfg_path)
......
......@@ -121,7 +121,7 @@ def _replaceParameterValue(original_content, to_replace):
"""
for key, value in to_replace:
original_content = re.sub('%s\s+=.*' % key, '%s = %s' % (key, value),
original_content)
str(original_content))
return original_content
def _generateSlaposNodeConfigurationFile(slapos_node_config_path, args):
......@@ -148,7 +148,7 @@ def _generateSlaposNodeConfigurationFile(slapos_node_config_path, args):
slapos_node_configuration_content = re.sub(
'(key_file|cert_file|certificate_repository_path).*=.*\n',
'', slapos_node_configuration_content)
with open(slapos_node_config_path, 'w') as fout:
with open(slapos_node_config_path, 'wb') as fout:
fout.write(slapos_node_configuration_content.encode('utf8'))
def _generateSlaposProxyConfigurationFile(conf):
......@@ -171,7 +171,7 @@ def _generateSlaposProxyConfigurationFile(conf):
slapos_proxy_configuration_content = _replaceParameterValue(
slapos_proxy_configuration_template, to_replace)
with open(slapos_proxy_configuration_path, 'w') as fout:
with open(slapos_proxy_configuration_path, 'wb') as fout:
fout.write(slapos_proxy_configuration_content.encode('utf8'))
return slapos_proxy_configuration_path
......
......@@ -28,7 +28,7 @@
##############################################################################
import atexit
import ConfigParser
import six.moves.configparser
import os
import sys
......@@ -119,7 +119,7 @@ def _getSoftwareReleaseFromSoftwareString(logger, software_string, product):
try:
return product.__getattr__(software_string[len(SOFTWARE_PRODUCT_NAMESPACE):])
except AttributeError as e:
logger.error('Error: %s Exiting now.' % e.message)
logger.error('Error: %s Exiting now.' % e)
sys.exit(1)
def do_console(local):
......
......@@ -27,6 +27,7 @@
#
##############################################################################
from __future__ import print_function
from psutil import process_iter, NoSuchProcess, AccessDenied
from time import strftime
import shutil
......@@ -157,5 +158,5 @@ def do_collect(conf):
database.garbageCollect()
except AccessDenied:
print "You HAVE TO execute this script with root permission."
print("You HAVE TO execute this script with root permission.")
......@@ -27,6 +27,7 @@
#
##############################################################################
from __future__ import print_function
import psutil
import os
import subprocess
......@@ -159,7 +160,7 @@ class HeatingContributionSnapshot(_Snapshot):
result = launchTemperatureTest(sensor_id)
if result is None:
print "Impossible to test sensor: %s " % sensor_id
print("Impossible to test sensor: %s " % sensor_id)
initial_temperature, final_temperature, duration = result
......
......@@ -38,7 +38,7 @@ import subprocess
import tarfile
import tempfile
import time
import xmlrpclib
from six.moves import xmlrpc_client as xmlrpclib
from supervisor import xmlrpc
......@@ -150,7 +150,7 @@ class Software(object):
self.software_min_free_space = software_min_free_space
def check_free_space(self):
required = self.software_min_free_space
required = self.software_min_free_space or 0
available = free_space_nonroot(self.software_path)
if available < required:
......@@ -293,7 +293,7 @@ class Software(object):
def _create_buildout_profile(self, buildout_cfg, url):
with open(buildout_cfg, 'wb') as fout:
fout.write('[buildout]\nextends = ' + url + '\n')
fout.write(('[buildout]\nextends = ' + url + '\n').encode('utf-8'))
self._set_ownership(buildout_cfg)
def uploadSoftwareRelease(self, tarpath):
......@@ -419,7 +419,7 @@ class Partition(object):
def check_free_space(self):
required = self.instance_min_free_space
required = self.instance_min_free_space or 0
available = free_space_nonroot(self.instance_path)
if available < required:
......
......@@ -11,6 +11,7 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
from __future__ import print_function
import ast
import json
......@@ -29,10 +30,10 @@ try:
else:
LIBNETWORKCACHE_ENABLED = True
except:
print 'There was problem while trying to import slapos.libnetworkcache:'\
'\n%s' % traceback.format_exc()
print('There was problem while trying to import slapos.libnetworkcache:'\
'\n%s' % traceback.format_exc())
LIBNETWORKCACHE_ENABLED = False
print 'Networkcache forced to be disabled.'
print('Networkcache forced to be disabled.')
......@@ -45,8 +46,8 @@ def fallback_call(function):
try:
return function(self, *args, **kwd)
except: # indeed, *any* exception is swallowed
print 'There was problem while calling method %r:\n%s' % (
function.__name__, traceback.format_exc())
print('There was problem while calling method %r:\n%s' % (
function.__name__, traceback.format_exc()))
return False
wrapper.__doc__ = function.__doc__
return wrapper
......@@ -107,7 +108,7 @@ def download_network_cached(cache_url, dir_url, software_url, software_root,
f.close()
file_descriptor.close()
return True
except (IOError, DirectoryNotFound), e:
except (IOError, DirectoryNotFound) as e:
logger.info('Failed to download from network cache %s: %s' % \
(software_url, str(e)))
return False
......@@ -169,7 +170,7 @@ def upload_network_cached(software_root, software_url, cached_key,
try:
return nc.upload_generic(f, cached_key, **kw)
except (IOError, UploadError), e:
except (IOError, UploadError) as e:
logger.info('Failed to upload file. %s' % (str(e)))
return False
finally:
......
......@@ -38,7 +38,7 @@ import importlib
import traceback
import psutil
from multiprocessing import Process, Queue as MQueue
import Queue
from six.moves import queue
from slapos.util import mkdir_p, chownDirectory
from slapos.grid.utils import dropPrivileges, killProcessTree
from slapos.grid.promise import interface
......@@ -168,7 +168,7 @@ class PromiseProcess(Process):
if not os.path.exists(init_file):
with open(init_file, 'w') as f:
f.write("")
os.chmod(init_file, 0644)
os.chmod(init_file, 0o644)
# add promise folder to sys.path so we can import promise script
if sys.path[0] != promise_folder:
sys.path[0:0] = [promise_folder]
......@@ -362,7 +362,7 @@ class PromiseLauncher(object):
try:
result = PromiseQueueResult()
result.load(json.loads(f.read()))
except ValueError, e:
except ValueError as e:
result = None
self.logger.warn('Bad promise JSON result at %r: %s' % (
promise_output_file,
......@@ -375,7 +375,7 @@ class PromiseLauncher(object):
while True:
try:
self.queue_result.get_nowait()
except Queue.Empty:
except queue.Empty:
return
def _updateFolderOwner(self, folder_path=None):
......@@ -443,7 +443,7 @@ class PromiseLauncher(object):
if not promise_process.is_alive():
try:
queue_item = self.queue_result.get(True, 1)
except Queue.Empty:
except queue.Empty:
# no result found in process result Queue
pass
else:
......
......@@ -410,7 +410,7 @@ class GenericPromise(object):
try:
self.__queue.put_nowait(result_item)
break
except Queue.Full, e:
except Queue.Full as e:
error = e
time.sleep(0.5)
if error:
......@@ -459,7 +459,7 @@ class GenericPromise(object):
"""
try:
self.sense()
except Exception, e:
except Exception as e:
# log the result
self.logger.error(str(e))
if check_anomaly:
......@@ -468,7 +468,7 @@ class GenericPromise(object):
result = self.anomaly()
if result is None:
raise ValueError("Promise anomaly method returned 'None'")
except Exception, e:
except Exception as e:
result = AnomalyResult(problem=True, message=str(e))
else:
if isinstance(result, AnomalyResult) and result.hasFailed() and can_bang:
......@@ -482,7 +482,7 @@ class GenericPromise(object):
result = self.test()
if result is None:
raise ValueError("Promise test method returned 'None'")
except Exception, e:
except Exception as e:
result = TestResult(problem=True, message=str(e))
if self.__logger_buffer is not None:
......
......@@ -32,17 +32,16 @@ import subprocess
import functools
import signal
import traceback
from zope import interface as zope_interface
from zope.interface import implementer
from slapos.grid.promise import interface
from slapos.grid.promise.generic import GenericPromise
@implementer(interface.IPromise)
class WrapPromise(GenericPromise):
"""
A wrapper promise used to run old promises style and bash promises
"""
zope_interface.implements(interface.IPromise)
def __init__(self, config):
GenericPromise.__init__(self, config)
self.setPeriodicity(minute=2)
......
......@@ -32,7 +32,7 @@ import os
import pkg_resources
import random
import socket
import StringIO
from six import StringIO
import subprocess
import sys
import tempfile
......@@ -672,7 +672,7 @@ stderr_logfile_backups=1
computer_partition.setComputerPartitionRelatedInstanceList(
[reference for reference in tf.read().split('\n') if reference]
)
except NotFoundError, e:
except NotFoundError as e:
# Master doesn't implement this feature ?
self.logger.warning("NotFoundError: %s. \nCannot send requested instance "\
"list to master. Please check if this feature is"\
......@@ -922,7 +922,7 @@ stderr_logfile_backups=1
self._checkPromiseList(local_partition,
check_anomaly=True,
force=False)
except PromiseError, e:
except PromiseError as e:
self.logger.error(e)
if partition_access_status is None or not status_error:
computer_partition.error(e, logger=self.logger)
......@@ -1140,7 +1140,7 @@ stderr_logfile_backups=1
(computer_partition_id, computer_partition_state)
computer_partition.error(error_string, logger=self.logger)
raise NotImplementedError(error_string)
except Exception, e:
except Exception as e:
if not isinstance(e, PromiseError):
with open(error_output_file, 'w') as error_file:
# Write error message in a log file assible to computer partition user
......
......@@ -35,7 +35,7 @@ import subprocess
import stat
import sys
import time
import xmlrpclib
from six.moves import xmlrpc_client as xmlrpclib
from slapos.grid.utils import (createPrivateDirectory, SlapPopen, updateFile)
......
......@@ -40,6 +40,8 @@ import logging
import psutil
import time
import six
from slapos.grid.exception import BuildoutFailedError, WrongPermissionError
# Such umask by default will create paths with full permission
......@@ -131,12 +133,12 @@ class SlapPopen(subprocess.Popen):
break
if line:
output_lines.append(line)
logger.info(line.rstrip('\n'))
logger.info(line.rstrip(b'\n'))
self.output = ''.join(output_lines)
def md5digest(url):
return hashlib.md5(url).hexdigest()
return hashlib.md5(url.encode('utf-8')).hexdigest()
def getCleanEnvironment(logger, home_path='/tmp'):
......@@ -150,7 +152,7 @@ def getCleanEnvironment(logger, home_path='/tmp'):
if old is not None:
removed_env.append(k)
changed_env['HOME'] = env['HOME'] = home_path
for k in sorted(changed_env.iterkeys()):
for k in sorted(six.iterkeys(changed_env)):
logger.debug('Overridden %s = %r' % (k, changed_env[k]))
if removed_env:
logger.debug('Removed from environment: %s' % ', '.join(sorted(removed_env)))
......@@ -399,7 +401,7 @@ def killProcessTree(pid, logger):
for child in running_process_list:
try:
child.suspend()
except psutil.Error, e:
except psutil.Error as e:
logger.debug(str(e))
time.sleep(0.2)
......@@ -408,5 +410,5 @@ def killProcessTree(pid, logger):
for process in process_list:
try:
process.kill()
except psutil.Error, e:
except psutil.Error as e:
logger.debug("Process kill: %s" % e)
......@@ -5,12 +5,12 @@ import os.path
import pwd
import time
from zope import interface as zope_interface
from zope.interface import implementer
from slapos.manager import interface
logger = logging.getLogger(__name__)
@implementer(interface.IManager)
class Manager(object):
"""Manage cgroup's cpuset in terms on initializing and runtime operations.
......@@ -21,8 +21,6 @@ class Manager(object):
TODO: there is no limit on number of reserved cores per user.
"""
zope_interface.implements(interface.IManager)
cpu_exclusive_file = ".slapos-cpu-exclusive"
cpuset_path = "/sys/fs/cgroup/cpuset/"
task_write_mode = "wt"
......
......@@ -2,5 +2,5 @@
import pkg_resources
DB_VERSION = pkg_resources.resource_stream('slapos.proxy', 'schema.sql').readline().strip().split(':')[1]
DB_VERSION = pkg_resources.resource_stream('slapos.proxy', 'schema.sql').readline().strip().split(b':')[1]
......@@ -28,6 +28,8 @@
#
##############################################################################
from __future__ import print_function
from lxml import etree
import random
import string
......@@ -42,6 +44,8 @@ import xml_marshaller
from xml_marshaller.xml_marshaller import loads
from xml_marshaller.xml_marshaller import dumps
from six.moves import range
app = Flask(__name__)
EMPTY_DICT_XML = dumps({})
......@@ -139,7 +143,7 @@ def execute_db(table, query, args=(), one=False, db_version=None, log=False, db=
db_version = DB_VERSION
query = query % (table + db_version,)
if log:
print query
print(query)
try:
cur = db.execute(query, args)
except:
......@@ -404,7 +408,7 @@ def parseRequestComputerPartitionForm(form):
return parsed_dict
run_id = ''.join([random.choice(string.ascii_letters + string.digits) for n in xrange(32)])
run_id = ''.join([random.choice(string.ascii_letters + string.digits) for n in range(32)])
def checkIfMasterIsCurrentMaster(master_url):
"""
Because there are several ways to contact this server, we can't easily check
......
......@@ -34,7 +34,7 @@ import slapos.cli.configure_local
from slapos.cli.configure_local import ConfigureLocalCommand, _createConfigurationDirectory
from slapos.cli.entry import SlapOSApp
from argparse import Namespace
from ConfigParser import ConfigParser
from six.moves.configparser import ConfigParser
# Disable any command to launch slapformat and supervisor
slapos.cli.configure_local._runFormat = lambda x: "Do nothing"
......
......@@ -47,7 +47,7 @@ class SlapPopenTestCase(unittest.TestCase):
def test_exec(self):
"""Test command execution with SlapPopen.
"""
self.script.write('#!/bin/sh\necho "hello"\nexit 123')
self.script.write(b'#!/bin/sh\necho "hello"\nexit 123')
self.script.close()
logger = mock.MagicMock()
......
......@@ -28,7 +28,7 @@ import unittest
from zope.interface.verify import verifyClass
import zope.interface
import types
from six import class_types
from slapos import slap
def getOnlyImplementationAssertionMethod(klass, method_list):
......@@ -61,7 +61,7 @@ def generateTestMethodListOnClass(klass, module):
"""Generate test method on klass"""
for class_id in dir(module):
implementing_class = getattr(module, class_id)
if type(implementing_class) not in (types.ClassType, types.TypeType):
if not isinstance(implementing_class, class_types):
continue
# add methods to assert that publicly available classes are defining
# interfaces
......
......@@ -59,7 +59,7 @@ class TestSlapOSPromiseMixin(unittest.TestCase):
def writeInit(self):
with open(os.path.join(self.plugin_dir, '__init__'), 'w') as f:
f.write('')
os.chmod(os.path.join(self.plugin_dir, '__init__'), 0644)
os.chmod(os.path.join(self.plugin_dir, '__init__'), 0o644)
if sys.path[0] != self.plugin_dir:
sys.path[0:0] = [self.plugin_dir]
......@@ -128,7 +128,7 @@ class TestSlapOSPromiseMixin(unittest.TestCase):
wrap=wrap,
)
def writeFile(self, path, content, mode=0644):
def writeFile(self, path, content, mode=0o644):
with open(path, 'w') as f:
f.write(content)
os.chmod(path, mode)
......@@ -781,7 +781,7 @@ class RunPromise(GenericPromise):
f.write("""#!/bin/bash
echo "success"
""")
os.chmod(promise_path, 0744)
os.chmod(promise_path, 0o744)
def test_method(result):
self.called = True
......@@ -810,7 +810,7 @@ echo "success"
echo "This promise failed"
exit 1
""")
os.chmod(promise_path, 0744)
os.chmod(promise_path, 0o744)
self.configureLauncher()
state_file = os.path.join(self.partition_dir, PROMISE_STATE_FOLDER_NAME)
......@@ -841,13 +841,13 @@ exit 1
echo "promise 1 succeeded"
exit 0
""")
os.chmod(promise_path, 0744)
os.chmod(promise_path, 0o744)
with open(promise_path2, 'w') as f:
f.write("""#!/bin/bash
echo "promise 2 succeeded"
exit 0
""")
os.chmod(promise_path2, 0744)
os.chmod(promise_path2, 0o744)
self.generatePromiseScript("first_promise.py", success=True)
self.generatePromiseScript("second_promise.py", success=True)
......@@ -989,7 +989,7 @@ exit 0
sleep 20
echo "success"
""")
os.chmod(promise_path, 0744)
os.chmod(promise_path, 0o744)
def test_method(result):
self.called = True
......@@ -1025,7 +1025,7 @@ sleep 20
echo "success"
exit 1
""")
os.chmod(promise_path, 0744)
os.chmod(promise_path, 0o744)
createPromise(first_promise_path)
createPromise(second_promise_path)
......
......@@ -25,6 +25,8 @@
#
##############################################################################
from __future__ import print_function
import logging
import os
import unittest
......@@ -51,7 +53,7 @@ class SlapMixin(unittest.TestCase):
self.server_url = 'http://localhost/'
else:
self.server_url = self._server_url
print 'Testing against SLAP server %r' % self.server_url
print('Testing against SLAP server %r' % self.server_url)
self.slap = slapos.slap.slap()
self.partition_id = 'PARTITION_01'
if os.environ.has_key('SLAPGRID_INSTANCE_ROOT'):
......@@ -1205,7 +1207,7 @@ class TestSoftwareProductCollection(SlapMixin):
self.assertEqual(self.product_collection.foo, '0')
if __name__ == '__main__':
print 'You can point to any SLAP server by setting TEST_SLAP_SERVER_URL '\
'environment variable'
print('You can point to any SLAP server by setting TEST_SLAP_SERVER_URL '\
'environment variable')
unittest.main()
......@@ -26,6 +26,8 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from __future__ import print_function
import glob
import logging
import slapos.format
......@@ -86,7 +88,7 @@ class FakeCallAndRead:
retval = 0, 'UP'
global INTERFACE_DICT
if 'useradd' in argument_list:
print argument_list
print(argument_list)
global USER_LIST
username = argument_list[-1]
if username == '-r':
......
......@@ -38,13 +38,13 @@ import tempfile
import textwrap
import time
import unittest
import urlparse
from six.moves.urllib import parse
import json
import re
import xml_marshaller
from mock import patch
from zope import interface
from zope.interface import implementer
import slapos.slap.slap
import slapos.grid.utils
......@@ -113,7 +113,7 @@ class BasicMixin(object):
self.manager_list = []
self.software_root = os.path.join(self._tempdir, 'software')
self.instance_root = os.path.join(self._tempdir, 'instance')
if os.environ.has_key('SLAPGRID_INSTANCE_ROOT'):
if 'SLAPGRID_INSTANCE_ROOT' in os.environ:
del os.environ['SLAPGRID_INSTANCE_ROOT']
logging.basicConfig(level=logging.DEBUG)
self.setSlapgrid()
......@@ -314,9 +314,9 @@ class ComputerForTest(object):
"""
self.sequence.append(url.path)
if req.method == 'GET':
qs = urlparse.parse_qs(url.query)
qs = parse.parse_qs(url.query)
else:
qs = urlparse.parse_qs(req.body)
qs = parse.parse_qs(req.body)
if (url.path == '/getFullComputerInformation'
and 'computer_id' in qs):
slap_computer = self.getComputer(qs['computer_id'][0])
......@@ -549,9 +549,8 @@ touch worked"""):
fout.write(str(periodicity))
@implementer(IManager)
class DummyManager(object):
interface.implements(IManager)
def __init__(self):
self.sequence = []
......@@ -2151,8 +2150,8 @@ echo "ERROR: $var"
exit 1
""")
os.chmod(self.firewall_cmd_add, 0755)
os.chmod(self.firewall_cmd_remove, 0755)
os.chmod(self.firewall_cmd_add, 0o755)
os.chmod(self.firewall_cmd_remove, 0o755)
firewall_conf= dict(
authorized_sources=source_ip,
......@@ -2543,7 +2542,7 @@ exit 0
os.makedirs(pre_delete_dir, 0o700)
with open(pre_delete_script, 'w') as f:
f.write(self.prerm_script_content)
os.chmod(pre_delete_script, 0754)
os.chmod(pre_delete_script, 0o754)
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(partition.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
......@@ -2593,7 +2592,7 @@ exit 0
os.makedirs(pre_delete_dir, 0o700)
with open(pre_delete_script, 'w') as f:
f.write(self.prerm_script_content)
os.chmod(pre_delete_script, 0754)
os.chmod(pre_delete_script, 0o754)
self.assertTrue(os.path.exists(pre_delete_script))
manager_list = slapmanager.from_config({'manager_list': 'prerm'})
......@@ -2640,7 +2639,7 @@ exit 0
os.makedirs(pre_delete_dir, 0o700)
with open(pre_delete_script, 'w') as f:
f.write(self.prerm_script_content)
os.chmod(pre_delete_script, 0754)
os.chmod(pre_delete_script, 0o754)
self.assertEqual(partition.state, 'started')
manager_list = slapmanager.from_config({'manager_list': 'prerm'})
self.grid._manager_list = manager_list
......@@ -2675,7 +2674,7 @@ exit 0
os.makedirs(pre_delete_dir, 0o700)
with open(pre_delete_script, 'w') as f:
f.write(self.prerm_script_content)
os.chmod(pre_delete_script, 0754)
os.chmod(pre_delete_script, 0o754)
manager_list = slapmanager.from_config({'manager_list': 'prerm'})
self.grid._manager_list = manager_list
......
......@@ -28,7 +28,7 @@
#
##############################################################################
import ConfigParser
from six.moves import configparser
import os
import logging
import shutil
......@@ -106,7 +106,7 @@ database_uri = %(tempdir)s/lib/proxy.db
Set config for slapproxy and start it
"""
conf = slapos.proxy.ProxyConfig(logger=logging.getLogger())
configp = ConfigParser.SafeConfigParser()
configp = configparser.SafeConfigParser()
configp.read(self.slapos_cfg)
conf.mergeConfig(ProxyOption(self.proxy_db), configp)
conf.setConfig()
......@@ -1040,7 +1040,7 @@ database_uri = %(tempdir)s/lib/external_proxy.db
self.external_proxy_slap._connection_helper.GET('/')
except slapos.slap.NotFoundError:
break
except slapos.slap.ConnectionError, socket.error:
except (slapos.slap.ConnectionError, socket.error):
attempts = attempts + 1
time.sleep(0.1)
else:
......
......@@ -65,9 +65,9 @@ class TestUtil(unittest.TestCase):
wanted_directory0 = os.path.join(root_slaptest, 'slap-write0')
wanted_directory1 = os.path.join(root_slaptest, 'slap-write0', 'write-slap1')
wanted_directory2 = os.path.join(root_slaptest, 'slap-write0', 'write-slap1', 'write-teste2')
wanted_directory_mkdir0 = os.makedirs(wanted_directory0, mode=0777)
wanted_directory_mkdir1 = os.makedirs(wanted_directory1, mode=0777)
wanted_directory_mkdir2 = os.makedirs(wanted_directory2, mode=0777)
wanted_directory_mkdir0 = os.makedirs(wanted_directory0, mode=0o777)
wanted_directory_mkdir1 = os.makedirs(wanted_directory1, mode=0o777)
wanted_directory_mkdir2 = os.makedirs(wanted_directory2, mode=0o777)
create_file_txt = tempfile.mkstemp(suffix='.txt', prefix='tmp', dir=wanted_directory2, text=True)
user = 'nobody'
try:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment