This is an automated email from the ASF dual-hosted git repository.

spolavarapu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/master by this push:
     new a07bde1  RANGER-2927: Python client libraries for Publicv2APIs
a07bde1 is described below

commit a07bde1024518126b4881b0e71f4fd30b8405edd
Author: Abhishek Kumar <[email protected]>
AuthorDate: Fri Aug 14 19:26:42 2020 -0400

    RANGER-2927: Python client libraries for Publicv2APIs
    
    Signed-off-by: Sailaja Polavarapu <[email protected]>
---
 .gitignore                                         |   1 +
 intg/.gitignore                                    |   3 +-
 intg/pom.xml                                       | 107 ++++-
 intg/scripts/build.sh                              |  23 +
 intg/scripts/setup.py                              |  30 ++
 .../main/java/org/apache/ranger/RangerClient.java  |   8 +-
 intg/src/main/python/__init__.py                   |  17 +
 intg/src/main/python/ranger/client/__init__.py     |  17 +
 .../src/main/python/ranger/client/ranger_client.py | 496 +++++++++++++++++++++
 intg/src/main/python/ranger/model/__init__.py      |  17 +
 .../ranger/model/grant_revoke_role_request.py      |  39 ++
 intg/src/main/python/ranger/model/ranger_base.py   |  34 ++
 intg/src/main/python/ranger/model/ranger_policy.py | 127 ++++++
 intg/src/main/python/ranger/model/ranger_role.py   |  48 ++
 .../python/ranger/model/ranger_security_zone.py    |  48 ++
 .../src/main/python/ranger/model/ranger_service.py |  43 ++
 .../main/python/ranger/model/ranger_service_def.py | 136 ++++++
 intg/src/main/resources/logging.conf               |  40 ++
 intg/src/test/python/test_ranger_client.py         | 109 +++++
 pom.xml                                            |   5 +-
 .../distro/src/main/assembly/sample-client.xml     |  21 +-
 ranger-examples/sample-client/.gitignore           |   3 +-
 .../sample-client/conf/config.properties           |  21 +
 ranger-examples/sample-client/pom.xml              |  38 ++
 .../sample-client/scripts/run-pyclient.sh          |  50 +++
 ranger-examples/sample-client/scripts/setup.py     |  30 ++
 .../sample-client/src/main/python/sample_client.py | 110 +++++
 27 files changed, 1611 insertions(+), 10 deletions(-)

diff --git a/.gitignore b/.gitignore
index c2def9d..e351815 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@
 .classpath
 .project
 /target/
+/venv/
 winpkg/target
 .DS_Store
 .idea
diff --git a/intg/.gitignore b/intg/.gitignore
index a6f89c2..897593c 100644
--- a/intg/.gitignore
+++ b/intg/.gitignore
@@ -1 +1,2 @@
-/target/
\ No newline at end of file
+/target/
+/venv/
\ No newline at end of file
diff --git a/intg/pom.xml b/intg/pom.xml
index c8eb330..83b87d8 100644
--- a/intg/pom.xml
+++ b/intg/pom.xml
@@ -19,7 +19,6 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0";
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
-    <packaging>jar</packaging>
     <parent>
         <artifactId>ranger</artifactId>
         <groupId>org.apache.ranger</groupId>
@@ -28,6 +27,112 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>ranger-intg</artifactId>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-pmd-plugin</artifactId>
+                <version>${maven.pmd.plugin.version}</version>
+                <executions>
+                    <execution>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <rulesets>
+                        
<ruleset>${project.parent.basedir}/dev-support/ranger-pmd-ruleset.xml</ruleset>
+                    </rulesets>
+                    <sourceEncoding>UTF-8</sourceEncoding>
+                    <failOnViolation>true</failOnViolation>
+                    <linkXRef>false</linkXRef>
+                    <includeTests>true</includeTests>
+                    <verbose>true</verbose>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-clean-plugin</artifactId>
+                <version>2.6.1</version>
+                <configuration>
+                    <filesets>
+                        <fileset>
+                            <directory>venv</directory>
+                        </fileset>
+                    </filesets>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <version>${maven.exec.plugin.version}</version>
+                <executions>
+                    <execution>
+                        <configuration>
+                            <skip>${skipTests}</skip>
+                            <executable>python3</executable>
+                            <arguments>
+                                <argument>-m</argument>
+                                <argument>venv</argument>
+                                <argument>venv</argument>
+                            </arguments>
+                        </configuration>
+                        <id>create-venv</id>
+                        <phase>validate</phase>
+                        <goals>
+                            <goal>exec</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <phase>test</phase>
+                        <goals>
+                            <goal>exec</goal>
+                        </goals>
+                        <configuration>
+                            <skip>${skipTests}</skip>
+                            <executable>bash</executable>
+                            <commandlineArgs>scripts/build.sh</commandlineArgs>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-resources-plugin</artifactId>
+                <version>2.7</version>
+                <executions>
+                    <execution>
+                        <id>copy-resources</id>
+                        <phase>initialize</phase>
+                        <goals>
+                            <goal>copy-resources</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>venv/pylib</outputDirectory>
+                            <resources>
+                                <resource>
+                                    <directory>src/main/python</directory>
+                                    <includes>
+                                        <include>**/*.py</include>
+                                    </includes>
+                                </resource>
+                                <resource>
+                                    <directory>src/test/python</directory>
+                                </resource>
+                                <resource>
+                                    <directory>src/main/resources</directory>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
     <dependencies>
         <dependency>
             <groupId>org.apache.ranger</groupId>
diff --git a/intg/scripts/build.sh b/intg/scripts/build.sh
new file mode 100644
index 0000000..a875da1
--- /dev/null
+++ b/intg/scripts/build.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+cd venv || exit
+source bin/activate
+INSTALL="python3 ./../scripts/setup.py install"
+$INSTALL >> build.log
+TEST="python3 build/lib/pylib/test_ranger_client.py"
+$TEST
diff --git a/intg/scripts/setup.py b/intg/scripts/setup.py
new file mode 100644
index 0000000..9ce9773
--- /dev/null
+++ b/intg/scripts/setup.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+from setuptools import setup, find_packages
+
+# External dependencies
+requirements = ['requests>=2.24']
+
+setup(
+    name='pyclient',
+    packages=['pylib', 'pylib/ranger/client', 'pylib/ranger/model'],
+    data_files=[('../../lib/resources', 
['./../src/main/resources/logging.conf'])],
+    install_requires=requirements,
+    zip_safe=False
+)
diff --git a/intg/src/main/java/org/apache/ranger/RangerClient.java 
b/intg/src/main/java/org/apache/ranger/RangerClient.java
index 0e2fe56..bef3857 100644
--- a/intg/src/main/java/org/apache/ranger/RangerClient.java
+++ b/intg/src/main/java/org/apache/ranger/RangerClient.java
@@ -468,7 +468,7 @@ public class RangerClient {
         }
 
         if (clientResponse == null) {
-            throw new RangerServiceException(api, clientResponse);
+            throw new RangerServiceException(api, null);
         } else if (clientResponse.getStatus() == 
api.getExpectedStatus().getStatusCode()) {
             if (responseType != null) {
                 ret = clientResponse.getEntity(responseType);
@@ -542,11 +542,9 @@ public class RangerClient {
             try {
                 URI uri = new URI(path);
 
-                if (uri != null) {
-                    URI normalizedUri = uri.normalize();
+                URI normalizedUri = uri.normalize();
 
-                    ret = normalizedUri.toString();
-                }
+                ret = normalizedUri.toString();
             } catch (Exception e) {
                 LOG.error("getNormalizedPath() caught exception for path={}", 
path, e);
 
diff --git a/intg/src/main/python/__init__.py b/intg/src/main/python/__init__.py
new file mode 100644
index 0000000..ed9d0b3
--- /dev/null
+++ b/intg/src/main/python/__init__.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
diff --git a/intg/src/main/python/ranger/client/__init__.py 
b/intg/src/main/python/ranger/client/__init__.py
new file mode 100644
index 0000000..ed9d0b3
--- /dev/null
+++ b/intg/src/main/python/ranger/client/__init__.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
diff --git a/intg/src/main/python/ranger/client/ranger_client.py 
b/intg/src/main/python/ranger/client/ranger_client.py
new file mode 100644
index 0000000..319261d
--- /dev/null
+++ b/intg/src/main/python/ranger/client/ranger_client.py
@@ -0,0 +1,496 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+
+import enum
+import json
+import logging
+import requests
+import logging.config
+
+from os       import path
+from http     import HTTPStatus
+from requests import Session, Response
+
+from ranger.model.ranger_role          import RangerRole
+from ranger.model.ranger_policy        import RangerPolicy
+from ranger.model.ranger_service       import RangerService
+from ranger.model.ranger_service_def   import RangerServiceDef
+from ranger.model.ranger_security_zone import RangerSecurityZone
+
+
+APPLICATION_JSON = 'application/json'
+LOG_CONFIG = path.dirname(__file__) + '/../../../resources/logging.conf'
+
+logging.config.fileConfig(LOG_CONFIG, disable_existing_loggers=False)
+requests.packages.urllib3.disable_warnings()
+
+
+class Message:
+    def __init__(self, name=None, rbKey=None, message=None, objectId=None, 
fieldName=None):
+        self.name      = name
+        self.rbKey     = rbKey
+        self.message   = message
+        self.objectId  = objectId
+        self.fieldName = fieldName
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RESTResponse:
+    def __init__(self, httpStatusCode=None, statusCode=None, msgDesc=None, 
messageList=None):
+        self.httpStatusCode = httpStatusCode
+        self.statusCode     = statusCode
+        self.msgDesc        = msgDesc
+        self.messageList    = messageList if messageList is not None else []
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class HttpMethod(enum.Enum):
+    GET    = "GET"
+    PUT    = "PUT"
+    POST   = "POST"
+    DELETE = "DELETE"
+
+
+class API:
+    def __init__(self, path, method, expected_status, 
consumes=APPLICATION_JSON, produces=APPLICATION_JSON):
+        self.log = logging.getLogger(__name__)
+        self.path            = path
+        self.method          = method
+        self.expected_status = expected_status
+        self.consumes        = consumes
+        self.produces        = produces
+
+    def call(self, session, baseUrl, params, request):
+        session.headers['Accept']       = self.consumes
+        session.headers['Content-type'] = self.produces
+
+        if self.log.isEnabledFor(logging.DEBUG):
+            self.log.debug('==> call({},{},{})'.format(self, params, request))
+            
self.log.debug('------------------------------------------------------')
+            self.log.debug('Call         : {} {}'.format(self.method, 
self.path))
+            self.log.debug('Content-type : {}'.format(self.consumes))
+            self.log.debug('Accept       : {}'.format(self.produces))
+
+            if request is not None:
+                self.log.debug("Request      : {}".format(request))
+
+        path = baseUrl + self.path
+
+        if self.method == HttpMethod.GET:
+            client_response = session.get(path)
+        elif self.method == HttpMethod.POST:
+            client_response = session.post(path, data=request.__repr__())
+        elif self.method == HttpMethod.PUT:
+            client_response = session.put(path, data=request.__repr__())
+        elif self.method == HttpMethod.DELETE:
+            client_response = session.delete(path, params=params)
+        else:
+            raise Exception('Unsupported HTTP Method {}'.format(self.method))
+
+        if client_response.status_code == self.expected_status:
+            if client_response.content:
+                if self.log.isEnabledFor(logging.DEBUG):
+                    self.log.debug('Response: {}'.format(client_response.text))
+            else:
+                return None
+        elif client_response.status_code == HTTPStatus.SERVICE_UNAVAILABLE:
+            self.log.error('Ranger Admin unavailable. HTTP Status: 
{}'.format(HTTPStatus.SERVICE_UNAVAILABLE))
+            self.log.error("client_response=: {}" + str(client_response))
+            return None
+        else:
+            raise Exception(client_response.text)
+
+        __json = json.loads(str(json.dumps(client_response.json())))
+
+        if self.log.isEnabledFor(logging.DEBUG):
+            self.log.debug('<== call({},{},{}), result = {}'.format(self, 
params, request, client_response))
+
+        return __json
+
+    def apply_url_params(self, params):
+        try:
+            return API(self.path.format(**params), self.method, 
self.expected_status, self.consumes, self.produces)
+        except (KeyError, TypeError) as e:
+            self.log.error('Arguments not formatted properly' + str(e))
+            raise e
+
+
+class RangerClient:
+    # Query Params
+    PARAM_ID                            = "id"
+    PARAM_NAME                          = "name"
+    PARAM_DAYS                          = "days"
+    PARAM_EXEC_USER                     = "execUser"
+    PARAM_POLICY_NAME                   = "policyname"
+    PARAM_SERVICE_NAME                  = "serviceName"
+    PARAM_RELOAD_SERVICE_POLICIES_CACHE = "reloadServicePoliciesCache"
+
+    # URIs
+    URI_BASE = "/service/public/v2/api"
+
+    URI_SERVICEDEF         = URI_BASE + "/servicedef"
+    URI_SERVICEDEF_BY_ID   = URI_SERVICEDEF + "/{id}"
+    URI_SERVICEDEF_BY_NAME = URI_SERVICEDEF + "/name/{name}"
+
+    URI_SERVICE             = URI_BASE + "/service"
+    URI_SERVICE_BY_ID       = URI_SERVICE + "/{id}"
+    URI_SERVICE_BY_NAME     = URI_SERVICE + "/name/{name}"
+    URI_POLICIES_IN_SERVICE = URI_SERVICE + "/{serviceName}/policy"
+
+    URI_POLICY         = URI_BASE + "/policy"
+    URI_APPLY_POLICY   = URI_POLICY + "/apply"
+    URI_POLICY_BY_ID   = URI_POLICY + "/{id}"
+    URI_POLICY_BY_NAME = URI_SERVICE + "/{serviceName}/policy/{policyname}"
+
+    URI_ROLE         = URI_BASE + "/roles"
+    URI_ROLE_NAMES   = URI_ROLE + "/names"
+    URI_ROLE_BY_ID   = URI_ROLE + "/{id}"
+    URI_ROLE_BY_NAME = URI_ROLE + "/name/{name}"
+    URI_USER_ROLES   = URI_ROLE + "/user/{name}"
+    URI_GRANT_ROLE   = URI_ROLE + "/grant/{name}"
+    URI_REVOKE_ROLE  = URI_ROLE + "/revoke/{name}"
+
+    URI_ZONE         = URI_BASE + "/zones"
+    URI_ZONE_BY_ID   = URI_ZONE + "/{id}"
+    URI_ZONE_BY_NAME = URI_ZONE + "/name/{name}"
+
+    URI_PLUGIN_INFO   = URI_BASE + "/plugins/info"
+    URI_POLICY_DELTAS = URI_BASE + "/server/policydeltas"
+
+    # APIs
+    CREATE_SERVICEDEF         = API(URI_SERVICEDEF, HttpMethod.POST, 
HTTPStatus.OK)
+    UPDATE_SERVICEDEF_BY_ID   = API(URI_SERVICEDEF_BY_ID, HttpMethod.PUT, 
HTTPStatus.OK)
+    UPDATE_SERVICEDEF_BY_NAME = API(URI_SERVICEDEF_BY_NAME, HttpMethod.PUT, 
HTTPStatus.OK)
+    DELETE_SERVICEDEF_BY_ID   = API(URI_SERVICEDEF_BY_ID, HttpMethod.DELETE, 
HTTPStatus.NO_CONTENT)
+    DELETE_SERVICEDEF_BY_NAME = API(URI_SERVICEDEF_BY_NAME, HttpMethod.DELETE, 
HTTPStatus.NO_CONTENT)
+    GET_SERVICEDEF_BY_ID      = API(URI_SERVICEDEF_BY_ID, HttpMethod.GET, 
HTTPStatus.OK)
+    GET_SERVICEDEF_BY_NAME    = API(URI_SERVICEDEF_BY_NAME, HttpMethod.GET, 
HTTPStatus.OK)
+    FIND_SERVICEDEFS          = API(URI_SERVICEDEF, HttpMethod.GET, 
HTTPStatus.OK)
+
+    CREATE_SERVICE         = API(URI_SERVICE, HttpMethod.POST, HTTPStatus.OK)
+    UPDATE_SERVICE_BY_ID   = API(URI_SERVICE_BY_ID, HttpMethod.PUT, 
HTTPStatus.OK)
+    UPDATE_SERVICE_BY_NAME = API(URI_SERVICE_BY_NAME, HttpMethod.PUT, 
HTTPStatus.OK)
+    DELETE_SERVICE_BY_ID   = API(URI_SERVICE_BY_ID, HttpMethod.DELETE, 
HTTPStatus.NO_CONTENT)
+    DELETE_SERVICE_BY_NAME = API(URI_SERVICE_BY_NAME, HttpMethod.DELETE, 
HTTPStatus.NO_CONTENT)
+    GET_SERVICE_BY_ID      = API(URI_SERVICE_BY_ID, HttpMethod.GET, 
HTTPStatus.OK)
+    GET_SERVICE_BY_NAME    = API(URI_SERVICE_BY_NAME, HttpMethod.GET, 
HTTPStatus.OK)
+    FIND_SERVICES          = API(URI_SERVICE, HttpMethod.GET, HTTPStatus.OK)
+
+    CREATE_POLICY           = API(URI_POLICY, HttpMethod.POST, HTTPStatus.OK)
+    UPDATE_POLICY_BY_ID     = API(URI_POLICY_BY_ID, HttpMethod.PUT, 
HTTPStatus.OK)
+    UPDATE_POLICY_BY_NAME   = API(URI_POLICY_BY_NAME, HttpMethod.PUT, 
HTTPStatus.OK)
+    APPLY_POLICY            = API(URI_APPLY_POLICY, HttpMethod.POST, 
HTTPStatus.OK)
+    DELETE_POLICY_BY_ID     = API(URI_POLICY_BY_ID, HttpMethod.DELETE, 
HTTPStatus.NO_CONTENT)
+    DELETE_POLICY_BY_NAME   = API(URI_POLICY, HttpMethod.DELETE, 
HTTPStatus.NO_CONTENT)
+    GET_POLICY_BY_ID        = API(URI_POLICY_BY_ID, HttpMethod.GET, 
HTTPStatus.OK)
+    GET_POLICY_BY_NAME      = API(URI_POLICY_BY_NAME, HttpMethod.GET, 
HTTPStatus.OK)
+    GET_POLICIES_IN_SERVICE = API(URI_POLICIES_IN_SERVICE, HttpMethod.GET, 
HTTPStatus.OK)
+    FIND_POLICIES           = API(URI_POLICY, HttpMethod.GET, HTTPStatus.OK)
+
+    CREATE_ZONE         = API(URI_ZONE, HttpMethod.POST, HTTPStatus.OK)
+    UPDATE_ZONE_BY_ID   = API(URI_ZONE_BY_ID, HttpMethod.PUT, HTTPStatus.OK)
+    UPDATE_ZONE_BY_NAME = API(URI_ZONE_BY_NAME, HttpMethod.PUT, HTTPStatus.OK)
+    DELETE_ZONE_BY_ID   = API(URI_ZONE_BY_ID, HttpMethod.DELETE, 
HTTPStatus.NO_CONTENT)
+    DELETE_ZONE_BY_NAME = API(URI_ZONE_BY_NAME, HttpMethod.DELETE, 
HTTPStatus.NO_CONTENT)
+    GET_ZONE_BY_ID      = API(URI_ZONE_BY_ID, HttpMethod.GET, HTTPStatus.OK)
+    GET_ZONE_BY_NAME    = API(URI_ZONE_BY_NAME, HttpMethod.GET, HTTPStatus.OK)
+    FIND_ZONES          = API(URI_ZONE, HttpMethod.GET, HTTPStatus.OK)
+
+    CREATE_ROLE         = API(URI_ROLE, HttpMethod.POST, HTTPStatus.OK)
+    UPDATE_ROLE_BY_ID   = API(URI_ROLE_BY_ID, HttpMethod.PUT, HTTPStatus.OK)
+    DELETE_ROLE_BY_ID   = API(URI_ROLE_BY_ID, HttpMethod.DELETE, 
HTTPStatus.NO_CONTENT)
+    DELETE_ROLE_BY_NAME = API(URI_ROLE_BY_NAME, HttpMethod.DELETE, 
HTTPStatus.NO_CONTENT)
+    GET_ROLE_BY_ID      = API(URI_ROLE_BY_ID, HttpMethod.GET, HTTPStatus.OK)
+    GET_ROLE_BY_NAME    = API(URI_ROLE_BY_NAME, HttpMethod.GET, HTTPStatus.OK)
+    GET_ALL_ROLE_NAMES  = API(URI_ROLE_NAMES, HttpMethod.GET, HTTPStatus.OK)
+    GET_USER_ROLES      = API(URI_USER_ROLES, HttpMethod.GET, HTTPStatus.OK)
+    GRANT_ROLE          = API(URI_GRANT_ROLE, HttpMethod.PUT, HTTPStatus.OK)
+    REVOKE_ROLE         = API(URI_REVOKE_ROLE, HttpMethod.PUT, HTTPStatus.OK)
+    FIND_ROLES          = API(URI_ROLE, HttpMethod.GET, HTTPStatus.OK)
+
+    GET_PLUGIN_INFO      = API(URI_PLUGIN_INFO, HttpMethod.GET, HTTPStatus.OK)
+    DELETE_POLICY_DELTAS = API(URI_POLICY_DELTAS, HttpMethod.DELETE, 
HTTPStatus.NO_CONTENT)
+
+    def __init__(self, url, username, password):
+        self.log            = logging.getLogger(__name__)
+        self.__password     = password
+        self.url            = url
+        self.username       = username
+        self.session        = Session()
+        self.session.auth   = (username, password)
+        self.session.verify = False
+
+    def __call_api(self, api, params, request):
+        return api.call(self.session, self.url, params, request)
+
+    def __call_api_with_url_params(self, api, urlParams, params, request):
+        return api.apply_url_params(urlParams).call(self.session, self.url, 
params, request)
+
+    # Service Definition APIs
+    def create_service_def(self, serviceDef):
+        ret = self.__call_api(RangerClient.CREATE_SERVICEDEF, {}, serviceDef)
+
+        return RangerServiceDef(**ret) if ret is not None else None
+
+    def update_service_def_by_id(self, serviceDefId, serviceDef):
+        ret = 
self.__call_api_with_url_params(RangerClient.UPDATE_SERVICEDEF_BY_ID, { 
RangerClient.PARAM_ID: serviceDefId }, {}, serviceDef)
+
+        return RangerServiceDef(**ret) if ret is not None else None
+
+    def update_service_def(self, serviceDefName, serviceDef):
+        ret = 
self.__call_api_with_url_params(RangerClient.UPDATE_SERVICEDEF_BY_NAME, { 
RangerClient.PARAM_NAME: serviceDefName }, {}, serviceDef)
+
+        return RangerServiceDef(**ret) if ret is not None else None
+
+    def delete_service_def_by_id(self, serviceDefId):
+        self.__call_api_with_url_params(RangerClient.DELETE_SERVICEDEF_BY_ID, 
{ RangerClient.PARAM_ID: serviceDefId }, {}, {})
+
+        return None
+
+    def delete_service_def(self, serviceDefName):
+        
self.__call_api_with_url_params(RangerClient.DELETE_SERVICEDEF_BY_NAME, { 
RangerClient.PARAM_NAME: serviceDefName }, {}, {})
+
+        return None
+
+    def get_service_def_by_id(self, serviceDefId):
+        ret = 
self.__call_api_with_url_params(RangerClient.GET_SERVICEDEF_BY_ID, { 
RangerClient.PARAM_ID: serviceDefId }, {}, {})
+
+        return RangerServiceDef(**ret) if ret is not None else None
+
+    def get_service_def(self, serviceDefName):
+        ret = 
self.__call_api_with_url_params(RangerClient.GET_SERVICEDEF_BY_NAME, { 
RangerClient.PARAM_NAME: serviceDefName }, {}, {})
+
+        return RangerServiceDef(**ret) if ret is not None else None
+
+    def find_service_defs(self, filter):
+        ret = self.__call_api(RangerClient.FIND_SERVICEDEFS, filter, {})
+
+        return list(ret) if ret is not None else None
+
+    # Service APIs
+    def create_service(self, service):
+        ret = self.__call_api(RangerClient.CREATE_SERVICE, {}, service)
+
+        return RangerService(**ret) if ret is not None else None
+
+    def update_service_by_id(self, serviceId, service):
+        ret = 
self.__call_api_with_url_params(RangerClient.UPDATE_SERVICE_BY_ID, { 
RangerClient.PARAM_ID: serviceId }, {}, service)
+
+        return RangerService(**ret) if ret is not None else None
+
+    def update_service(self, serviceName, service):
+        ret = 
self.__call_api_with_url_params(RangerClient.UPDATE_SERVICE_BY_NAME, { 
RangerClient.PARAM_NAME: serviceName }, {}, service)
+
+        return RangerService(**ret) if ret is not None else None
+
+    def delete_service_by_id(self, serviceId):
+        self.__call_api_with_url_params(RangerClient.DELETE_SERVICE_BY_ID, { 
RangerClient.PARAM_ID: serviceId }, {}, {})
+
+        return None
+
+    def delete_service(self, serviceName):
+        self.__call_api_with_url_params(RangerClient.DELETE_SERVICE_BY_NAME, { 
RangerClient.PARAM_NAME: serviceName }, {}, {})
+
+        return None
+
+    def get_service_by_id(self, serviceId):
+        ret = self.__call_api_with_url_params(RangerClient.GET_SERVICE_BY_ID, 
{ RangerClient.PARAM_ID: serviceId }, {}, {})
+
+        return RangerService(**ret) if ret is not None else None
+
+    def get_service(self, serviceName):
+        ret = 
self.__call_api_with_url_params(RangerClient.GET_SERVICE_BY_NAME, { 
RangerClient.PARAM_NAME: serviceName }, {}, {})
+
+        return RangerService(**ret) if ret is not None else None
+
+    def find_services(self, filter):
+        ret = self.__call_api(RangerClient.FIND_SERVICES, filter, {})
+
+        return list(ret) if ret is not None else None
+
+    # Policy APIs
+    def create_policy(self, policy):
+        ret = self.__call_api(RangerClient.CREATE_POLICY, {}, policy)
+
+        return RangerPolicy(**ret) if ret is not None else None
+
+    def update_policy_by_id(self, policyId, policy):
+        ret = 
self.__call_api_with_url_params(RangerClient.UPDATE_POLICY_BY_ID, { 
RangerClient.PARAM_ID: policyId }, {}, policy)
+
+        return RangerPolicy(**ret) if ret is not None else None
+
+    def update_policy(self, serviceName, policyName, policy):
+        path_params = { RangerClient.PARAM_SERVICE_NAME: serviceName, 
RangerClient.PARAM_POLICY_NAME: policyName }
+        ret         = 
self.__call_api_with_url_params(RangerClient.UPDATE_POLICY_BY_NAME, 
path_params, {}, policy)
+
+        return RangerPolicy(**ret) if ret is not None else None
+
+    def apply_policy(self, policy):
+        ret = self.__call_api(RangerClient.APPLY_POLICY, {}, policy)
+
+        return RangerPolicy(**ret) if ret is not None else None
+
+    def delete_policy_by_id(self, policyId):
+        self.__call_api_with_url_params(RangerClient.DELETE_POLICY_BY_ID, { 
RangerClient.PARAM_ID: policyId }, {}, {})
+
+        return None
+
+    def delete_policy(self, serviceName, policyName):
+        query_params = { RangerClient.PARAM_POLICY_NAME: policyName, 
'servicename': serviceName }
+
+        self.__call_api(RangerClient.DELETE_POLICY_BY_NAME, query_params, {})
+
+        return None
+
+    def get_policy_by_id(self, policyId):
+        ret = self.__call_api_with_url_params(RangerClient.GET_POLICY_BY_ID, { 
RangerClient.PARAM_ID: policyId }, {}, {})
+
+        return RangerPolicy(**ret) if ret is not None else None
+
+    def get_policy(self, serviceName, policyName):
+        path_params = {RangerClient.PARAM_SERVICE_NAME: serviceName, 
RangerClient.PARAM_POLICY_NAME: policyName}
+        ret         = 
self.__call_api_with_url_params(RangerClient.GET_POLICY_BY_NAME, path_params, 
{}, {})
+
+        return RangerPolicy(**ret) if ret is not None else None
+
+    def get_policies_in_service(self, serviceName):
+        ret = 
self.__call_api_with_url_params(RangerClient.GET_POLICIES_IN_SERVICE, { 
RangerClient.PARAM_SERVICE_NAME: serviceName }, {}, {})
+
+        return list(ret) if ret is not None else None
+
+    def find_policies(self, filter):
+        ret = self.__call_api(RangerClient.FIND_POLICIES, filter, {})
+
+        return list(ret) if ret is not None else None
+
+    # SecurityZone APIs
+    def create_security_zone(self, securityZone):
+        ret = self.__call_api(RangerClient.CREATE_ZONE, {}, securityZone)
+
+        return RangerSecurityZone(**ret) if ret is not None else None
+
+    def update_security_zone_by_id(self, zoneId, securityZone):
+        ret = self.__call_api_with_url_params(RangerClient.UPDATE_ZONE_BY_ID, 
{ RangerClient.PARAM_ID: zoneId }, {}, securityZone)
+
+        return RangerSecurityZone(**ret) if ret is not None else None
+
+    def update_security_zone(self, zoneName, securityZone):
+        ret = 
self.__call_api_with_url_params(RangerClient.UPDATE_ZONE_BY_NAME, { 
RangerClient.PARAM_NAME: zoneName }, {}, securityZone)
+
+        return RangerSecurityZone(**ret) if ret is not None else None
+
+    def delete_security_zone_by_id(self, zoneId):
+        self.__call_api_with_url_params(RangerClient.DELETE_ZONE_BY_ID, { 
RangerClient.PARAM_ID: zoneId }, {}, {})
+        return None
+
+    def delete_security_zone(self, zoneName):
+        self.__call_api_with_url_params(RangerClient.DELETE_ZONE_BY_NAME, { 
RangerClient.PARAM_NAME: zoneName }, {}, {})
+
+        return None
+
+    def get_security_zone_by_id(self, zoneId):
+        ret = self.__call_api_with_url_params(RangerClient.GET_ZONE_BY_ID, { 
RangerClient.PARAM_ID: zoneId }, {}, {})
+
+        return RangerSecurityZone(**ret) if ret is not None else None
+
+    def get_security_zone(self, zoneName):
+        ret = self.__call_api_with_url_params(RangerClient.GET_ZONE_BY_NAME, { 
RangerClient.PARAM_NAME: zoneName }, {}, {})
+
+        return RangerSecurityZone(**ret) if ret is not None else None
+
+    def find_security_zones(self, filter):
+        ret = self.__call_api(RangerClient.FIND_ZONES, filter, {})
+
+        return list(ret) if ret is not None else None
+
+    # Role APIs
+    def create_role(self, serviceName, role):
+        query_params = {RangerClient.PARAM_SERVICE_NAME, serviceName}
+        ret          = self.__call_api(RangerClient.CREATE_ROLE, query_params, 
role)
+
+        return RangerRole(**ret) if ret is not None else None
+
+    def update_role(self, roleId, role):
+        ret = self.__call_api_with_url_params(RangerClient.UPDATE_ROLE_BY_ID, 
{ RangerClient.PARAM_ID: roleId }, {}, role)
+
+        return RangerRole(**ret) if ret is not None else None
+
+    def delete_role_by_id(self, roleId):
+        self.__call_api_with_url_params(RangerClient.DELETE_ROLE_BY_ID, { 
RangerClient.PARAM_ID: roleId }, {}, {})
+
+        return None
+
+    def delete_role(self, roleName, execUser, serviceName):
+        query_params = { RangerClient.PARAM_EXEC_USER: execUser, 
RangerClient.PARAM_SERVICE_NAME: serviceName }
+
+        self.__call_api_with_url_params(RangerClient.DELETE_ROLE_BY_NAME, { 
RangerClient.PARAM_NAME: roleName }, query_params, {})
+
+        return None
+
+    def get_role_by_id(self, roleId):
+        ret = self.__call_api_with_url_params(RangerClient.GET_ROLE_BY_ID, { 
RangerClient.PARAM_ID: roleId }, {}, {})
+
+        return RangerRole(**ret) if ret is not None else None
+
+    def get_role(self, roleName, execUser, serviceName):
+        query_params = { RangerClient.PARAM_EXEC_USER: execUser, 
RangerClient.PARAM_SERVICE_NAME: serviceName }
+        ret          = 
self.__call_api_with_url_params(RangerClient.GET_ROLE_BY_NAME, { 
RangerClient.PARAM_NAME: roleName }, query_params, {})
+
+        return RangerRole(**ret) if ret is not None else None
+
+    def get_all_role_names(self, execUser, serviceName):
+        query_params = { RangerClient.PARAM_EXEC_USER: execUser, 
RangerClient.PARAM_SERVICE_NAME: serviceName }
+        ret          = 
self.__call_api_with_url_params(RangerClient.GET_ALL_ROLE_NAMES, { 
RangerClient.PARAM_NAME: serviceName }, query_params, {})
+
+        return list(ret) if ret is not None else None
+
+    def get_user_roles(self, user):
+        ret = self.__call_api_with_url_params(RangerClient.GET_USER_ROLES, { 
RangerClient.PARAM_NAME: user }, {}, {})
+
+        return list(ret) if ret is not None else None
+
+    def find_roles(self, filter):
+        ret = self.__call_api(RangerClient.FIND_ROLES, filter, {})
+
+        return list(ret) if ret is not None else None
+
+    def grant_role(self, serviceName, request):
+        ret = self.__call_api_with_url_params(RangerClient.GRANT_ROLE, { 
RangerClient.PARAM_NAME: serviceName }, {}, request)
+
+        return RESTResponse(**ret) if ret is not None else None
+
+    def revoke_role(self, serviceName, request):
+        ret = self.__call_api_with_url_params(RangerClient.REVOKE_ROLE, { 
RangerClient.PARAM_NAME: serviceName }, {}, request)
+
+        return RESTResponse(**ret) if ret is not None else None
+
+    # Admin APIs
+    def delete_policy_deltas(self, days, reloadServicePoliciesCache):
+        query_params = {
+            RangerClient.PARAM_DAYS: days,
+            RangerClient.PARAM_RELOAD_SERVICE_POLICIES_CACHE: 
reloadServicePoliciesCache
+        }
+
+        self.__call_api(RangerClient.DELETE_POLICY_DELTAS, query_params, {})
+
+        return None
diff --git a/intg/src/main/python/ranger/model/__init__.py 
b/intg/src/main/python/ranger/model/__init__.py
new file mode 100644
index 0000000..ed9d0b3
--- /dev/null
+++ b/intg/src/main/python/ranger/model/__init__.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
diff --git a/intg/src/main/python/ranger/model/grant_revoke_role_request.py 
b/intg/src/main/python/ranger/model/grant_revoke_role_request.py
new file mode 100644
index 0000000..fb0eab2
--- /dev/null
+++ b/intg/src/main/python/ranger/model/grant_revoke_role_request.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+import json
+
+
+class GrantRevokeRoleRequest:
+    def __init__(self, grantor=None, grantorGroups=None, targetRoles=None, 
users=None, groups=None, roles=None, grantOption=None, clientIPAddress=None, 
clientType=None, requestData=None, sessionId=None, clusterName=None):
+        self.grantor         = grantor
+        self.grantorGroups   = grantorGroups if grantorGroups is not None else 
[]
+        self.targetRoles     = targetRoles if targetRoles is not None else []
+        self.users           = users if users is not None else []
+        self.groups          = groups if groups is not None else []
+        self.roles           = roles if roles is not None else []
+        self.grantOption     = grantOption if grantOption is not None else 
False
+        self.clientIPAddress = clientIPAddress
+        self.clientType      = clientType
+        self.requestData     = requestData
+        self.sessionId       = sessionId
+        self.clusterName     = clusterName
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
diff --git a/intg/src/main/python/ranger/model/ranger_base.py 
b/intg/src/main/python/ranger/model/ranger_base.py
new file mode 100644
index 0000000..ae8de74
--- /dev/null
+++ b/intg/src/main/python/ranger/model/ranger_base.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+import json
+
+
+class RangerBase:
+    def __init__(self, id=None, guid=None, createdBy=None, updatedBy=None, 
createTime=None, updateTime=None, version=None, isEnabled=None):
+        self.id         = id
+        self.guid       = guid
+        self.isEnabled  = isEnabled if isEnabled is not None else True
+        self.createdBy  = createdBy
+        self.updatedBy  = updatedBy
+        self.createTime = createTime
+        self.updateTime = updateTime
+        self.version    = version
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
diff --git a/intg/src/main/python/ranger/model/ranger_policy.py 
b/intg/src/main/python/ranger/model/ranger_policy.py
new file mode 100644
index 0000000..9195f4b
--- /dev/null
+++ b/intg/src/main/python/ranger/model/ranger_policy.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+import json
+
+from ranger.model.ranger_base import RangerBase
+
+
+class RangerPolicyResource:
+    def __init__(self, values=None, isExcludes=None, isRecursive=None):
+        self.values      = values if values is not None else []
+        self.isExcludes  = isExcludes if isExcludes is not None else False
+        self.isRecursive = isRecursive if isRecursive is not None else False
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RangerPolicyItemCondition:
+    def __init__(self, type=None, values=None):
+        self.type   = type
+        self.values = values if values is not None else []
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RangerPolicyItem:
+    def __init__(self, accesses=None, users=None, groups=None, roles=None, 
conditions=None, delegateAdmin=None):
+        self.accesses      = accesses if accesses is not None else []
+        self.users         = users if users is not None else []
+        self.groups        = groups if groups is not None else []
+        self.roles         = roles if roles is not None else []
+        self.conditions    = conditions if conditions is not None else []
+        self.delegateAdmin = delegateAdmin if delegateAdmin is not None else 
False
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RangerPolicyItemAccess:
+    def __init__(self, type=None, isAllowed=None):
+        self.type      = type
+        self.isAllowed = isAllowed if isAllowed is not None else True
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RangerPolicyItemDataMaskInfo:
+    def __init__(self, dataMaskType=None, conditionExpr=None, valueExpr=None):
+        self.dataMaskType  = dataMaskType
+        self.conditionExpr = conditionExpr
+        self.valueExpr     = valueExpr
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RangerDataMaskPolicyItem(RangerPolicyItem):
+    def __init__(self, dataMaskInfo=None, accesses=None, users=None, 
groups=None, roles=None, conditions=None, delegateAdmin=None):
+        super().__init__(accesses, users, groups, roles, conditions, 
delegateAdmin)
+
+        self.dataMaskInfo = dataMaskInfo if dataMaskInfo is not None else 
RangerPolicyItemDataMaskInfo()
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RangerRowFilterPolicyItem(RangerPolicyItem):
+    def __init__(self, rowFilterInfo=None, accesses=None, users=None, 
groups=None, roles=None, conditions=None, delegateAdmin=None):
+        super().__init__(accesses, users, groups, roles, conditions, 
delegateAdmin)
+
+        self.rowFilterInfo = rowFilterInfo
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RangerPolicy(RangerBase):
+    def __init__(self, id=None, guid=None, createdBy=None, updatedBy=None, 
createTime=None, updateTime=None,
+                 service=None, name=None, description=None, isEnabled=True, 
isAuditEnabled=None, resources=None,
+                 policyItems=None, dataMaskPolicyItems=None, 
rowFilterPolicyItems=None, serviceType=None, options=None,
+                 policyLabels=None, zoneName=None, isDenyAllElse=None, 
validitySchedules=None, version=None,
+                 denyPolicyItems=None, denyExceptions=None, 
allowExceptions=None, resourceSignature=None,
+                 policyType=None, policyPriority=None, conditions=None):
+        super().__init__(id, guid, createdBy, updatedBy, createTime, 
updateTime, version, isEnabled)
+
+        self.service              = service
+        self.name                 = name
+        self.policyType           = policyType
+        self.policyPriority       = policyPriority if policyPriority is not 
None else 0
+        self.description          = description
+        self.resourceSignature    = resourceSignature
+        self.isAuditEnabled       = isAuditEnabled if isAuditEnabled is not 
None else True
+        self.resources            = resources if resources is not None else {}
+        self.policyItems          = policyItems if policyItems is not None 
else []
+        self.denyPolicyItems      = denyPolicyItems if denyPolicyItems is not 
None else []
+        self.allowExceptions      = allowExceptions if allowExceptions is not 
None else []
+        self.denyExceptions       = denyExceptions if denyExceptions is not 
None else []
+        self.dataMaskPolicyItems  = dataMaskPolicyItems if dataMaskPolicyItems 
is not None else []
+        self.rowFilterPolicyItems = rowFilterPolicyItems if 
rowFilterPolicyItems is not None else []
+        self.serviceType          = serviceType
+        self.options              = options if options is not None else {}
+        self.validitySchedules    = validitySchedules if validitySchedules is 
not None else []
+        self.policyLabels         = policyLabels if policyLabels is not None 
else []
+        self.zoneName             = zoneName
+        self.conditions           = conditions
+        self.isDenyAllElse        = isDenyAllElse if isDenyAllElse is not None 
else False
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
diff --git a/intg/src/main/python/ranger/model/ranger_role.py 
b/intg/src/main/python/ranger/model/ranger_role.py
new file mode 100644
index 0000000..aa05216
--- /dev/null
+++ b/intg/src/main/python/ranger/model/ranger_role.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+import json
+
+from ranger.model.ranger_base import RangerBase
+
+
+class RoleMember:
+    def __init__(self, name=None, isAdmin=None):
+        self.name    = name
+        self.isAdmin = isAdmin if isAdmin is not None else False
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RangerRole(RangerBase):
+    def __init__(self, id=None, guid=None, createdBy=None, updatedBy=None, 
createTime=None, updateTime=None,
+                 version=None, isEnabled=None, name=None, description=None, 
options=None, users=None, groups=None,
+                 roles=None, createdByUser=None):
+        super().__init__(id, guid, createdBy, updatedBy, createTime, 
updateTime, version, isEnabled)
+
+        self.name          = name
+        self.description   = description
+        self.options       = options if options is not None else {}
+        self.users         = users if users is not None else []
+        self.groups        = groups if groups is not None else []
+        self.roles         = roles if roles is not None else []
+        self.createdByUser = createdByUser
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
diff --git a/intg/src/main/python/ranger/model/ranger_security_zone.py 
b/intg/src/main/python/ranger/model/ranger_security_zone.py
new file mode 100644
index 0000000..40a5209
--- /dev/null
+++ b/intg/src/main/python/ranger/model/ranger_security_zone.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+import json
+
+from ranger.model.ranger_base import RangerBase
+
+
+class RangerSecurityZoneService:
+    def __init__(self, resources=None):
+        self.resources = resources if resources is not None else []
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RangerSecurityZone(RangerBase):
+    def __init__(self, id=None, guid=None, createdBy=None, updatedBy=None, 
createTime=None, updateTime=None,
+                 version=None, isEnabled=None, name=None, services=None, 
tagServices=None, adminUsers=None,
+                 adminUserGroups=None, auditUsers=None, auditUserGroups=None, 
description=None):
+        super().__init__(id, guid, createdBy, updatedBy, createTime, 
updateTime, version, isEnabled)
+        self.name = name
+        self.services = services if services is not None else {}
+        self.tagServices = tagServices if tagServices is not None else []
+        self.adminUsers = adminUsers if adminUsers is not None else []
+        self.adminUserGroups = adminUserGroups if adminUserGroups is not None 
else []
+        self.auditUsers = auditUsers if auditUsers is not None else []
+        self.auditUserGroups = auditUserGroups if auditUserGroups is not None 
else []
+        self.description = description
+        return
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
diff --git a/intg/src/main/python/ranger/model/ranger_service.py 
b/intg/src/main/python/ranger/model/ranger_service.py
new file mode 100644
index 0000000..637c38f
--- /dev/null
+++ b/intg/src/main/python/ranger/model/ranger_service.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+import json
+
+from ranger.model.ranger_base import RangerBase
+
+
+class RangerService(RangerBase):
+    def __init__(self, id=None, guid=None, createdBy=None, updatedBy=None, 
createTime=None, updateTime=None,
+                 version=None, isEnabled=None, type=None, name=None, 
displayName=None, description=None,
+                 tagService=None, configs=None, policyVersion=None, 
policyUpdateTime=None, tagVersion=None,
+                 tagUpdateTime=None):
+        super().__init__(id, guid, createdBy, updatedBy, createTime, 
updateTime, version, isEnabled)
+
+        self.type             = type
+        self.name             = name
+        self.displayName      = displayName
+        self.description      = description
+        self.tagService       = tagService
+        self.configs          = configs if configs is not None else {}
+        self.policyVersion    = policyVersion
+        self.policyUpdateTime = policyUpdateTime
+        self.tagVersion       = tagVersion
+        self.tagUpdateTime    = tagUpdateTime
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
diff --git a/intg/src/main/python/ranger/model/ranger_service_def.py 
b/intg/src/main/python/ranger/model/ranger_service_def.py
new file mode 100644
index 0000000..8cd4757
--- /dev/null
+++ b/intg/src/main/python/ranger/model/ranger_service_def.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+import json
+
+from ranger.model.ranger_base import RangerBase
+
+
+class RangerDataMaskDef:
+    def __init__(self, maskTypes=None, accessTypes=None, resources=None):
+        self.maskTypes   = maskTypes if maskTypes is not None else []
+        self.accessTypes = accessTypes if accessTypes is not None else []
+        self.resources   = resources if resources is not None else []
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RangerRowFilterDef:
+    def __init__(self, accessTypes=None, resources=None):
+        self.accessTypes = accessTypes if accessTypes is not None else []
+        self.resources   = resources if resources is not None else []
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RangerServiceConfigDef:
+    def __init__(self, itemId=None, name=None, type=None, subType=None, 
mandatory=None, defaultValue=None,
+                 validationRegEx=None, validationMessage=None, uiHint=None, 
label=None, description=None,
+                 rbKeyLabel=None, rbKeyDescription=None, 
rbKeyValidationMessage=None):
+        self.itemId                 = itemId
+        self.name                   = name
+        self.type                   = type
+        self.subType                = subType
+        self.mandatory              = mandatory if mandatory is not None else 
False
+        self.defaultValue           = defaultValue
+        self.validationRegEx        = validationRegEx
+        self.validationMessage      = validationMessage
+        self.uiHint                 = uiHint
+        self.label                  = label
+        self.description            = description
+        self.rbKeyLabel             = rbKeyLabel
+        self.rbKeyDescription       = rbKeyDescription
+        self.rbKeyValidationMessage = rbKeyValidationMessage
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RangerAccessTypeDef:
+    def __init__(self, itemId=None, name=None, label=None, rbKeyLabel=None, 
impliedGrants=None):
+        self.itemId        = itemId
+        self.name          = name
+        self.label         = label
+        self.rbKeyLabel    = rbKeyLabel
+        self.impliedGrants = impliedGrants if impliedGrants is not None else []
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RangerResourceDef:
+    def __init__(self, itemId=None, name=None, type=None, level=None, 
parent=None, mandatory=None, lookupSupported=None,
+                 recursiveSupported=None, excludesSupported=None, 
matcher=None, matcherOptions=None,
+                 validationRegEx=None, validationMessage=None, uiHint=None, 
label=None, description=None,
+                 rbKeyLabel=None, rbKeyDescription=None, 
rbKeyValidationMessage=None, accessTypeRestrictions=None,
+                 isValidLeaf=None):
+        self.itemId                 = itemId
+        self.name                   = name
+        self.type                   = type
+        self.level                  = level if level is not None else 1
+        self.parent                 = parent
+        self.mandatory              = mandatory if mandatory is not None else 
False
+        self.lookupSupported        = lookupSupported if lookupSupported is 
not None else False
+        self.recursiveSupported     = recursiveSupported if recursiveSupported 
is not None else False
+        self.excludesSupported      = excludesSupported if excludesSupported 
is not None else False
+        self.matcher                = matcher
+        self.matcherOptions         = matcherOptions if matcherOptions is not 
None else {}
+        self.validationRegEx        = validationRegEx
+        self.validationMessage      = validationMessage
+        self.uiHint                 = uiHint
+        self.label                  = label
+        self.description            = description
+        self.rbKeyLabel             = rbKeyLabel
+        self.rbKeyDescription       = rbKeyDescription
+        self.rbKeyValidationMessage = rbKeyValidationMessage
+        self.accessTypeRestrictions = accessTypeRestrictions if 
accessTypeRestrictions is not None else []
+        self.isValidLeaf            = isValidLeaf
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
+
+
+class RangerServiceDef(RangerBase):
+    def __init__(self, id=None, guid=None, createdBy=None, updatedBy=None, 
createTime=None, updateTime=None,
+                 version=None, isEnabled=None, name=None, displayName=None, 
implClass=None, label=None,
+                 description=None, rbKeyLabel=None, rbKeyDescription=None, 
options=None, configs=None, resources=None,
+                 accessTypes=None, policyConditions=None, 
contextEnrichers=None, enums=None, dataMaskDef=None,
+                 rowFilterDef=None):
+        super().__init__(id, guid, createdBy, updatedBy, createTime, 
updateTime, version, isEnabled)
+
+        self.name             = name
+        self.displayName      = displayName
+        self.implClass        = implClass
+        self.label            = label
+        self.description      = description
+        self.rbKeyLabel       = rbKeyLabel
+        self.rbKeyDescription = rbKeyDescription
+        self.options          = options if options is not None else {}
+        self.configs          = configs if configs is not None else []
+        self.resources        = resources if resources is not None else []
+        self.accessTypes      = accessTypes if accessTypes is not None else []
+        self.policyConditions = policyConditions if policyConditions is not 
None else []
+        self.contextEnrichers = contextEnrichers if contextEnrichers is not 
None else []
+        self.enums            = enums if enums is not None else []
+        self.dataMaskDef      = dataMaskDef if dataMaskDef is not None else 
RangerDataMaskDef()
+        self.rowFilterDef     = rowFilterDef if rowFilterDef is not None else 
RangerRowFilterDef()
+
+    def __repr__(self):
+        return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True, 
indent=4)
diff --git a/intg/src/main/resources/logging.conf 
b/intg/src/main/resources/logging.conf
new file mode 100644
index 0000000..c168775
--- /dev/null
+++ b/intg/src/main/resources/logging.conf
@@ -0,0 +1,40 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+#
+
+[loggers]
+keys=root
+
+[handlers]
+keys=consoleHandler
+
+[formatters]
+keys=simpleFormatter
+
+[logger_root]
+level=INFO
+handlers=consoleHandler
+
+[handler_consoleHandler]
+class=StreamHandler
+level=INFO
+formatter=simpleFormatter
+args=(sys.stdout,)
+
+[formatter_simpleFormatter]
+format=%(asctime)s.%(msecs)03d %(levelname)s:%(name)s:%(message)s
+datefmt=%Y-%m-%d %H:%M:%S
diff --git a/intg/src/test/python/test_ranger_client.py 
b/intg/src/test/python/test_ranger_client.py
new file mode 100644
index 0000000..5efda23
--- /dev/null
+++ b/intg/src/test/python/test_ranger_client.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+import unittest
+from unittest.mock import patch
+from ranger.model.ranger_service import RangerService
+from ranger.client.ranger_client import API, HttpMethod, HTTPStatus, 
RangerClient
+
+
+class MockResponse:
+    def __init__(self, status_code, response=None, content=None):
+        self.status_code = status_code
+        self.response    = response
+        self.content     = content
+        return
+
+    def json(self):
+        return [self.response.__repr__()]
+
+    def text(self):
+        return str(self.content)
+
+
+class TestRangerClient(unittest.TestCase):
+    URL      = "url"
+    USERNAME = "user"
+    PASSWORD = "password"
+
+    @patch('ranger.client.ranger_client.Session')
+    def test_get_service_unavailable(self, mock_session):
+        mock_session.return_value.get.return_value = 
MockResponse(HTTPStatus.SERVICE_UNAVAILABLE)
+        result                                     = 
RangerClient(TestRangerClient.URL, TestRangerClient.USERNAME, 
TestRangerClient.PASSWORD).find_services({})
+
+        self.assertTrue(result is None)
+
+
+    @patch('ranger.client.ranger_client.Session')
+    def test_get_success(self, mock_session):
+        response                                   = RangerService()
+        mock_session.return_value.get.return_value = 
MockResponse(HTTPStatus.OK, response=response, content='Success')
+        result                                     = 
RangerClient(TestRangerClient.URL, TestRangerClient.USERNAME, 
TestRangerClient.PASSWORD).find_services({})
+
+        self.assertTrue(response.__repr__() in result)
+
+
+    @patch('ranger.client.ranger_client.Session')
+    @patch('ranger.client.ranger_client.Response')
+    def test_get_unexpected_status_code(self, mock_response, mock_session):
+        content                                    = 'Internal Server Error'
+        mock_response.text                         = content
+        mock_response.content                      = content
+        mock_response.status_code                  = 
HTTPStatus.INTERNAL_SERVER_ERROR
+        mock_session.return_value.get.return_value = mock_response
+
+        try:
+            RangerClient(TestRangerClient.URL, TestRangerClient.USERNAME, 
TestRangerClient.PASSWORD).find_services({})
+        except Exception as e:
+            self.assertTrue(content in repr(e))
+
+
+    @patch('ranger.client.ranger_client.RangerClient.FIND_SERVICES')
+    def test_unexpected_http_method(self, mock_api):
+        mock_api.method.return_value = "PATCH"
+
+        try:
+            RangerClient(TestRangerClient.URL, TestRangerClient.USERNAME, 
TestRangerClient.PASSWORD).find_services({})
+        except Exception as e:
+            self.assertTrue('Unsupported HTTP Method' in repr(e))
+
+
+    def test_url_missing_format(self):
+        params = {'arg1': 1, 'arg2': 2}
+
+        try:
+            API("{arg1}test{arg2}path{arg3}", HttpMethod.GET, 
HTTPStatus.OK).apply_url_params(params)
+
+            self.fail("Supposed to fail")
+        except KeyError as e:
+            self.assertTrue('KeyError' in repr(e))
+
+
+    def test_url_invalid_format(self):
+        params = {'1', '2'}
+
+        try:
+            API("{}test{}path{}", HttpMethod.GET, 
HTTPStatus.OK).apply_url_params(params)
+
+            self.fail("Supposed to fail")
+        except TypeError as e:
+            self.assertTrue('TypeError' in repr(e))
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/pom.xml b/pom.xml
index 4e7c922..b8d6141 100644
--- a/pom.xml
+++ b/pom.xml
@@ -158,6 +158,8 @@
         <libpam4j.version>1.10</libpam4j.version>
         <local.lib.dir>${project.basedir}/../lib/local</local.lib.dir>
         <log4j.version>1.2.17</log4j.version>
+        <maven.exec.plugin.version>1.6.0</maven.exec.plugin.version>
+        <maven.pmd.plugin.version>3.7</maven.pmd.plugin.version>
         <metrics.core.version>3.0.2</metrics.core.version>
         <mockito.version>3.0.0</mockito.version>
         <mockito.all.version>1.10.19</mockito.all.version>
@@ -948,7 +950,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-pmd-plugin</artifactId>
-                <version>3.7</version>
+                <version>${maven.pmd.plugin.version}</version>
                 <executions>
                     <execution>
                         <phase>verify</phase>
@@ -1030,6 +1032,7 @@
                         <exclude>**/package.json</exclude>
                         <exclude>**/package-lock.json</exclude>
                         <exclude>**/ranger_es_schema.json</exclude>
+                        <exclude>**/venv/**</exclude>
                     </excludes>
                 </configuration>
             </plugin>
diff --git a/ranger-examples/distro/src/main/assembly/sample-client.xml 
b/ranger-examples/distro/src/main/assembly/sample-client.xml
index cde39f1..e3fa1bb 100644
--- a/ranger-examples/distro/src/main/assembly/sample-client.xml
+++ b/ranger-examples/distro/src/main/assembly/sample-client.xml
@@ -78,10 +78,10 @@
             
<directory>${project.parent.basedir}/sample-client/scripts</directory>
             <includes>
                 <include>*.sh</include>
+                <include>*.py</include>
             </includes>
             <fileMode>755</fileMode>
         </fileSet>
-<!--        only for testing -->
         <fileSet>
             <outputDirectory></outputDirectory>
             <directory>${project.parent.basedir}/sample-client/conf</directory>
@@ -99,5 +99,24 @@
                 <include>*.properties</include>
             </includes>
         </fileSet>
+        <fileSet>
+            <outputDirectory>venv/pylib</outputDirectory>
+            <directory>../../intg/src/main/python</directory>
+        </fileSet>
+        <fileSet>
+            <outputDirectory>venv/resources</outputDirectory>
+            <directory>../../intg/src/main/resources</directory>
+        </fileSet>
+        <fileSet>
+            <outputDirectory>venv/pylib</outputDirectory>
+            <directory>../sample-client/src/main/python</directory>
+            <includes>
+                <include>sample_client.py</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <outputDirectory>venv</outputDirectory>
+            <directory>../sample-client/venv</directory>
+        </fileSet>
     </fileSets>
 </assembly>
diff --git a/ranger-examples/sample-client/.gitignore 
b/ranger-examples/sample-client/.gitignore
index a6f89c2..897593c 100644
--- a/ranger-examples/sample-client/.gitignore
+++ b/ranger-examples/sample-client/.gitignore
@@ -1 +1,2 @@
-/target/
\ No newline at end of file
+/target/
+/venv/
\ No newline at end of file
diff --git a/ranger-examples/sample-client/conf/config.properties 
b/ranger-examples/sample-client/conf/config.properties
new file mode 100644
index 0000000..5be772a
--- /dev/null
+++ b/ranger-examples/sample-client/conf/config.properties
@@ -0,0 +1,21 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+ranger.client.url=https://localhost:6182
+# Authentication properties
+ranger.client.authentication.type=kerberos
+ranger.client.kerberos.principal=
+ranger.client.kerberos.keytab=
+ranger.client.ssl.config.filename=
\ No newline at end of file
diff --git a/ranger-examples/sample-client/pom.xml 
b/ranger-examples/sample-client/pom.xml
index b102e38..d112c6f 100644
--- a/ranger-examples/sample-client/pom.xml
+++ b/ranger-examples/sample-client/pom.xml
@@ -28,6 +28,44 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>sample-client</artifactId>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-clean-plugin</artifactId>
+                <version>2.6.1</version>
+                <configuration>
+                    <filesets>
+                        <fileset>
+                            <directory>venv</directory>
+                        </fileset>
+                    </filesets>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <version>${maven.exec.plugin.version}</version>
+                <executions>
+                    <execution>
+                        <configuration>
+                            <executable>python3</executable>
+                            <arguments>
+                                <argument>-m</argument>
+                                <argument>venv</argument>
+                                <argument>venv</argument>
+                            </arguments>
+                        </configuration>
+                        <id>create-venv</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>exec</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
     <dependencies>
         <dependency>
             <groupId>org.apache.ranger</groupId>
diff --git a/ranger-examples/sample-client/scripts/run-pyclient.sh 
b/ranger-examples/sample-client/scripts/run-pyclient.sh
new file mode 100644
index 0000000..8649f0b
--- /dev/null
+++ b/ranger-examples/sample-client/scripts/run-pyclient.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+usage() {
+  echo "usage: run-pyclient.sh
+  -n <arg> url to connect to
+  -h       show help."
+  exit 1
+}
+
+cd venv || exit
+source bin/activate
+INSTALL="python3 ./../setup.py install"
+$INSTALL >> build.log
+PYTHON_CMD="python3 build/lib/pylib/sample_client.py"
+while getopts "n:h" opt; do
+  case $opt in
+    n) URL=$OPTARG
+       PYTHON_CMD="$PYTHON_CMD --url $URL"
+       ;;
+    h) usage
+       ;;
+    \?) echo -e \\n"Option -$OPTARG not allowed."
+       usage
+       ;;
+  esac
+done
+
+prompt="Sample Authentication User Name:"
+read -p "$prompt" userName
+prompt="Sample Authentication User Password:"
+read -p "$prompt" -s password
+printf "\n"
+PYTHON_CMD="$PYTHON_CMD --username $userName --password $password"
+printf "Python command : %s\n" "$PYTHON_CMD"
+$PYTHON_CMD
\ No newline at end of file
diff --git a/ranger-examples/sample-client/scripts/setup.py 
b/ranger-examples/sample-client/scripts/setup.py
new file mode 100644
index 0000000..c4e7407
--- /dev/null
+++ b/ranger-examples/sample-client/scripts/setup.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+from setuptools import setup, find_packages
+
+# External dependencies
+requirements = ['requests>=2.24']
+
+setup(
+    name='sample_pyclient',
+    packages=['pylib', 'pylib/ranger/client', 'pylib/ranger/model'],
+    data_files=[('../../lib/resources', ['resources/logging.conf'])],
+    install_requires=requirements,
+    zip_safe=False
+)
diff --git a/ranger-examples/sample-client/src/main/python/sample_client.py 
b/ranger-examples/sample-client/src/main/python/sample_client.py
new file mode 100644
index 0000000..7ff6e70
--- /dev/null
+++ b/ranger-examples/sample-client/src/main/python/sample_client.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+import argparse
+import logging.config
+
+from ranger.model.ranger_service import RangerService
+from ranger.model.ranger_role import RangerRole, RoleMember
+from ranger.client.ranger_client import RangerClient, LOG_CONFIG
+from ranger.model.ranger_policy import RangerPolicy, RangerPolicyResource
+from ranger.model.ranger_service_def import RangerServiceConfigDef, 
RangerAccessTypeDef, RangerResourceDef, RangerServiceDef
+
+logging.config.fileConfig(LOG_CONFIG, disable_existing_loggers=False)
+log = logging.getLogger(__name__)
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description='Parameters required for 
connection to Ranger Admin!')
+    parser.add_argument('--url', default='http://localhost:6080')
+    parser.add_argument('--username')
+    parser.add_argument('--password')
+    args = parser.parse_args()
+
+    service_def_name = "sampleServiceDef"
+    service_name = "sampleService"
+    policy_name = "samplePolicy"
+    role_name = "sampleRole"
+
+    ranger_client = RangerClient(args.url, args.username, args.password)
+
+    # create a new service definition
+    config = RangerServiceConfigDef(itemId=1, name='sampleConfig', 
type='string')
+    access_type = RangerAccessTypeDef(itemId=1, name='sampleAccess')
+    resource_def = RangerResourceDef(itemId=1, name='root', type='string')
+    service_def = RangerServiceDef(name=service_def_name, configs=[config], 
accessTypes=[access_type],
+                                   resources=[resource_def])
+
+    created_service_def = ranger_client.create_service_def(service_def)
+    log.info('New Service Definition created successfully 
{}'.format(created_service_def))
+
+    # create a new service
+    service = RangerService(name=service_name, type=service_def_name)
+
+    created_service = ranger_client.create_service(service)
+    log.info('New Service created successfully {}'.format(created_service))
+
+    # create a new policy
+    resource = RangerPolicyResource(['/path/to/sample/resource'], False, False)
+    policy = RangerPolicy(service=service_name, name=policy_name, 
resources={'root': resource})
+    created_policy = ranger_client.create_policy(policy)
+    log.info('New Ranger Policy created successfully 
{}'.format(created_policy))
+
+    # update an existing policy
+    policy = RangerPolicy(service=service_name, name=policy_name, 
description='Policy Updated!')
+    updated_policy = ranger_client.update_policy(service_name, policy_name, 
policy)
+    log.info('Ranger Policy updated successfully {}'.format(updated_policy))
+
+    # get a policy by name
+    fetched_policy = ranger_client.get_policy(service_name, policy_name)
+    log.info('Policy {} fetched {}'.format(policy_name, fetched_policy))
+
+    # get all policies
+    all_policies = ranger_client.find_policies({})
+    for id, policy in enumerate(all_policies):
+        log.info('Policy #{}: {}'.format(id, RangerPolicy(**policy)))
+
+    # delete a policy by name
+    ranger_client.delete_policy(service_name, policy_name)
+    log.info('Policy {} successfully deleted'.format(policy_name))
+
+    # create a role in ranger
+    sample_role = RangerRole(name=role_name, description='Sample Role', 
users=[RoleMember(isAdmin=True)])
+    created_role = ranger_client.create_role(service_name, sample_role)
+    log.info('New Role successfully created {}'.format(created_role))
+
+    # update a role in ranger
+    sample_role = RangerRole(name=role_name, description='Updated Sample 
Role!')
+    updated_role = ranger_client.update_role(created_role.id, sample_role)
+    log.info('Role {} successfully updated {}'.format(role_name, updated_role))
+
+    # get all roles in ranger
+    all_roles = ranger_client.find_roles({})
+    log.info('List of Roles {}'.format(all_roles)) if all_roles is not None 
else log.info('No roles found!')
+
+    # delete a role in ranger
+    ranger_client.delete_role(role_name, ranger_client.username, service_name)
+    log.info('Role {} successfully deleted'.format(role_name))
+
+    # delete a service
+    ranger_client.delete_service(service_name)
+    log.info('Service {} successfully deleted'.format(service_name))
+
+    # delete a service definition
+    ranger_client.delete_service_def(service_def_name)
+    log.info('Service Definition {} successfully 
deleted'.format(service_def_name))
+

Reply via email to