Move object subclass instance allocation logic out of suite.py/resource.py

This way we get rid of object related content in resource.py and we can
finally move it to core/ in next commit.

Change-Id: Ia5b861115ae3ae1b90149863c4265dcc730b8cd4
diff --git a/src/osmo_gsm_tester/obj/bts.py b/src/osmo_gsm_tester/obj/bts.py
index 8b05ea0..e23b555 100644
--- a/src/osmo_gsm_tester/obj/bts.py
+++ b/src/osmo_gsm_tester/obj/bts.py
@@ -162,6 +162,34 @@
         'Nothing to do by default. Subclass can override if required.'
         pass
 
+    def get_instance_by_type(suite_run, conf):
+        """Allocate a BTS child class based on type. Opts are passed to the newly created object."""
+        bts_type = conf.get('type')
+        if bts_type is None:
+            raise RuntimeError('BTS type is not defined!')
+
+        if bts_type == 'osmo-bts-sysmo':
+            from .bts_sysmo import SysmoBts
+            bts_class = SysmoBts
+        elif bts_type == 'osmo-bts-trx':
+            from .bts_osmotrx import OsmoBtsTrx
+            bts_class = OsmoBtsTrx
+        elif bts_type == 'osmo-bts-oc2g':
+            from .bts_oc2g import OsmoBtsOC2G
+            bts_class = OsmoBtsOC2G
+        elif bts_type == 'osmo-bts-octphy':
+            from .bts_octphy import OsmoBtsOctphy
+            bts_class = OsmoBtsOctphy
+        elif bts_type == 'osmo-bts-virtual':
+            from .bts_osmovirtual import OsmoBtsVirtual
+            bts_class = OsmoBtsVirtual
+        elif bts_type == 'nanobts':
+            from .bts_nanobts import NanoBts
+            bts_class = NanoBts
+        else:
+            raise log.Error('BTS type not supported:', bts_type)
+        return bts_class(suite_run, conf)
+
 ###################
 # PUBLIC (test API included)
 ###################
diff --git a/src/osmo_gsm_tester/obj/enb.py b/src/osmo_gsm_tester/obj/enb.py
index c652761..7514604 100644
--- a/src/osmo_gsm_tester/obj/enb.py
+++ b/src/osmo_gsm_tester/obj/enb.py
@@ -192,6 +192,22 @@
 
         return rf_dev_args
 
+    def get_instance_by_type(suite_run, conf):
+        """Allocate a ENB child class based on type. Opts are passed to the newly created object."""
+        enb_type = conf.get('type')
+        if enb_type is None:
+            raise RuntimeError('ENB type is not defined!')
+
+        if enb_type == 'amarisoftenb':
+            from .enb_amarisoft import AmarisoftENB
+            enb_class = AmarisoftENB
+        elif enb_type == 'srsenb':
+            from .enb_srs import srsENB
+            enb_class = srsENB
+        else:
+            raise log.Error('ENB type not supported:', enb_type)
+        return  enb_class(suite_run, conf)
+
 ###################
 # PUBLIC (test API included)
 ###################
diff --git a/src/osmo_gsm_tester/obj/epc.py b/src/osmo_gsm_tester/obj/epc.py
index c725f76..cbca0fb 100644
--- a/src/osmo_gsm_tester/obj/epc.py
+++ b/src/osmo_gsm_tester/obj/epc.py
@@ -57,6 +57,25 @@
         'Nothing to do by default. Subclass can override if required.'
         pass
 
+    def get_instance_by_type(suite_run, run_node):
+        """Allocate a EPC child class based on type. Opts are passed to the newly created object."""
+        values = dict(epc=config.get_defaults('epc'))
+        config.overlay(values, dict(epc=suite_run.config().get('epc', {})))
+        epc_type = values['epc'].get('type', None)
+        if epc_type is None:
+            raise RuntimeError('EPC type is not defined!')
+
+        if epc_type == 'amarisoftepc':
+            from .epc_amarisoft import AmarisoftEPC
+            epc_class = AmarisoftEPC
+        elif epc_type == 'srsepc':
+            from .epc_srs import srsEPC
+            epc_class = srsEPC
+        else:
+            raise log.Error('EPC type not supported:', epc_type)
+
+        return  epc_class(suite_run, run_node)
+
 ###################
 # PUBLIC (test API included)
 ###################
diff --git a/src/osmo_gsm_tester/obj/ms.py b/src/osmo_gsm_tester/obj/ms.py
index b72333a..a30a9c7 100644
--- a/src/osmo_gsm_tester/obj/ms.py
+++ b/src/osmo_gsm_tester/obj/ms.py
@@ -38,11 +38,48 @@
 class MS(log.Origin, metaclass=ABCMeta):
     """Base for everything about mobile/modem and SIMs."""
 
+##############
+# PROTECTED
+##############
     def __init__(self, name, conf):
         super().__init__(log.C_TST, name)
         self._conf = conf
         self.msisdn = None
 
+########################
+# PUBLIC - INTERNAL API
+########################
+    @abstractmethod
+    def cleanup(self):
+        """Cleans up resources allocated."""
+        pass
+
+    def get_instance_by_type(suite_run, conf):
+        """Allocate a MS child class based on type. Opts are passed to the newly created object."""
+        ms_type = conf.get('type')
+        if ms_type is None:
+            # Map None to ofono for forward compability
+            ms_type = 'ofono'
+
+        if ms_type == 'ofono':
+            from .ms_ofono import Modem
+            ms_class = Modem
+        elif ms_type == 'osmo-mobile':
+            from .ms_osmo_mobile import MSOsmoMobile
+            ms_class = MSOsmoMobile
+        elif ms_type == 'srsue':
+            from .ms_srs import srsUE
+            ms_class = srsUE
+        elif ms_type == 'amarisoftue':
+            from .ms_amarisoft import AmarisoftUE
+            ms_class = AmarisoftUE
+        else:
+            raise log.Error('MS type not supported:', ms_type)
+        return ms_class(suite_run, conf)
+
+###################
+# PUBLIC (test API included)
+###################
     def imsi(self):
         return self._conf.get('imsi')
 
@@ -60,8 +97,3 @@
 
     def msisdn(self):
         return self.msisdn
-
-    @abstractmethod
-    def cleanup(self):
-        """Cleans up resources allocated."""
-        pass