Add tox 41/6941/3
authorBin Yang <bin.yang@windriver.com>
Tue, 26 Oct 2021 10:28:10 +0000 (18:28 +0800)
committerBin Yang <bin.yang@windriver.com>
Wed, 27 Oct 2021 05:11:17 +0000 (13:11 +0800)
Issue-ID: INF-196
Signed-off-by: Bin Yang <bin.yang@windriver.com>
Change-Id: I668e72886de29f894ababa99f3ac0a84f552f87c

41 files changed:
.gitignore
Dockerfile
LICENSE [new file with mode: 0644]
README.md
constraints.txt [new file with mode: 0644]
docker-compose.yml
o2common/__init__.py [moved from src/__init__.py with 100% similarity]
o2dms/__init__.py [moved from src/o2common/__init__.py with 100% similarity]
o2ims/__init__.py [moved from src/o2dms/__init__.py with 100% similarity]
o2ims/adapter/clients/__init__.py [moved from src/o2ims/__init__.py with 100% similarity]
o2ims/adapter/clients/ocloud_sa_client.py [new file with mode: 0644]
o2ims/adapter/clients/orm_stx.py [new file with mode: 0644]
o2ims/adapter/notifications.py [moved from src/o2ims/adapter/notifications.py with 96% similarity]
o2ims/adapter/ocloud_repository.py [moved from src/o2ims/adapter/ocloud_repository.py with 88% similarity]
o2ims/adapter/orm.py [moved from src/o2ims/adapter/orm.py with 91% similarity]
o2ims/adapter/redis_eventpublisher.py [moved from src/o2ims/adapter/redis_eventpublisher.py with 100% similarity]
o2ims/bootstrap.py [moved from src/o2ims/bootstrap.py with 91% similarity]
o2ims/config.py [moved from src/o2ims/config.py with 77% similarity]
o2ims/domain/__init__.py [moved from src/o2ims/domain/__init__.py with 100% similarity]
o2ims/domain/commands.py [moved from src/o2ims/domain/commands.py with 89% similarity]
o2ims/domain/events.py [moved from src/o2ims/domain/events.py with 99% similarity]
o2ims/domain/ocloud.py [moved from src/o2ims/domain/ocloud.py with 68% similarity]
o2ims/domain/resource_type.py [moved from src/o2ims/domain/resource_type.py with 93% similarity]
o2ims/domain/stx_object.py [new file with mode: 0644]
o2ims/entrypoints/__init__.py [moved from src/o2ims/entrypoints/__init__.py with 100% similarity]
o2ims/entrypoints/flask_application.py [moved from src/o2ims/entrypoints/flask_application.py with 81% similarity]
o2ims/entrypoints/redis_eventconsumer.py [moved from src/o2ims/entrypoints/redis_eventconsumer.py with 100% similarity]
o2ims/service/__init__.py [moved from src/o2ims/service/__init__.py with 100% similarity]
o2ims/service/client/__init__.py [new file with mode: 0644]
o2ims/service/client/base_client.py [new file with mode: 0644]
o2ims/service/handlers.py [moved from src/o2ims/service/handlers.py with 81% similarity]
o2ims/service/messagebus.py [moved from src/o2ims/service/messagebus.py with 98% similarity]
o2ims/service/unit_of_work.py [moved from src/o2ims/service/unit_of_work.py with 94% similarity]
o2ims/views/ocloud_view.py [moved from src/o2ims/views/ocloud_view.py with 100% similarity]
requirements-test.txt
requirements.txt
setup.py [new file with mode: 0644]
src/o2dms/setup.py [deleted file]
src/setup.py [deleted file]
tests/e2e/__init__.py [new file with mode: 0644]
tox.ini [new file with mode: 0644]

index ea29b71..addeb00 100644 (file)
@@ -2,3 +2,5 @@
 .mypy_cache
 __pycache__
 *.egg-info
+*.pyc
+.tox
\ No newline at end of file
index e1afbcb..593e21f 100644 (file)
@@ -1,13 +1,20 @@
 FROM python:3.10-slim-buster\r
 \r
+RUN apt-get update; apt-get install -y git gcc\r
 COPY requirements.txt /tmp/\r
-RUN pip install -r /tmp/requirements.txt\r
+COPY constraints.txt /tmp/\r
+\r
+RUN pip install -r /tmp/requirements.txt -c /tmp/constraints.txt\r
 \r
 COPY requirements-test.txt /tmp/\r
 RUN pip install -r /tmp/requirements-test.txt\r
 \r
 RUN mkdir -p /src\r
-COPY src/ /src/\r
+COPY o2ims/ /src/o2ims/\r
+COPY o2dms/ /src/o2dms/\r
+COPY o2common/ /src/o2common/\r
+COPY setup.py /src/\r
+\r
 RUN pip install -e /src\r
 \r
 COPY tests/ /tests/\r
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
index eb6900a..e4c75d9 100644 (file)
--- a/README.md
+++ b/README.md
@@ -5,23 +5,11 @@
 docker-compose build\r
 ```\r
 \r
-\r
-## Creating a local virtualenv (optional)\r
-\r
-```sh\r
-python3.8 -m venv .venv && source .venv/bin/activate\r
-pip install -r requirements.txt\r
-pip install -e src/\r
-```\r
-\r
 ## Running the tests\r
 \r
 ```sh\r
 docker-compose up -d\r
 docker-compose run --rm --no-deps --entrypoint=pytest api /tests/unit /tests/integration\r
-pytest tests/unit\r
-pytest tests/integration\r
-pytest tests/e2e\r
 ```\r
 \r
 ## Tear down containers\r
@@ -29,3 +17,16 @@ pytest tests/e2e
 ```sh\r
 docker-compose down --remove-orphans\r
 ```\r
+\r
+## Test with local virtualenv\r
+\r
+```sh\r
+python3.8 -m venv .venv && source .venv/bin/activate\r
+pip install -r requirements.txt -c constraints.txt\r
+pip install -r requirements-test.txt\r
+pip install -e o2ims\r
+# pip install -e o2dms -e o2common\r
+pytest tests/unit\r
+pytest tests/integration\r
+pytest tests/e2e\r
+```\r
diff --git a/constraints.txt b/constraints.txt
new file mode 100644 (file)
index 0000000..a1cc510
--- /dev/null
@@ -0,0 +1,4 @@
+# -e git+https://opendev.org/starlingx/distcloud-client.git@master#egg=distributedcloud-client&subdirectory=distributedcloud-client
+# -e git+https://opendev.org/starlingx/config.git@master#egg=cgtsclient&subdirectory=sysinv/cgts-client/cgts-client
+cryptography==3.3.2
+python-keystoneclient==3.21.0
\ No newline at end of file
index 91d3ec8..255fbd9 100644 (file)
@@ -16,11 +16,13 @@ services:
       - REDIS_HOST=redis
       - PYTHONDONTWRITEBYTECODE=1
     volumes:
-      - ./src:/src
+      - ./o2ims:/o2ims
+      - ./o2dms:/o2dms
+      - ./o2common:/o2common
       - ./tests:/tests
     entrypoint:
       - python
-      - /src/o2ims/entrypoints/redis_eventconsumer.py
+      - /o2ims/entrypoints/redis_eventconsumer.py
 
   api:
     image: o2imsdms-image
@@ -32,11 +34,16 @@ services:
       - API_HOST=api
       - REDIS_HOST=redis
       - PYTHONDONTWRITEBYTECODE=1
-      - FLASK_APP=o2ims/entrypoints/flask_application.py
+      - FLASK_APP=/o2ims/entrypoints/flask_application.py
       - FLASK_DEBUG=1
       - PYTHONUNBUFFERED=1
+      - STX_AUTH_URL=http://192.168.204.1:5000/v3
+      - STX_USERNAME=admin
+      - STX_PASSWORD=password1
     volumes:
-      - ./src:/src
+      - ./o2ims:/o2ims
+      - ./o2dms:/o2dms
+      - ./o2common:/o2common
       - ./tests:/tests
     entrypoint:
       - flask
similarity index 100%
rename from src/__init__.py
rename to o2common/__init__.py
similarity index 100%
rename from src/o2common/__init__.py
rename to o2dms/__init__.py
similarity index 100%
rename from src/o2dms/__init__.py
rename to o2ims/__init__.py
diff --git a/o2ims/adapter/clients/ocloud_sa_client.py b/o2ims/adapter/clients/ocloud_sa_client.py
new file mode 100644 (file)
index 0000000..b8607ef
--- /dev/null
@@ -0,0 +1,94 @@
+# Copyright (C) 2021 Wind River Systems, Inc.\r
+#\r
+#  Licensed under the Apache License, Version 2.0 (the "License");\r
+#  you may not use this file except in compliance with the License.\r
+#  You may obtain a copy of the License at\r
+#\r
+#      http://www.apache.org/licenses/LICENSE-2.0\r
+#\r
+#  Unless required by applicable law or agreed to in writing, software\r
+#  distributed under the License is distributed on an "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+#  See the License for the specific language governing permissions and\r
+#  limitations under the License.\r
+\r
+# client talking to Stx standalone\r
+\r
+from service.client.base_client import BaseClient\r
+from typing import List\r
+# Optional,  Set\r
+from o2ims.domain import stx_object as ocloudModel\r
+from o2ims import config\r
+\r
+\r
+class StxSaOcloudClient(BaseClient):\r
+    def __init__(self):\r
+        super().__init__()\r
+        self.driver = StxSaClientImp()\r
+\r
+    # def list(self) -> List[ocloudModel.StxGenericModel]:\r
+    #     return self._list()\r
+\r
+    # def get(self, id) -> ocloudModel.StxGenericModel:\r
+    #     return self._get(id)\r
+\r
+    def _get(self, id) -> ocloudModel.StxGenericModel:\r
+        raise self.driver.getInstanceInfo()\r
+\r
+    def _list(self):\r
+        return [self.driver.getInstanceInfo()]\r
+\r
+\r
+class StxSaResourcePoolClient(BaseClient):\r
+    def __init__(self):\r
+        super().__init__()\r
+        self.driver = StxSaClientImp()\r
+\r
+    def _get(self, id) -> ocloudModel.StxGenericModel:\r
+        return self.driver.getInstanceInfo()\r
+\r
+    def _list(self):\r
+        return [self.driver.getInstanceInfo()]\r
+\r
+\r
+class StxSaDmsClient(BaseClient):\r
+    def __init__(self):\r
+        super().__init__()\r
+        self.driver = StxSaClientImp()\r
+\r
+    def _get(self, id) -> ocloudModel.StxGenericModel:\r
+        return self.driver.getK8sDetail(id)\r
+\r
+    def _list(self):\r
+        return self.driver.getK8sList()\r
+\r
+# internal driver which implement client call to Stx Standalone instance\r
+\r
+# from keystoneauth1.identity import v3\r
+# from keystoneauth1 import session\r
+# # from keystoneclient.v3 import ksclient\r
+# from starlingxclient.v3 import stxclient\r
+\r
+\r
+class StxSaClientImp(object):\r
+    def __init__(self, access_info=None) -> None:\r
+        super().__init__()\r
+        self.access_info = access_info\r
+        if self.access_info is None:\r
+            self.access_info = config.get_stx_access_info()\r
+        # self.auth = auth = v3.Password(\r
+        #     auth_url="http://example.com:5000/v3", username="admin",\r
+        #     password="password", project_name="admin",\r
+        #     user_domain_id="default", project_domain_id="default")\r
+        # self.session = sess = session.Session(auth=auth)\r
+        # # self.keystone = ksclient.Client(session=sess)\r
+        # self.stx = stxclient.Client(session=sess)\r
+\r
+    def getInstanceInfo(self) -> ocloudModel.StxGenericModel:\r
+        raise NotImplementedError\r
+\r
+    def getK8sList(self) -> List[ocloudModel.StxGenericModel]:\r
+        raise NotImplementedError\r
+\r
+    def getK8sDetail(self, id) -> ocloudModel.StxGenericModel:\r
+        raise NotImplementedError\r
diff --git a/o2ims/adapter/clients/orm_stx.py b/o2ims/adapter/clients/orm_stx.py
new file mode 100644 (file)
index 0000000..02f9bec
--- /dev/null
@@ -0,0 +1,51 @@
+# Copyright (C) 2021 Wind River Systems, Inc.\r
+#\r
+#  Licensed under the Apache License, Version 2.0 (the "License");\r
+#  you may not use this file except in compliance with the License.\r
+#  You may obtain a copy of the License at\r
+#\r
+#      http://www.apache.org/licenses/LICENSE-2.0\r
+#\r
+#  Unless required by applicable law or agreed to in writing, software\r
+#  distributed under the License is distributed on an "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+#  See the License for the specific language governing permissions and\r
+#  limitations under the License.\r
+\r
+# from datetime import datetime\r
+import logging\r
+\r
+from sqlalchemy import (\r
+    Table,\r
+    MetaData,\r
+    Column,\r
+    # Integer,\r
+    String,\r
+    # Date,\r
+    DateTime,\r
+    # ForeignKey,\r
+    # event,\r
+)\r
+\r
+from sqlalchemy.orm import mapper\r
+# from sqlalchemy.sql.expression import true\r
+\r
+from o2ims.domain import stx_object as ocloudModel\r
+\r
+logger = logging.getLogger(__name__)\r
+\r
+metadata = MetaData()\r
+\r
+stxobject = Table(\r
+    "stxcache",\r
+    metadata,\r
+    Column("id", String(255), primary_key=True),\r
+    Column("name", String(255)),\r
+    Column("lastupdate", DateTime),\r
+    Column("content", String(255))\r
+)\r
+\r
+\r
+def start_o2ims_stx_mappers():\r
+    logger.info("Starting O2 IMS Stx mappers")\r
+    mapper(ocloudModel.StxGenericModel, stxobject)\r
similarity index 96%
rename from src/o2ims/adapter/notifications.py
rename to o2ims/adapter/notifications.py
index e2ad375..4c49c1b 100644 (file)
@@ -1,17 +1,17 @@
 # pylint: disable=too-few-public-methods
 import abc
-import smtplib
 from o2ims import config
 
 
+SMO_O2_ENDPOINT = config.get_smo_o2endpoint()
+
+
 class AbstractNotifications(abc.ABC):
     @abc.abstractmethod
     def send(self, message):
         raise NotImplementedError
 
 
-SMO_O2_ENDPOINT = config.get_smo_o2endpoint()
-
 class SmoO2Notifications(AbstractNotifications):
     def __init__(self, smoO2Endpoint=SMO_O2_ENDPOINT):
         self.smoO2Endpoint = smoO2Endpoint
similarity index 88%
rename from src/o2ims/adapter/ocloud_repository.py
rename to o2ims/adapter/ocloud_repository.py
index 6d3d221..8a547fe 100644 (file)
 
 import abc
 from typing import Set
-from o2ims.adapter import orm
+from o2ims.adapter import orm
 from o2ims.domain import ocloud
 
+
 class OcloudRepository(abc.ABC):
     def __init__(self):
         self.seen = set()  # type: Set[ocloud.Ocloud]
@@ -30,7 +31,7 @@ class OcloudRepository(abc.ABC):
         if ocloud:
             self.seen.add(ocloud)
         return ocloud
-    
+
     def update(self, ocloud: ocloud.Ocloud):
         self._update(ocloud)
 
@@ -60,7 +61,8 @@ class OcloudSqlAlchemyRepository(OcloudRepository):
         # self.session.add_all(ocloud.deploymentManagers)
 
     def _get(self, ocloudid) -> ocloud.Ocloud:
-        return self.session.query(ocloud.Ocloud).filter_by(oCloudId=ocloudid).first()
+        return self.session.query(ocloud.Ocloud).filter_by(
+            oCloudId=ocloudid).first()
 
     def _update(self, ocloud: ocloud.Ocloud):
         self.session.add(ocloud)
@@ -70,8 +72,10 @@ class OcloudSqlAlchemyRepository(OcloudRepository):
     #     if dmslist:
     #         self._update_dms_list(dmslist)
     #     if updatefields:
-    #         self.session.query(ocloud.Ocloud).filter_by(oCloudId=ocloudid).update(updatefields)
+    #         self.session.query(ocloud.Ocloud).filter_by(
+    # oCloudId=ocloudid).update(updatefields)
 
     # def _update_dms_list(self, dms_list: list):
     #     for dms in dms_list or []:
-    #         self.session.query(ocloud.DeploymentManager).filter_by(deploymentManagerId=dms.deploymentManagerId).update(dms)
+    #         self.session.query(ocloud.DeploymentManager).filter_by(
+    # deploymentManagerId=dms.deploymentManagerId).update(dms)
similarity index 91%
rename from src/o2ims/adapter/orm.py
rename to o2ims/adapter/orm.py
index 6cf9847..2028538 100644 (file)
@@ -18,15 +18,15 @@ from sqlalchemy import (
     Table,\r
     MetaData,\r
     Column,\r
-    Integer,\r
+    Integer,\r
     String,\r
-    Date,\r
+    Date,\r
     ForeignKey,\r
-    event,\r
+    event,\r
 )\r
 \r
 from sqlalchemy.orm import mapper, relationship\r
-from sqlalchemy.sql.expression import true\r
+from sqlalchemy.sql.expression import true\r
 \r
 from o2ims.domain import ocloud as ocloudModel\r
 \r
@@ -86,8 +86,8 @@ def start_o2ims_mappers():
     dm_mapper = mapper(ocloudModel.DeploymentManager, deploymentmanager)\r
     resourcepool_mapper = mapper(ocloudModel.ResourcePool, resourcepool)\r
     resourcetype_mapper = mapper(ocloudModel.ResourceType, resourcetype)\r
-    resource_mapper = mapper(ocloudModel.Resource, resource)\r
-    ocloud_mapper = mapper(\r
+    resource_mapper = mapper(ocloudModel.Resource, resource)\r
+    mapper(\r
         ocloudModel.Ocloud,\r
         ocloud,\r
         properties={\r
similarity index 91%
rename from src/o2ims/bootstrap.py
rename to o2ims/bootstrap.py
index 00cdc4a..524f325 100644 (file)
@@ -15,7 +15,8 @@
 import inspect
 from typing import Callable
 from o2ims.adapter import orm, redis_eventpublisher
-from o2ims.adapter.notifications import AbstractNotifications, SmoO2Notifications
+from o2ims.adapter.notifications import AbstractNotifications,\
+    SmoO2Notifications
 
 from o2ims.service import handlers, messagebus, unit_of_work
 
@@ -33,7 +34,8 @@ def bootstrap(
     if start_orm:
         orm.start_o2ims_mappers()
 
-    dependencies = {"uow": uow, "notifications": notifications, "publish": publish}
+    dependencies = {"uow": uow, "notifications": notifications,
+                    "publish": publish}
     injected_event_handlers = {
         event_type: [
             inject_dependencies(handler, dependencies)
similarity index 77%
rename from src/o2ims/config.py
rename to o2ims/config.py
index 798ea79..f42ec72 100644 (file)
@@ -14,6 +14,7 @@
 
 import os
 
+
 def get_postgres_uri():
     host = os.environ.get("DB_HOST", "localhost")
     port = 54321 if host == "localhost" else 5432
@@ -27,9 +28,11 @@ def get_api_url():
     port = 5005 if host == "localhost" else 80
     return f"http://{host}:{port}"
 
+
 def get_o2ims_api_base():
     return '/o2ims_infrastructureInventory/v1'
 
+
 def get_redis_host_and_port():
     host = os.environ.get("REDIS_HOST", "localhost")
     port = 63791 if host == "localhost" else 6379
@@ -37,5 +40,14 @@ def get_redis_host_and_port():
 
 
 def get_smo_o2endpoint():
-    smo_o2endpoint = os.environ.get("SMO_O2_ENDPOINT", "http://localhost/smo_sim")
+    smo_o2endpoint = os.environ.get(
+        "SMO_O2_ENDPOINT", "http://localhost/smo_sim")
     return smo_o2endpoint
+
+
+def get_stx_access_info():
+    authurl = os.environ.get("STX_AUTH_URL", "http://192.168.204.1:5000/v3")
+    username = os.environ.get("STX_USERNAME", "admin")
+    pswd = os.environ.get("STX_PASSWORD", "passwd1")
+    stx_access_info = (authurl, username, pswd)
+    return stx_access_info
similarity index 89%
rename from src/o2ims/domain/commands.py
rename to o2ims/domain/commands.py
index 9df6cff..869bf52 100644 (file)
 #  limitations under the License.
 
 # pylint: disable=too-few-public-methods
-from datetime import date
-from typing import Optional
+from datetime import date
+from typing import Optional
 from dataclasses import dataclass
 
 
 class Command:
     pass
 
+
+@dataclass
 class UpdateDms(Command):
-    ref: str
\ No newline at end of file
+    ref: str
similarity index 99%
rename from src/o2ims/domain/events.py
rename to o2ims/domain/events.py
index 755f65e..591662f 100644 (file)
 # pylint: disable=too-few-public-methods
 from dataclasses import dataclass
 
+
 class Event:
     pass
 
+
 @dataclass
 class OcloudUpdated(Event):
     oCloudId: str
similarity index 68%
rename from src/o2ims/domain/ocloud.py
rename to o2ims/domain/ocloud.py
index ddd2646..68e304e 100644 (file)
 #  limitations under the License.\r
 \r
 from __future__ import annotations\r
-from dataclasses import dataclass\r
-from datetime import date\r
-from typing import Optional, List, Set\r
+from dataclasses import dataclass\r
+from datetime import date\r
+from typing import Optional, List, Set\r
 from .resource_type import ResourceTypeEnum\r
 # from uuid import UUID\r
 \r
-class Ocloud:\r
-    def __init__(\r
-        self, ocloudid: str, name: str, imsendpoint: str,\r
-        description: str = '', version_number: int = 0) -> None:\r
-\r
-        self.oCloudId = ocloudid\r
-        self.version_number = version_number\r
-        self.name = name\r
-        self.description = description\r
-        self.infrastructureManagementServiceEndpoint = imsendpoint\r
-        self.resourcePools = []\r
-        self.deploymentManagers = []\r
-        self.resourceTypes = []\r
-        self.extensions = []\r
-        self.events = []\r
-    \r
-    def addDeploymentManager(self, deploymentManager: DeploymentManager) -> None:\r
-        deploymentManager.oCloudId = self.oCloudId\r
-        old = filter(\r
-            lambda x: x.deploymentManagerId == deploymentManager.deploymentManagerId,\r
-            self.deploymentManagers)\r
-        for o in old or []:\r
-            self.deploymentManagers.remove(o)\r
-        self.deploymentManagers.append(deploymentManager)\r
 \r
 class DeploymentManager:\r
-    def __init__(self, id: str, name: str, ocloudid: str, dmsendpoint: str) -> None:\r
+    def __init__(self, id: str, name: str, ocloudid: str,\r
+                 dmsendpoint: str) -> None:\r
         self.deploymentManagerId = id\r
         self.name = name\r
         self.oCloudId = ocloudid\r
@@ -54,7 +31,8 @@ class DeploymentManager:
 \r
 \r
 class ResourcePool:\r
-    def __init__(self, id: str, name: str, location: str, ocloudid: str) -> None:\r
+    def __init__(self, id: str, name: str, location: str,\r
+                 ocloudid: str) -> None:\r
         self.resourcePoolId = id\r
         self.name = name\r
         self.location = location\r
@@ -63,7 +41,8 @@ class ResourcePool:
 \r
 \r
 class ResourceType:\r
-    def __init__(self, typeid: str, name:str, typeEnum: ResourceTypeEnum, ocloudid: str) -> None:\r
+    def __init__(self, typeid: str, name: str, typeEnum: ResourceTypeEnum,\r
+                 ocloudid: str) -> None:\r
         self.resourceTypeId = typeid\r
         self.resourceTypeEnum = typeEnum.value\r
         self.name = name\r
@@ -72,12 +51,40 @@ class ResourceType:
 \r
 \r
 class Resource:\r
-    def __init__(self, resourceId:str, resourceTypeId: str, resourcePoolId: str) -> None:\r
+    def __init__(self, resourceId: str, resourceTypeId: str,\r
+                 resourcePoolId: str) -> None:\r
         self.resourceId = resourceId\r
-        self.oCloudId = None # tbd\r
+        self.oCloudId = None  # tbd\r
         self.resourceTypeId = resourceTypeId\r
         self.resourcePoolId = resourcePoolId\r
         self.parentId = None\r
         self.elements = []\r
         self.extensions = []\r
 \r
+\r
+class Ocloud:\r
+    def __init__(self, ocloudid: str, name: str, imsendpoint: str,\r
+                 description: str = '', version_number: int = 0) -> None:\r
+\r
+        self.oCloudId = ocloudid\r
+        self.version_number = version_number\r
+        self.name = name\r
+        self.description = description\r
+        self.infrastructureManagementServiceEndpoint = imsendpoint\r
+        self.resourcePools = []\r
+        self.deploymentManagers = []\r
+        self.resourceTypes = []\r
+        self.extensions = []\r
+        self.events = []\r
+\r
+    def addDeploymentManager(self,\r
+                             deploymentManager: DeploymentManager):\r
+\r
+        deploymentManager.oCloudId = self.oCloudId\r
+        old = filter(\r
+            lambda x: x.deploymentManagerId ==\r
+            deploymentManager.deploymentManagerId,\r
+            self.deploymentManagers)\r
+        for o in old or []:\r
+            self.deploymentManagers.remove(o)\r
+        self.deploymentManagers.append(deploymentManager)\r
similarity index 93%
rename from src/o2ims/domain/resource_type.py
rename to o2ims/domain/resource_type.py
index faff8ea..72f0db0 100644 (file)
@@ -1,5 +1,6 @@
 from enum import Enum\r
 \r
+\r
 class ResourceTypeEnum(Enum):\r
     PSERVER = 1\r
     PSERVER_CPU = 2\r
diff --git a/o2ims/domain/stx_object.py b/o2ims/domain/stx_object.py
new file mode 100644 (file)
index 0000000..a3adaf2
--- /dev/null
@@ -0,0 +1,28 @@
+# Copyright (C) 2021 Wind River Systems, Inc.\r
+#\r
+#  Licensed under the Apache License, Version 2.0 (the "License");\r
+#  you may not use this file except in compliance with the License.\r
+#  You may obtain a copy of the License at\r
+#\r
+#      http://www.apache.org/licenses/LICENSE-2.0\r
+#\r
+#  Unless required by applicable law or agreed to in writing, software\r
+#  distributed under the License is distributed on an "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+#  See the License for the specific language governing permissions and\r
+#  limitations under the License.\r
+\r
+# from dataclasses import dataclass\r
+import datetime\r
+\r
+\r
+class StxGenericModel:\r
+    def __init__(self, id: str, name: str,\r
+                 lastupdate: datetime, content: str) -> None:\r
+        self.id = id\r
+        self.name = name\r
+        self.lastupdate = lastupdate\r
+        self.content = content\r
+\r
+    def isChanged(self, updatetime: datetime) -> bool:\r
+        return True if self.lastupdate > updatetime else False\r
similarity index 81%
rename from src/o2ims/entrypoints/flask_application.py
rename to o2ims/entrypoints/flask_application.py
index 933301c..8965a30 100644 (file)
 #  See the License for the specific language governing permissions and\r
 #  limitations under the License.\r
 \r
-from datetime import datetime\r
-from flask import Flask, jsonify, request\r
-from o2ims.domain import commands\r
-from o2ims.service.handlers import InvalidResourceType\r
+# from datetime import datetime\r
+from flask import Flask, jsonify\r
+# request\r
+# from o2ims.domain import commands\r
+# from o2ims.service.handlers import InvalidResourceType\r
 from o2ims import bootstrap, config\r
 from o2ims.views import ocloud_view\r
 \r
@@ -23,6 +24,7 @@ app = Flask(__name__)
 bus = bootstrap.bootstrap()\r
 apibase = config.get_o2ims_api_base()\r
 \r
+\r
 @app.route(apibase, methods=["GET"])\r
 def oclouds():\r
     result = ocloud_view.oclouds(bus.uow)\r
diff --git a/o2ims/service/client/__init__.py b/o2ims/service/client/__init__.py
new file mode 100644 (file)
index 0000000..b514342
--- /dev/null
@@ -0,0 +1,13 @@
+# Copyright (C) 2021 Wind River Systems, Inc.\r
+#\r
+#  Licensed under the Apache License, Version 2.0 (the "License");\r
+#  you may not use this file except in compliance with the License.\r
+#  You may obtain a copy of the License at\r
+#\r
+#      http://www.apache.org/licenses/LICENSE-2.0\r
+#\r
+#  Unless required by applicable law or agreed to in writing, software\r
+#  distributed under the License is distributed on an "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+#  See the License for the specific language governing permissions and\r
+#  limitations under the License.\r
diff --git a/o2ims/service/client/base_client.py b/o2ims/service/client/base_client.py
new file mode 100644 (file)
index 0000000..6057ab3
--- /dev/null
@@ -0,0 +1,37 @@
+# Copyright (C) 2021 Wind River Systems, Inc.\r
+#\r
+#  Licensed under the Apache License, Version 2.0 (the "License");\r
+#  you may not use this file except in compliance with the License.\r
+#  You may obtain a copy of the License at\r
+#\r
+#      http://www.apache.org/licenses/LICENSE-2.0\r
+#\r
+#  Unless required by applicable law or agreed to in writing, software\r
+#  distributed under the License is distributed on an "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+#  See the License for the specific language governing permissions and\r
+#  limitations under the License.\r
+\r
+import abc\r
+# from typing import Optional, List, Set\r
+from typing import List\r
+from o2ims.domain import stx_object as ocloudModel\r
+\r
+\r
+class BaseClient(abc.ABC):\r
+    def __init__(self):\r
+        pass\r
+\r
+    def list(self) -> List[ocloudModel.StxGenericModel]:\r
+        return self._list()\r
+\r
+    def get(self, id) -> ocloudModel.StxGenericModel:\r
+        return self._get(id)\r
+\r
+    @abc.abstractmethod\r
+    def _get(self, id) -> ocloudModel.StxGenericModel:\r
+        raise NotImplementedError\r
+\r
+    @abc.abstractmethod\r
+    def _list(self):\r
+        raise NotImplementedError\r
similarity index 81%
rename from src/o2ims/service/handlers.py
rename to o2ims/service/handlers.py
index c75dc07..c80ea6f 100644 (file)
 
 # pylint: disable=unused-argument
 from __future__ import annotations
-from dataclasses import asdict
-from typing import List, Dict, Callable, Type, TYPE_CHECKING
-from o2ims.domain import commands, events, ocloud
+# from dataclasses import asdict
+from typing import List, Dict, Callable, Type
+# TYPE_CHECKING
+from o2ims.domain import commands, events
+# ocloud
+
+# if TYPE_CHECKING:
+#     from . import unit_of_work
 
-if TYPE_CHECKING:
-    from . import unit_of_work
 
 class InvalidResourceType(Exception):
     pass
@@ -28,5 +31,6 @@ class InvalidResourceType(Exception):
 EVENT_HANDLERS = {
 }  # type: Dict[Type[events.Event], List[Callable]]
 
+
 COMMAND_HANDLERS = {
 }  # type: Dict[Type[commands.Command], Callable]
similarity index 98%
rename from src/o2ims/service/messagebus.py
rename to o2ims/service/messagebus.py
index bff45ab..0758529 100644 (file)
@@ -51,7 +51,8 @@ class MessageBus:
     def handle_event(self, event: events.Event):
         for handler in self.event_handlers[type(event)]:
             try:
-                logger.debug("handling event %s with handler %s", event, handler)
+                logger.debug("handling event %s with handler %s",
+                             event, handler)
                 handler(event)
                 self.queue.extend(self.uow.collect_new_events())
             except Exception:
similarity index 94%
rename from src/o2ims/service/unit_of_work.py
rename to o2ims/service/unit_of_work.py
index 4714729..40e0f76 100644 (file)
@@ -19,7 +19,6 @@ from sqlalchemy import create_engine
 from sqlalchemy.orm import sessionmaker
 from sqlalchemy.orm.session import Session
 
-
 from o2ims import config
 from o2ims.adapter import ocloud_repository
 
@@ -27,7 +26,7 @@ from o2ims.adapter import ocloud_repository
 class AbstractUnitOfWork(abc.ABC):
     oclouds: ocloud_repository.OcloudRepository
 
-    def __enter__(self) -> AbstractUnitOfWork:
+    def __enter__(self):
         return self
 
     def __exit__(self, *args):
@@ -64,7 +63,8 @@ class SqlAlchemyUnitOfWork(AbstractUnitOfWork):
 
     def __enter__(self):
         self.session = self.session_factory()  # type: Session
-        self.oclouds = ocloud_repository.OcloudSqlAlchemyRepository(self.session)
+        self.oclouds = ocloud_repository\
+            .OcloudSqlAlchemyRepository(self.session)
         return super().__enter__()
 
     def __exit__(self, *args):
index 251bb2b..e22107b 100644 (file)
@@ -1,8 +1,13 @@
+flake8\r
 pylint\r
 mypy\r
 requests\r
+tox\r
 \r
 pytest\r
 pytest-icdiff\r
 \r
 tenacity\r
+\r
+# -e git+https://opendev.org/starlingx/distcloud-client.git@master#egg=distributedcloud-client&subdirectory=distributedcloud-client\r
+# -e git+https://opendev.org/starlingx/config.git@master#egg=cgtsclient&subdirectory=sysinv/cgts-client/cgts-client\r
index 985e50e..acbfe10 100644 (file)
@@ -2,3 +2,8 @@ flask
 sqlalchemy\r
 redis\r
 psycopg2-binary\r
+\r
+Cython>=3.0a1\r
+\r
+# -e git+https://opendev.org/starlingx/distcloud-client.git@master#egg=distributedcloud-client&subdirectory=distributedcloud-client\r
+# -e git+https://opendev.org/starlingx/config.git@master#egg=cgtsclient&subdirectory=sysinv/cgts-client/cgts-client#\r
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..553dd19
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,17 @@
+from setuptools import setup\r
+from setuptools import find_packages\r
+\r
+setup(\r
+    name="o2imsdms",\r
+    version="1.0",\r
+    packages=find_packages(),\r
+    license="LICENSE",\r
+    description="Represent O2 IMS and O2 DMS",\r
+    install_requires=[\r
+        'httplib2',\r
+        # 'distributedcloud-client',\r
+        # 'cgtsclient',\r
+        'babel',  # Required by distributedcloud-client\r
+        'PrettyTable<0.8,>=0.7.2',  # Required by distributedcloud-client\r
+    ]\r
+)\r
diff --git a/src/o2dms/setup.py b/src/o2dms/setup.py
deleted file mode 100644 (file)
index feafb7a..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-from setuptools import setup\r
-\r
-setup(\r
-    name="o2common",\r
-    version="1.0",\r
-    packages=["o2common"],\r
-)\r
-\r
-setup(\r
-    name="o2ims",\r
-    version="1.0",\r
-    packages=["o2ims"],\r
-)\r
-\r
-setup(\r
-    name="o2dms",\r
-    version="1.0",\r
-    packages=["o2dms"],\r
-)\r
diff --git a/src/setup.py b/src/setup.py
deleted file mode 100644 (file)
index 7558373..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-from setuptools import setup\r
-\r
-setup(\r
-    name="o2imsdms",\r
-    version="1.0",\r
-    packages=["o2ims", "o2dms", "o2common"],\r
-)\r
diff --git a/tests/e2e/__init__.py b/tests/e2e/__init__.py
new file mode 100644 (file)
index 0000000..b514342
--- /dev/null
@@ -0,0 +1,13 @@
+# Copyright (C) 2021 Wind River Systems, Inc.\r
+#\r
+#  Licensed under the Apache License, Version 2.0 (the "License");\r
+#  you may not use this file except in compliance with the License.\r
+#  You may obtain a copy of the License at\r
+#\r
+#      http://www.apache.org/licenses/LICENSE-2.0\r
+#\r
+#  Unless required by applicable law or agreed to in writing, software\r
+#  distributed under the License is distributed on an "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+#  See the License for the specific language governing permissions and\r
+#  limitations under the License.\r
diff --git a/tox.ini b/tox.ini
new file mode 100644 (file)
index 0000000..3480ef8
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,35 @@
+# content of: tox.ini , put in same dir as setup.py\r
+[tox]\r
+envlist=flake8,code\r
+\r
+minversion = 1.6\r
+skipsdist = True\r
+\r
+[testenv]\r
+basepython =\r
+    code: python3.8\r
+    flake8: python3.8\r
+setenv =\r
+        VIRTUAL_ENV={envdir}\r
+\r
+# NOTE: relative paths were used due to '-w' flag for nosetests util\r
+\r
+usedevelop = True\r
+install_command = pip install -U {opts} {packages}\r
+deps = -r{toxinidir}/requirements.txt\r
+       -r{toxinidir}/requirements-test.txt\r
+whitelist_externals = bash, flake8, pytest\r
+\r
+[testenv:flake8]\r
+commands =\r
+    flake8 o2ims\r
+    flake8 o2dms\r
+    flake8 o2common\r
+\r
+[testenv:code]\r
+commands =\r
+    pytest tests\r
+\r
+[testenv:nosetests]\r
+commands =\r
+    pytest tests\r