From e1d1fe5329c8b060f9a9b44acfb56db09a484aba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Louis-Francis=20Ratt=C3=A9-Boulianne?= <lfrb@collabora.com>
Date: Fri, 9 Dec 2016 16:13:29 -0500
Subject: [PATCH] Add device class and option to retrieve the image version
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Louis-Francis Ratté-Boulianne <lfrb@collabora.com>
Reviewed-by: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
Differential Revision: https://phabricator.apertis.org/D5227
---
 doc/man/ade-info.1 |  7 ++++
 tools/ade          | 85 +++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 91 insertions(+), 1 deletion(-)

diff --git a/doc/man/ade-info.1 b/doc/man/ade-info.1
index ca20162..d460c65 100644
--- a/doc/man/ade-info.1
+++ b/doc/man/ade-info.1
@@ -12,6 +12,7 @@ ade-info \- Print information about an entity
 .SH SYNOPSIS
 .sp
 .nf
+\fIade info\fR --device [<user>[:<pass>]@]<hostname>[:<port>]
 \fIade info\fR --sysroot <sysroot>
 .fi
 .sp
@@ -22,6 +23,12 @@ Retrieve information about a sysroot or another ADE object\&.
 .SH OPTIONS
 .sp
 .PP
+\fI\-\-device\fR
+.RS 4
+Return image version of given device (e.g. user@apertis)\&.
+.sp
+.RE
+.PP
 \fI\-\-sysroot\fR
 .RS 4
 Return installed version and path of given sysroot (e.g. apertis-16.12-armhf)\&.
diff --git a/tools/ade b/tools/ade
index 65c54e4..99d8816 100755
--- a/tools/ade
+++ b/tools/ade
@@ -17,6 +17,7 @@ import glob
 import logging
 import os
 import pathlib
+import paramiko
 import re
 import shutil
 import struct
@@ -26,6 +27,7 @@ import tempfile
 import urllib
 import xdg.BaseDirectory
 
+from contextlib import contextmanager, closing
 from urllib.error import URLError
 from urllib.parse import urlparse
 from urllib.request import urlopen, urlretrieve
@@ -53,6 +55,14 @@ class SysrootManagerError(Exception):
         return self.message
 
 
+class InvalidDeviceError(Exception):
+    def __init__(self, message):
+        self.message = message
+
+    def __str__(self):
+        return self.message
+
+
 class InvalidSysrootArchiveError(Exception):
     def __init__(self, message):
         self.message = message
@@ -120,6 +130,66 @@ class TargetTriplet:
         raise NotSupportedError
 
 
+class Device:
+
+    def __init__(self, host, port=22, user=None, password=None):
+        self.host = host
+        if port is None:
+            port = 22
+        self.port = port
+        self.user = user
+        if not self.user:
+            self.user = "user"
+        self.password = password
+        self.version = None
+        self._ssh = None
+
+    def from_uri(uri):
+        try:
+            r = urlparse("ssh://" + uri)
+        except Exception:
+            raise InvalidDeviceError("Invalid URI format")
+        return Device(r.hostname, r.port, r.username, r.password)
+
+    def __str__(self):
+        string = ""
+        if self.user:
+            string += user
+            if self.password:
+                string += ':' + self.password
+            string += '@'
+        string += self.host
+        if self.port != 22:
+            string += ':' + str(self.port)
+        return string
+
+    def _connect(self):
+        ssh = paramiko.SSHClient()
+        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+        ssh.connect(self.host, port=self.port,
+                    allow_agent=False, look_for_keys=False, gss_auth=False,
+                    username=self.user, password=self.password,
+                    key_filename=os.path.expanduser("~/.ssh/apertis.ssh"))
+        return ssh
+
+    def _exec(self, *args):
+        with closing(self._connect()) as ssh:
+            stdin, stdout, stderr = ssh.exec_command(' '.join(args))
+            return stdout.read().decode().strip()
+
+    def load_sysroot_version(self):
+        try:
+            v = self._exec('cat', '/etc/image_version')
+            self.version = SysrootVersion.from_string(v)
+        except Exception as e:
+            print(e)
+            raise InvalidDeviceError("No image_version file found")
+        a = self._exec('uname', '-m')
+        if a not in TargetTriplet.SUPPORTED:
+            raise InvalidDeviceError("Unsupported architecture")
+        self.version.arch = a
+
+
 class SysrootVersion:
 
     def __init__(self, distro, release, arch, date=None, build=None, author=None, url=None):
@@ -486,6 +556,7 @@ class Ade:
 
         self.target = None
         self.sysroot = None
+        self.device = None
 
         self.sysroot_version = None
         self.distro = None
@@ -550,8 +621,14 @@ class Ade:
             except InvalidSysrootError:
                 self.die("Invalid sysroot installed for {0}{1}{2}" \
                          .format(Colors.OKBLUE, version.get_name(), Colors.ENDC))
+        elif self.device:
+            try:
+                self.target = Device.from_uri(self.device)
+                self.target.load_sysroot_version()
+            except InvalidDeviceError as e:
+                self.die("Invalid device: {0}".format(e))
         else:
-            self.die("No target (sysroot) specified")
+            self.die("No target (sysroot or device) specified")
 
         return self.target
 
@@ -749,6 +826,11 @@ class Ade:
             if self.format == 'parseable':
                 print("SysrootVersion:{0}".format(self.target.version.get_tag()))
                 print("SysrootPath:{0}".format(self.target.path))
+        elif isinstance(target, Device):
+            self.info("* Device has image version {0}{1}{2}"
+                    .format(Colors.WARNING, self.target.version, Colors.ENDC))
+            if self.format == 'parseable':
+                print("ImageVersion:{0}".format(self.target.version.get_tag()))
 
     def info(self, message):
         if self.format == 'friendly':
@@ -780,6 +862,7 @@ if __name__ == '__main__':
     info_parser = subparsers.add_parser('info', help="Retrieve information about an object")
     group = info_parser.add_mutually_exclusive_group()
     group.add_argument('--sysroot', help="Use sysroot as target (e.g. apertis-16.12-armhf)")
+    group.add_argument('--device', help="Use device as target (e.g. user:pass@apertis)")
 
     # Sysroot parser
     sysroot_parser = subparsers.add_parser('sysroot', help='Sysroot related commands')
-- 
GitLab