| from argparse import ArgumentParser |
| import io |
| import os |
| import tempfile |
| |
| from duffy.client import DuffyClient |
| from fabric import Connection |
| from invoke import run as local |
| |
| class DuffyWrapper: |
| def __init__(self, auth_name, auth_key): |
| self.c = DuffyClient(url="https://duffy.ci.centos.org/api/v1", auth_name=auth_name, auth_key=auth_key) |
| self.last_session = None |
| |
| def get_hostnames(self, *query): |
| if not self.last_session: |
| self.request_session(*query) |
| |
| nodes = [n for n in self.last_session.session.nodes |
| if all(q in n.pool for q in query)] |
| return [n |
| for n in [n.data.get('provision',{}).get('public_hostname',None) for n in nodes] |
| if n] |
| |
| def find_hostnames(self, *query): |
| ls = self.c.list_sessions() |
| nodes = [n for s in ls.sessions for n in s.nodes |
| if all(q in n.pool for q in query)] |
| return [n |
| for n in [n.data.get('provision',{}).get('public_hostname',None) for n in nodes] |
| if n] |
| |
| def find_pool_name(self, *query): |
| return [p.name for p in self.c.list_pools().pools if all(q in p.name for q in query)] |
| |
| def request_session(self, *query): |
| pool = self.find_pool_name(*query) |
| session = self.c.request_session([{"pool":pool[0], "quantity":"1"}]) |
| self.last_session = session |
| return session |
| |
| class TmuxWrapper: |
| def __init__(self, host, session='default-session', private_key=None, **rest): |
| self.host = host |
| if private_key: |
| self.c = Connection(host,connect_kwargs={ |
| "key_filename": private_key, |
| }, **rest) |
| else: |
| self.c = Connection(host, **rest) |
| |
| self.c.run("dnf -y install git tmux") |
| self.ensure_session(session) |
| |
| def ensure_session(self,session='default-session'): |
| return self.c.run("tmux has-session -t {session} || tmux new -s {session} -d".format(session=session)) |
| |
| def run(self, cmd, session='default-session'): |
| return self.c.run("tmux send-keys -t {session}:0 '{cmd}' ENTER".format(session=session, cmd=cmd)) |
| |
| def run_and_notify(self, cmd, session='default-session'): |
| return self.c.run("tmux send-keys -t {session}:0 '{cmd}; tmux wait-for -S {session}' ENTER".format(session=session, cmd=cmd)) |
| |
| def wait_for(self, session='default-session'): |
| return self.c.run("tmux wait-for {session}".format(session=session)) |
| |
| def send_folder(self, path, remote): |
| with tempfile.NamedTemporaryFile() as t: |
| local("tar cf {to} -C {path} .".format(path=path, to=t.name)) |
| print(t.name) |
| self.c.run("mkdir -p {}".format(remote)) |
| self.c.put(local=t.name, remote=remote) |
| self.c.run("cd {path} && tar xf {name}".format(path=remote, name=t.name.split('/')[-1])) |
| def get_pane(self): |
| return tmux.c.run("tmux capture-pane -p -t default-session") |
| def set_repo(self, baseurl="https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/"): |
| repo = io.StringIO(""" |
| [baseos-compose] |
| name=CentOS Stream $releasever - BaseOS |
| baseurl={baseurl}/BaseOS/$arch/os/ |
| gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial |
| gpgcheck=0 |
| repo_gpgcheck=0 |
| metadata_expire=6h |
| countme=1 |
| enabled=1 |
| |
| [appstream-compose] |
| name=CentOS Stream $releasever - AppStream |
| baseurl={baseurl}/AppStream/$arch/os/ |
| gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial |
| gpgcheck=0 |
| repo_gpgcheck=0 |
| metadata_expire=6h |
| countme=1 |
| enabled=1 |
| """.format(baseurl=baseurl)) |
| self.c.put(repo, "/tmp/compose.repo") |
| self.c.run("dnf repolist | awk '(NR>1) {print $1}' | xargs dnf config-manager --disable") |
| self.c.run("dnf config-manager --add-repo /tmp/compose.repo") |
| self.c.run("dnf config-manager --enable baseos-compose") |
| self.c.run("dnf config-manager --enable appstream-compose") |
| self.c.run("dnf clean all") |
| |
| |
| |
| def runtests(auth_name, auth_key, query, path=None, compose=None, private_key=None): |
| d = DuffyWrapper(auth_name=auth_name, auth_key=auth_key) |
| print("Getting", *query) |
| hostnames = d.get_hostnames(*query) |
| if not hostnames: |
| raise Exception("Didn't manage to find or provision matching machine.") |
| |
| hostname = hostnames[0] |
| print("root@"+hostname) |
| tmux = TmuxWrapper("root@"+hostname, private_key=private_key) |
| if compose: |
| print("Setting compose", compose) |
| tmux.set_repo(compose) |
| if path: |
| print("Sending local repo",path ) |
| tmux.send_folder(path, "/opt/t_functional") |
| else: |
| tmux.c.run("git clone https://github.com/CentOS/sig-core-t_functional /opt/t_functional") |
| tmux.ensure_session() |
| tmux.run("cd /opt/t_functional") |
| tmux.run("export SYSTEMD_PAGER=") |
| tmux.run("ls -l") |
| tmux.c.run("cd /opt//t_functional && /bin/bash runtests.sh 0_common") |
| return tmux |
| |
| |
| def main(): |
| parser = ArgumentParser(prog='My App') |
| parser.add_argument('--arch', help="The duffy query.") |
| parser.add_argument('--path', help="", default=None, required=False) |
| parser.add_argument('--release', help="" ) |
| parser.add_argument('--compose', help="", default=None, required=False) |
| parser.add_argument('--sshkey', help="", default=None, required=False) |
| args = parser.parse_args() |
| auth_name=os.getenv("DUFFY_AUTH_NAME") |
| auth_key=os.getenv("DUFFY_AUTH_KEY") |
| print("Running with args:",args.arch, args.release, args.path, args.compose, args.sshkey) |
| if auth_name and auth_key: |
| runtests(auth_name, auth_key, ['virt', args.arch, args.release], path=args.path, compose=args.compose, private_key=args.sshkey) |
| else: |
| raise Exception("Duffy key or auth name not available") |
| |
| if __name__ == "__main__": |
| print("Running tests") |
| main() |