parent
0695b86add
commit
e9b7b52bb0
@ -1,39 +1,174 @@
|
||||
import os
|
||||
import grp
|
||||
import pathlib
|
||||
import pwd
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
##
|
||||
import paramiko
|
||||
##
|
||||
import arb_util
|
||||
|
||||
|
||||
class Mirror(object):
|
||||
def __init__(self, mirror_xml, ns = '', *args, **kwargs):
|
||||
self.xml = mirror_xml
|
||||
self.ns = ns
|
||||
if os.environ.get('SUDO_USER'):
|
||||
_uname = os.environ['SUDO_USER']
|
||||
else:
|
||||
_uname = pwd.getpwuid(os.geteuid()).pw_name
|
||||
self.user = pwd.getpwnam(mirror_xml.attrib.get('user', _uname))
|
||||
self.fmode = int(self.xml.attrib.get('fileMode', '0600'), 8)
|
||||
self.dmode = int(self.xml.attrib.get('dirMode', '0700'), 8)
|
||||
user, uid, self.is_sudo = arb_util.getSudoUser()
|
||||
self.user = pwd.getpwnam(mirror_xml.attrib.get('user', user.pw_name))
|
||||
try:
|
||||
self.fmode = int(mirror_xml.attrib.get('fileMode'), 8)
|
||||
except TypeError:
|
||||
self.fmode = None
|
||||
try:
|
||||
self.dmode = int(mirror_xml.attrib.get('dirMode'), 8)
|
||||
except TypeError:
|
||||
self.dmode = None
|
||||
self.dest = self.xml.text
|
||||
|
||||
def sync(self):
|
||||
# no-op; this is handled in the subclasses since it's unique to them.
|
||||
pass
|
||||
return(True)
|
||||
|
||||
|
||||
class LocalMirror(Mirror):
|
||||
def __init__(self, mirror_xml, ns = '', *args, **kwargs):
|
||||
super().__init__(mirror_xml, ns = ns, *args, **kwargs)
|
||||
if os.environ.get('SUDO_GID'):
|
||||
_grpnm = os.environ['SUDO_GID']
|
||||
else:
|
||||
_grpnm = grp.getgrgid(os.getegid()).gr_name
|
||||
self.group = grp.getgrnam(mirror_xml.attrib.get('group', _grpnm))
|
||||
if self.user.pw_uid == arb_util.getSudoUser()[1]:
|
||||
self.user = None
|
||||
group, gid, is_sudo = arb_util.getSudoGroup()
|
||||
self.group = grp.getgrnam(mirror_xml.attrib.get('group', group.gr_name))
|
||||
if self.group.gr_gid == gid:
|
||||
self.group = None
|
||||
self.dest = os.path.abspath(os.path.expanduser(self.dest))
|
||||
|
||||
def sync(self, source):
|
||||
source = os.path.abspath(os.path.expanduser(source))
|
||||
for root, dirs, files in os.walk(source):
|
||||
for d in dirs:
|
||||
dpath = os.path.join(root, d)
|
||||
reldpath = pathlib.PurePosixPath(dpath).relative_to(source)
|
||||
destdpath = os.path.join(self.dest, reldpath)
|
||||
if os.path.exists(destdpath):
|
||||
shutil.rmtree(destdpath)
|
||||
shutil.copytree(dpath, destdpath, symlinks = True, ignore_dangling_symlinks = True)
|
||||
for f in files:
|
||||
fpath = os.path.join(root, f)
|
||||
relfpath = pathlib.PurePosixPath(fpath).relative_to(source)
|
||||
destfpath = os.path.join(self.dest, relfpath)
|
||||
shutil.copy2(fpath, destfpath)
|
||||
break # We only need one iteration since copytree is recursive
|
||||
# Now we set the user/group ownership and the file/dir modes.
|
||||
# This first any() check is DEFINITELY a speed optimization if those perms aren't modified.
|
||||
if any((self.user, self.group, self.fmode, self.dmode)):
|
||||
if self.user:
|
||||
os.chown(self.dest, self.user.pw_uid, -1, follow_symlinks = False)
|
||||
if self.group:
|
||||
os.chown(self.dest, -1, self.group.gr_gid, follow_symlinks = False)
|
||||
if self.dmode:
|
||||
try:
|
||||
os.chmod(self.dest, self.dmode, follow_symlinks = False)
|
||||
except NotImplementedError:
|
||||
os.chmod(self.dest, self.dmode)
|
||||
for root, dirs, files in os.walk(self.dest):
|
||||
for d in dirs:
|
||||
dpath = os.path.join(root, d)
|
||||
if self.user:
|
||||
os.chown(dpath, self.user.pw_uid, -1, follow_symlinks = False)
|
||||
if self.group:
|
||||
os.chown(dpath, -1, self.group.gr_gid, follow_symlinks = False)
|
||||
if self.dmode:
|
||||
try:
|
||||
os.chmod(dpath, self.dmode, follow_symlinks = False)
|
||||
except NotImplementedError:
|
||||
os.chmod(dpath, self.dmode)
|
||||
for f in files:
|
||||
fpath = os.path.join(root, f)
|
||||
if self.user:
|
||||
os.chown(fpath, self.user.pw_uid, -1, follow_symlinks = False)
|
||||
if self.group:
|
||||
os.chown(fpath, -1, self.group.gr_gid, follow_symlinks = False)
|
||||
if self.fmode:
|
||||
try:
|
||||
os.chmod(fpath, self.fmode, follow_symlinks = False)
|
||||
except NotImplementedError:
|
||||
os.chmod(fpath, self.fmode)
|
||||
return(True)
|
||||
|
||||
|
||||
class RemoteMirror(Mirror):
|
||||
def __init__(self, mirror_xml, ns = '', *args, **kwargs):
|
||||
super().__init__(mirror_xml, ns = ns, *args, **kwargs)
|
||||
self.server = mirror_xml.attrib['server']
|
||||
self.hardened = arb_util.xmlBool(mirror_xml.attrib.get('hardened', False))
|
||||
self.port = int(mirror_xml.attrib.get('port', 22))
|
||||
self.keyfile = os.path.abspath(os.path.expanduser(mirror_xml.attrib.get('key', '~/.ssh/id_rsa')))
|
||||
self.remote_user = mirror_xml.attrib.get('remoteUser')
|
||||
self.remote_group = mirror_xml.attrib.get('remoteGroup')
|
||||
self.remote_group = mirror_xml.attrib.get('remoteGroup')
|
||||
self.ssh = None
|
||||
self.transport = None
|
||||
|
||||
def _initSSH(self):
|
||||
has_ssh = False
|
||||
if self.ssh and self.transport.is_active() and self.transport.is_alive():
|
||||
has_ssh = True
|
||||
if not has_ssh:
|
||||
userhostkeys = os.path.abspath(os.path.expanduser('~/.ssh/known_hosts'))
|
||||
self.ssh = paramiko.SSHClient()
|
||||
self.ssh.load_system_host_keys()
|
||||
if os.path.isfile(userhostkeys):
|
||||
self.ssh.load_system_host_keys(userhostkeys)
|
||||
self.ssh.set_missing_host_key_policy((paramiko.RejectPolicy
|
||||
if self.hardened else
|
||||
paramiko.AutoAddPolicy))
|
||||
self.ssh.connect(hostname = self.server,
|
||||
port = self.port,
|
||||
username = self.user.pw_name,
|
||||
key_filename = self.keyfile)
|
||||
self.transport = self.ssh.get_transport()
|
||||
return()
|
||||
|
||||
def _closeSSH(self):
|
||||
if self.transport:
|
||||
self.transport.close()
|
||||
if self.ssh:
|
||||
self.ssh.close()
|
||||
return()
|
||||
|
||||
def sync(self, source):
|
||||
source = os.path.abspath(os.path.expanduser(source))
|
||||
cmd = ['rsync',
|
||||
'--archive',
|
||||
# '--delete', # TODO: yes? no? configurable?
|
||||
'--rsh="ssh -p {0} -l {1}"'.format(self.port, self.user.pw_name),
|
||||
source,
|
||||
'{0}@{1}:{2}'.format(self.user.pw_name, self.server, self.dest)]
|
||||
# TODO: log output?
|
||||
rsync_out = subprocess.run(cmd, stderr = subprocess.PIPE, stdout = subprocess.PIPE)
|
||||
# This first if is technically unnecessary, but it can offer a *slight* speed benefit. VERY slight.
|
||||
# As in so negligible, only Jthan would care about it.
|
||||
if any((self.remote_user, self.remote_group, self.fmode, self.dmode)):
|
||||
if self.remote_user:
|
||||
self._initSSH()
|
||||
stdin, stdout, stderr = self.ssh.exec_command('chown -R {0} {1}'.format(self.remote_user,
|
||||
self.dest))
|
||||
if self.remote_group:
|
||||
self._initSSH()
|
||||
stdin, stdout, stderr = self.ssh.exec_command('chgrp -R {0} {1}'.format(self.remote_group,
|
||||
self.dest))
|
||||
if self.fmode:
|
||||
self._initSSH()
|
||||
stdin, stdout, stderr = self.ssh.exec_command(
|
||||
('find {0} -type f -print0 | '
|
||||
'xargs --null --no-run-if-empty chmod {1}').format(self.dest,
|
||||
re.sub('^0o', '', oct(self.fmode))))
|
||||
if self.dmode:
|
||||
self._initSSH()
|
||||
stdin, stdout, stderr = self.ssh.exec_command(
|
||||
('find {0} -type d -print0 | '
|
||||
'xargs --null --no-run-if-empty chmod {1}').format(self.dest,
|
||||
re.sub('^0o', '', oct(self.dmode))))
|
||||
self._closeSSH()
|
||||
return(True)
|
||||
|
@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This obviously will require some tweaking. Will roll into build.py later.
|
||||
set -e
|
||||
|
||||
server=my_repo.domain.tld
|
||||
port=2222
|
||||
user=pkgrepo
|
||||
src=~/pkgs/built/.
|
||||
# You should use rrsync to restrict to a specific directory
|
||||
dest='Arch/.'
|
||||
|
||||
echo "Syncing..."
|
||||
rsync -a --delete -e "ssh -p ${port}" ${src} ${user}@${server}:${dest}
|
||||
echo "Done."
|
Loading…
Reference in new issue