From be0a4f3f229a1677727fab99762521344670e3f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= <karel.koci@nic.cz>
Date: Wed, 15 Jul 2020 09:39:40 +0200
Subject: [PATCH] nsfarm: use ABC class to define abstract classes

This is arguably better than raising NotImplementedError. It also
catches inconsistencies much sooner.
---
 nsfarm/board/_board.py | 25 +++++++++++++------------
 nsfarm/board/omnia.py  |  4 ++++
 nsfarm/cli.py          |  8 ++++----
 nsfarm/lxd/device.py   |  5 +++--
 4 files changed, 24 insertions(+), 18 deletions(-)

diff --git a/nsfarm/board/_board.py b/nsfarm/board/_board.py
index 178959e..a8c974d 100644
--- a/nsfarm/board/_board.py
+++ b/nsfarm/board/_board.py
@@ -1,6 +1,7 @@
 """This defines generic board and its helpers.
 """
 import sys
+import abc
 import time
 import logging
 import serial
@@ -14,7 +15,7 @@ MINITERM_DEFAULT_MENU = '\x14'  # Ctrl+T
 MINITERM_ENCODING = sys.getdefaultencoding()
 
 
-class Board:
+class Board(abc.ABC):
     """General abstract class defining handle for board.
     """
 
@@ -62,10 +63,11 @@ class Board:
         self._pexpect.sendline("")
         return cli.Uboot(self._pexpect)
 
-    def bootup(self, device_wan):
+    def bootup(self, device_wan, os_branch):
         """Boot board using TFTP boot. This ensures that board is booted up and ready to accept commands.
 
         device_wan: Wan device to board. This is instance of nsfarm.lxd.NetInterface.
+        os_branch: Turris OS branch to download medkit from.
 
         Returns instance of cli.Shell
         """
@@ -74,7 +76,7 @@ class Board:
         # Now load image from TFTP
         with Container("boot", devices=[device_wan, ]) as cont:
             ccli = cli.Shell(cont.pexpect())
-            ccli.run("prepare_turris_image")
+            ccli.run(f"prepare_turris_image {os_branch}")
             uboot.run('setenv ipaddr 192.168.1.142')
             uboot.run('setenv serverip 192.168.1.1')
             self._board_bootup(uboot)
@@ -85,12 +87,12 @@ class Board:
         shell.run("sysctl -w kernel.printk='0 4 1 7'")  # disable kernel print to not confuse console flow
         return shell
 
+    @abc.abstractmethod
     def _board_bootup(self, uboot):
         """Board specific bootup routine.
 
         It has to implement TFTP uboot routines.
         """
-        raise NotImplementedError
 
     def serial_miniterm(self):
         """Runs interactive miniterm on serial TTY interface.
@@ -116,26 +118,25 @@ class Board:
 
         sys.stderr.write("\n--- Miniterm exit ---\n")
 
-    @property
+    @abc.abstractproperty
     def wan(self):
         """Network interface name for WAN interface in router
         """
-        raise NotImplementedError
 
-    @property
+    @abc.abstractproperty
     def lan1(self):
         """Network interface name for LAN1 interface in router
+        Note that this not matches lan1 port on router but rather is primary lan port.
         """
-        raise NotImplementedError
 
-    @property
+    @abc.abstractproperty
     def lan2(self):
         """Network interface name for LAN2 interface in router
+        Note that this not matches lan2 port on router but rather is secondary test port (such as different switch).
         """
-        raise NotImplementedError
 
-    @property
+    @abc.abstractproperty
     def lan3(self):
         """Network interface name for LAN3 interface in router
+        Note that this not matches lan3 port on router but rather is tercial test port (such as different switch).
         """
-        raise NotImplementedError
diff --git a/nsfarm/board/omnia.py b/nsfarm/board/omnia.py
index 8e4572b..8c736e2 100644
--- a/nsfarm/board/omnia.py
+++ b/nsfarm/board/omnia.py
@@ -25,3 +25,7 @@ class Omnia(Board):
     @property
     def lan2(self):
         return "lan3"
+
+    @property
+    def lan3(self):
+        return "lan4"
diff --git a/nsfarm/cli.py b/nsfarm/cli.py
index f1ebdd6..e934cd1 100644
--- a/nsfarm/cli.py
+++ b/nsfarm/cli.py
@@ -8,6 +8,7 @@ support for shell and u-boot.  They differ in a way how they handle prompt and m
 # There are new line character matches in regular expressions. Correct one is \r\n but some serial controlles for some
 # reason also use \n\r so we match both alternatives.
 #
+import abc
 import logging
 import base64
 
@@ -31,7 +32,7 @@ def run_exit_code_zero(exit_code):
     return 0
 
 
-class Cli:
+class Cli(abc.ABC):
     """This is generic abstraction on top of pexpect for command line interface.
     """
 
@@ -44,20 +45,19 @@ class Cli:
         # Just propagate anything we do not implement to pexect handle
         return getattr(self._pe, name)
 
+    @abc.abstractmethod
     def prompt(self, **kwargs):
         """Follow output until prompt is reached and parse it.  Exit code is returned.
         All keyword arguments are passed to pexpect's expect call.
         """
-        raise NotImplementedError
 
-    @property
+    @abc.abstractproperty
     def output(self):
         """All output before latest prompt.
 
         This is everything not matched till prompt is located.  Note that this is for some implementations same as
         pexpect before but in others it can differ so you should always use this property instead of before.
         """
-        raise NotImplementedError
 
     def command(self, cmd=""):
         """Calls pexpect sendline and expect cmd with trailing new line.
diff --git a/nsfarm/lxd/device.py b/nsfarm/lxd/device.py
index cd680fe..32b8750 100644
--- a/nsfarm/lxd/device.py
+++ b/nsfarm/lxd/device.py
@@ -1,8 +1,9 @@
 """Devices management and assigment to containers.
 """
+import abc
 
 
-class Device:
+class Device(abc.ABC):
     """Generic device handler for LXD container.
 
     This is device handler that can be assigned to containers. Note that it can be assigned to only one container at the
@@ -34,11 +35,11 @@ class Device:
             self._assignment[-2].unfreeze()
         self._assignment.remove(container)
 
+    @abc.abstractmethod
     def _definition(self):
         """This method has to be implemented by child class and should return definition of device that is later
         provided to caller as result of calling acquire().
         """
-        raise NotImplementedError
 
     @property
     def container(self):
-- 
GitLab