Add Acumos xApp Adapter 55/1155/1
authorManoop Talasila <talasila@research.att.com>
Tue, 15 Oct 2019 16:22:42 +0000 (12:22 -0400)
committerManoop Talasila <talasila@research.att.com>
Tue, 15 Oct 2019 16:43:48 +0000 (12:43 -0400)
Issue-Id: RICAPP-4
Signed-off-by: Manoop Talasila <talasila@research.att.com>
Change-Id: I369577ccc1c392af769089712c4a882f876c4489

.gitignore [new file with mode: 0644]
.gitreview [new file with mode: 0644]
AcumosXappAdapter/config.json [new file with mode: 0644]
AcumosXappAdapter/iris_sklearn.py [new file with mode: 0644]
AcumosXappAdapter/rmracumosadapter.py [new file with mode: 0644]
AcumosXappAdapter/testdata.csv [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..21276bf
--- /dev/null
@@ -0,0 +1,2 @@
+# OSx cruft
+**/.DS_Store
diff --git a/.gitreview b/.gitreview
new file mode 100644 (file)
index 0000000..07d953f
--- /dev/null
@@ -0,0 +1,4 @@
+[gerrit]
+host=gerrit.o-ran-sc.org
+port=29418
+project=ric-app/ml.git
diff --git a/AcumosXappAdapter/config.json b/AcumosXappAdapter/config.json
new file mode 100644 (file)
index 0000000..e425e82
--- /dev/null
@@ -0,0 +1,14 @@
+{
+    "version": "0.1.1",
+    "microserviceRootURL": "http://acumos:3330/",
+    "methodRoot": "model/methods/",
+    "artifactRoot": "model/artifacts/",
+    "methods": {
+       "1": {
+           "service": "classify",
+           "content-type": "application/json",
+           "return-type": "application/json",
+           "return-rte": "2"
+       }
+    }
+}
diff --git a/AcumosXappAdapter/iris_sklearn.py b/AcumosXappAdapter/iris_sklearn.py
new file mode 100644 (file)
index 0000000..7fd70fb
--- /dev/null
@@ -0,0 +1,56 @@
+# ===============LICENSE_START=======================================================
+# Acumos Apache-2.0
+# ===================================================================================
+# Copyright (C) 2017-2018 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+# ===================================================================================
+# This Acumos software file is distributed by AT&T and Tech Mahindra
+# 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
+#
+# This file 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.
+# ===============LICENSE_END=========================================================
+
+from acumos.session import AcumosSession
+from acumos.modeling import Model, List, create_dataframe
+
+import numpy as np
+import pandas as pd
+from sklearn.datasets import load_iris
+from sklearn.ensemble import RandomForestClassifier
+
+iris = load_iris()
+X = iris.data
+y = iris.target
+
+clf = RandomForestClassifier(random_state=0)
+clf.fit(X, y)
+
+# here, an appropriate NamedTuple type is inferred from a pandas DataFrame
+X_df = pd.DataFrame(X, columns=['sepal_length', 'sepal_width', 'petal_length', 'petal_width'])
+IrisDataFrame = create_dataframe('IrisDataFrame', X_df)
+
+# ==================================================================================
+# # or equivalently:
+#
+# IrisDataFrame = create_namedtuple('IrisDataFrame', [('sepal_length', List[float]),
+#                                                     ('sepal_width', List[float]),
+#                                                     ('petal_length', List[float]),
+#                                                     ('petal_width', List[float])])
+# ==================================================================================
+
+def classify_iris(df: IrisDataFrame) -> List[int]:
+    '''Returns an array of iris classifications'''
+    X = np.column_stack(df)
+    return clf.predict(X)
+
+model = Model(classify=classify_iris)
+
+session = AcumosSession()
+
+session.dump(model,'iris_sklearn','/Users/guy/Desktop')
diff --git a/AcumosXappAdapter/rmracumosadapter.py b/AcumosXappAdapter/rmracumosadapter.py
new file mode 100644 (file)
index 0000000..16ce726
--- /dev/null
@@ -0,0 +1,127 @@
+# ========================LICENSE_START=================================
+#   O-RAN-SC
+#   %%
+#   Copyright (c) 2019 AT&T Intellectual Property.
+#   %%
+#   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.
+# ========================LICENSE_END===================================
+
+# Adapter from RMR to standard Acumos model microservices. Must be deployed in the same pod as the Acumos model.
+# Translates RMR protocol messages into calls into Acumos RPC calls.
+
+
+from rmr import rmr
+import time
+import sys
+import signal
+import json
+import requests
+
+verbose = True
+requireartifacts = True
+
+confdir = '/conf/'
+conffilename = 'config.json'
+protobuffilename = 'model.proto'
+metadatafilename = 'metadata.json'
+
+configfilename = confdir + conffilename
+
+if verbose:
+    print("Reading config file")
+
+# Fetch and parse config file which must be mounted as a volume during deployment
+try:
+    with open(configfilename) as f:
+        conf = json.load(f)
+except:
+    print('Cannot read/parse config file at', configfilename, '; aborting')
+    exit(1)
+
+methodurl = conf['microserviceRootURL'] + conf['methodRoot']
+artifacturl = conf["microserviceRootURL"] + conf['artifactRoot']
+
+if verbose:
+    print ('\nRetrieving artifacts from Acumos model microservice\n')
+
+# See if we can retrieve protobuf and metadata artifacts from running model. Not all models may provide these, but we
+# should have a retry mechanism added for robustness
+try:
+    r = requests.get(artifacturl + 'protobuf')
+    protobuf = r.content
+    with open(confdir + protobuffilename, 'wb') as f:
+        f.write(protobuf)
+    if verbose:
+        print('Protbuf:')
+        print(protobuf.decode('ascii'))
+    r = requests.get(artifacturl + 'metadata')
+    metadata = r.content
+    with open(confdir + metadatafilename, 'wb') as f:
+        f.write(metadata)
+    if verbose:
+        print('\nMetadata:')
+        print(metadata.decode('ascii'))
+except:
+    if requireartifacts:
+        print('Problem with retrieving/saving model protobuf and/or metadata; aborting.')
+
+method1 = conf['methods']['1']
+method1url = methodurl + method1['service']
+method1headers = {'content-type': method1['content-type'], 'accept': method1['return-type']}
+
+if verbose:
+    print('\nInitializing RMR\n')
+
+if verbose:
+    print('\Awaiting connections')
+
+
+# NNG cleanup on signal
+def signal_handler(sig, frame):
+    if verbose:
+        print('SIGINT received! Cleaning up rmr')
+    rmr.rmr_close(mrc)
+    print("Exiting")
+    sys.exit(0)
+
+
+# Initialize RMR
+mrc = rmr.rmr_init("4560".encode('utf-8'), rmr.RMR_MAX_RCV_BYTES, 0x00)
+while rmr.rmr_ready(mrc) == 0:
+    time.sleep(1)
+    if verbose:
+        print("Not yet ready")
+rmr.rmr_set_stimeout(mrc, 2)
+
+
+# Capture ctrl-c
+signal.signal(signal.SIGINT, signal_handler)
+
+
+sbuf = None
+while True:
+    if verbose:
+        print("Waiting for a message; will time out after 2000ms")
+    sbuf = rmr.rmr_torcv_msg(mrc, sbuf, 2000)
+    summary = rmr.message_summary(sbuf)
+    if verbose and summary['message state'] == 12:
+        print("Nothing received.")
+    else:
+        if verbose:
+            print("Message received: {}".format(summary))
+        payload = sbuf['payload']
+        # Call Acumos microservice
+        r = requests.post(method1url, headers=method1headers, body=payload)
+        val = r.content
+        rmr.set_payload_and_length(val, sbuf)
+        sbuf = rmr.rmr_rts_msg(mrc, sbuf)
diff --git a/AcumosXappAdapter/testdata.csv b/AcumosXappAdapter/testdata.csv
new file mode 100644 (file)
index 0000000..fd0a1e6
--- /dev/null
@@ -0,0 +1,151 @@
+5.1,3.5,1.4,0.2
+4.9,3.0,1.4,0.2
+4.7,3.2,1.3,0.2
+4.6,3.1,1.5,0.2
+5.0,3.6,1.4,0.2
+5.4,3.9,1.7,0.4
+4.6,3.4,1.4,0.3
+5.0,3.4,1.5,0.2
+4.4,2.9,1.4,0.2
+4.9,3.1,1.5,0.1
+5.4,3.7,1.5,0.2
+4.8,3.4,1.6,0.2
+4.8,3.0,1.4,0.1
+4.3,3.0,1.1,0.1
+5.8,4.0,1.2,0.2
+5.7,4.4,1.5,0.4
+5.4,3.9,1.3,0.4
+5.1,3.5,1.4,0.3
+5.7,3.8,1.7,0.3
+5.1,3.8,1.5,0.3
+5.4,3.4,1.7,0.2
+5.1,3.7,1.5,0.4
+4.6,3.6,1.0,0.2
+5.1,3.3,1.7,0.5
+4.8,3.4,1.9,0.2
+5.0,3.0,1.6,0.2
+5.0,3.4,1.6,0.4
+5.2,3.5,1.5,0.2
+5.2,3.4,1.4,0.2
+4.7,3.2,1.6,0.2
+4.8,3.1,1.6,0.2
+5.4,3.4,1.5,0.4
+5.2,4.1,1.5,0.1
+5.5,4.2,1.4,0.2
+4.9,3.1,1.5,0.2
+5.0,3.2,1.2,0.2
+5.5,3.5,1.3,0.2
+4.9,3.6,1.4,0.1
+4.4,3.0,1.3,0.2
+5.1,3.4,1.5,0.2
+5.0,3.5,1.3,0.3
+4.5,2.3,1.3,0.3
+4.4,3.2,1.3,0.2
+5.0,3.5,1.6,0.6
+5.1,3.8,1.9,0.4
+4.8,3.0,1.4,0.3
+5.1,3.8,1.6,0.2
+4.6,3.2,1.4,0.2
+5.3,3.7,1.5,0.2
+5.0,3.3,1.4,0.2
+7.0,3.2,4.7,1.4
+6.4,3.2,4.5,1.5
+6.9,3.1,4.9,1.5
+5.5,2.3,4.0,1.3
+6.5,2.8,4.6,1.5
+5.7,2.8,4.5,1.3
+6.3,3.3,4.7,1.6
+4.9,2.4,3.3,1.0
+6.6,2.9,4.6,1.3
+5.2,2.7,3.9,1.4
+5.0,2.0,3.5,1.0
+5.9,3.0,4.2,1.5
+6.0,2.2,4.0,1.0
+6.1,2.9,4.7,1.4
+5.6,2.9,3.6,1.3
+6.7,3.1,4.4,1.4
+5.6,3.0,4.5,1.5
+5.8,2.7,4.1,1.0
+6.2,2.2,4.5,1.5
+5.6,2.5,3.9,1.1
+5.9,3.2,4.8,1.8
+6.1,2.8,4.0,1.3
+6.3,2.5,4.9,1.5
+6.1,2.8,4.7,1.2
+6.4,2.9,4.3,1.3
+6.6,3.0,4.4,1.4
+6.8,2.8,4.8,1.4
+6.7,3.0,5.0,1.7
+6.0,2.9,4.5,1.5
+5.7,2.6,3.5,1.0
+5.5,2.4,3.8,1.1
+5.5,2.4,3.7,1.0
+5.8,2.7,3.9,1.2
+6.0,2.7,5.1,1.6
+5.4,3.0,4.5,1.5
+6.0,3.4,4.5,1.6
+6.7,3.1,4.7,1.5
+6.3,2.3,4.4,1.3
+5.6,3.0,4.1,1.3
+5.5,2.5,4.0,1.3
+5.5,2.6,4.4,1.2
+6.1,3.0,4.6,1.4
+5.8,2.6,4.0,1.2
+5.0,2.3,3.3,1.0
+5.6,2.7,4.2,1.3
+5.7,3.0,4.2,1.2
+5.7,2.9,4.2,1.3
+6.2,2.9,4.3,1.3
+5.1,2.5,3.0,1.1
+5.7,2.8,4.1,1.3
+6.3,3.3,6.0,2.5
+5.8,2.7,5.1,1.9
+7.1,3.0,5.9,2.1
+6.3,2.9,5.6,1.8
+6.5,3.0,5.8,2.2
+7.6,3.0,6.6,2.1
+4.9,2.5,4.5,1.7
+7.3,2.9,6.3,1.8
+6.7,2.5,5.8,1.8
+7.2,3.6,6.1,2.5
+6.5,3.2,5.1,2.0
+6.4,2.7,5.3,1.9
+6.8,3.0,5.5,2.1
+5.7,2.5,5.0,2.0
+5.8,2.8,5.1,2.4
+6.4,3.2,5.3,2.3
+6.5,3.0,5.5,1.8
+7.7,3.8,6.7,2.2
+7.7,2.6,6.9,2.3
+6.0,2.2,5.0,1.5
+6.9,3.2,5.7,2.3
+5.6,2.8,4.9,2.0
+7.7,2.8,6.7,2.0
+6.3,2.7,4.9,1.8
+6.7,3.3,5.7,2.1
+7.2,3.2,6.0,1.8
+6.2,2.8,4.8,1.8
+6.1,3.0,4.9,1.8
+6.4,2.8,5.6,2.1
+7.2,3.0,5.8,1.6
+7.4,2.8,6.1,1.9
+7.9,3.8,6.4,2.0
+6.4,2.8,5.6,2.2
+6.3,2.8,5.1,1.5
+6.1,2.6,5.6,1.4
+7.7,3.0,6.1,2.3
+6.3,3.4,5.6,2.4
+6.4,3.1,5.5,1.8
+6.0,3.0,4.8,1.8
+6.9,3.1,5.4,2.1
+6.7,3.1,5.6,2.4
+6.9,3.1,5.1,2.3
+5.8,2.7,5.1,1.9
+6.8,3.2,5.9,2.3
+6.7,3.3,5.7,2.5
+6.7,3.0,5.2,2.3
+6.3,2.5,5.0,1.9
+6.5,3.0,5.2,2.0
+6.2,3.4,5.4,2.3
+5.9,3.0,5.1,1.8
+