You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2020/07/07 05:26:58 UTC

[cloudstack] branch master updated: server: Dynamic roles improvements. Add-on functionality below. (#4071)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 5040283  server: Dynamic roles improvements. Add-on functionality below. (#4071)
5040283 is described below

commit 5040283db9744175402b53ba9a6c701b9d8e3436
Author: sureshanaparti <12...@users.noreply.github.com>
AuthorDate: Tue Jul 7 10:56:43 2020 +0530

    server: Dynamic roles improvements. Add-on functionality below. (#4071)
    
    - Create a role from any of the existing role, using new parameter roleid in createRole API
    - Import a role with its rules, using a new importRole API
    - New default roles for Read-Only and Support Admin & User
    - No modifications allowed for Default roles
    - Cleaned up old NetApp APIs from role_permissions table.
---
 api/src/main/java/com/cloud/event/EventTypes.java  |   2 +
 .../main/java/org/apache/cloudstack/acl/Role.java  |   1 +
 .../org/apache/cloudstack/acl/RoleService.java     |   9 +-
 .../org/apache/cloudstack/api/ApiConstants.java    |   1 +
 .../apache/cloudstack/api/ApiServerService.java    |   1 +
 .../api/command/admin/acl/CreateRoleCmd.java       |  50 ++--
 .../api/command/admin/acl/ImportRoleCmd.java       | 149 +++++++++++
 .../api/command/admin/acl/ListRolesCmd.java        |   1 +
 .../cloudstack/api/command/admin/acl/RoleCmd.java  |  30 +++
 .../api/command/admin/acl/UpdateRoleCmd.java       |  15 --
 .../cloudstack/api/response/RoleResponse.java      |   8 +
 .../api/command/test/CreateRoleCmdTest.java        | 102 ++++++++
 .../api/command/test/ImportRoleCmdTest.java        | 131 ++++++++++
 .../com/cloud/upgrade/dao/Upgrade41400to41500.java | 286 +++++++++++++++++++++
 .../java/org/apache/cloudstack/acl/RoleVO.java     |  11 +
 .../org/apache/cloudstack/acl/dao/RoleDao.java     |   3 +
 .../org/apache/cloudstack/acl/dao/RoleDaoImpl.java |  22 ++
 .../cloudstack/acl/dao/RolePermissionsDao.java     |   8 +
 .../cloudstack/acl/dao/RolePermissionsDaoImpl.java |  14 +
 .../META-INF/db/schema-41400to41500-cleanup.sql    |   3 +
 .../resources/META-INF/db/schema-41400to41500.sql  |   9 +
 server/src/main/java/com/cloud/api/ApiServer.java  |  11 +
 .../org/apache/cloudstack/acl/RoleManagerImpl.java | 137 +++++++++-
 test/integration/smoke/test_dynamicroles.py        | 160 ++++++++++--
 tools/marvin/marvin/lib/base.py                    |  19 +-
 25 files changed, 1119 insertions(+), 64 deletions(-)

diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java
index 679c06a..5ea31a7 100644
--- a/api/src/main/java/com/cloud/event/EventTypes.java
+++ b/api/src/main/java/com/cloud/event/EventTypes.java
@@ -185,6 +185,7 @@ public class EventTypes {
     public static final String EVENT_ROLE_CREATE = "ROLE.CREATE";
     public static final String EVENT_ROLE_UPDATE = "ROLE.UPDATE";
     public static final String EVENT_ROLE_DELETE = "ROLE.DELETE";
+    public static final String EVENT_ROLE_IMPORT = "ROLE.IMPORT";
     public static final String EVENT_ROLE_PERMISSION_CREATE = "ROLE.PERMISSION.CREATE";
     public static final String EVENT_ROLE_PERMISSION_UPDATE = "ROLE.PERMISSION.UPDATE";
     public static final String EVENT_ROLE_PERMISSION_DELETE = "ROLE.PERMISSION.DELETE";
@@ -691,6 +692,7 @@ public class EventTypes {
         entityEventDetails.put(EVENT_ROLE_CREATE, Role.class);
         entityEventDetails.put(EVENT_ROLE_UPDATE, Role.class);
         entityEventDetails.put(EVENT_ROLE_DELETE, Role.class);
+        entityEventDetails.put(EVENT_ROLE_IMPORT, Role.class);
         entityEventDetails.put(EVENT_ROLE_PERMISSION_CREATE, RolePermission.class);
         entityEventDetails.put(EVENT_ROLE_PERMISSION_UPDATE, RolePermission.class);
         entityEventDetails.put(EVENT_ROLE_PERMISSION_DELETE, RolePermission.class);
diff --git a/api/src/main/java/org/apache/cloudstack/acl/Role.java b/api/src/main/java/org/apache/cloudstack/acl/Role.java
index b05d886..a31ec52 100644
--- a/api/src/main/java/org/apache/cloudstack/acl/Role.java
+++ b/api/src/main/java/org/apache/cloudstack/acl/Role.java
@@ -24,4 +24,5 @@ public interface Role extends InternalIdentity, Identity {
     String getName();
     RoleType getRoleType();
     String getDescription();
+    boolean isDefault();
 }
diff --git a/api/src/main/java/org/apache/cloudstack/acl/RoleService.java b/api/src/main/java/org/apache/cloudstack/acl/RoleService.java
index 6130c62..d7eef92 100644
--- a/api/src/main/java/org/apache/cloudstack/acl/RoleService.java
+++ b/api/src/main/java/org/apache/cloudstack/acl/RoleService.java
@@ -18,6 +18,7 @@
 package org.apache.cloudstack.acl;
 
 import java.util.List;
+import java.util.Map;
 
 import org.apache.cloudstack.acl.RolePermission.Permission;
 import org.apache.cloudstack.framework.config.ConfigKey;
@@ -39,13 +40,17 @@ public interface RoleService {
 
     Role createRole(String name, RoleType roleType, String description);
 
+    Role createRole(String name, Role role, String description);
+
+    Role importRole(String name, RoleType roleType, String description, List<Map<String, Object>> rules, boolean forced);
+
     Role updateRole(Role role, String name, RoleType roleType, String description);
 
     boolean deleteRole(Role role);
 
     RolePermission findRolePermission(Long id);
 
-    RolePermission findRolePermissionByUuid(String uuid);
+    RolePermission findRolePermissionByRoleIdAndRule(Long roleId, String rule);
 
     RolePermission createRolePermission(Role role, Rule rule, Permission permission, String description);
 
@@ -77,4 +82,6 @@ public interface RoleService {
     List<Role> findRolesByType(RoleType roleType);
 
     List<RolePermission> findAllPermissionsBy(Long roleId);
+
+    Permission getRolePermission(String permission);
 }
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index 8510554..073c940 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -478,6 +478,7 @@ public class ApiConstants {
     public static final String ROLE_NAME = "rolename";
     public static final String PERMISSION = "permission";
     public static final String RULE = "rule";
+    public static final String RULES = "rules";
     public static final String RULE_ID = "ruleid";
     public static final String RULE_ORDER = "ruleorder";
     public static final String USER = "user";
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java b/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java
index 382b48a..d00112b 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java
@@ -43,4 +43,5 @@ public interface ApiServerService {
 
     public Class<?> getCmdClass(String cmdName);
 
+    public boolean isValidApiName(String apiName);
 }
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java
index 87f0288..82cdcbd 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java
@@ -45,13 +45,9 @@ public class CreateRoleCmd extends RoleCmd {
             description = "creates a role with this unique name", validations = {ApiArgValidator.NotNullOrEmpty})
     private String roleName;
 
-    @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = true,
-            description = "The type of the role, valid options are: Admin, ResourceAdmin, DomainAdmin, User",
-            validations = {ApiArgValidator.NotNullOrEmpty})
-    private String roleType;
-
-    @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the role")
-    private String roleDescription;
+    @Parameter(name = ApiConstants.ROLE_ID, type = CommandType.UUID, entityType = RoleResponse.class,
+            description = "ID of the role to be cloned from. Either roleid or type must be passed in")
+    private Long roleId;
 
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
@@ -61,12 +57,8 @@ public class CreateRoleCmd extends RoleCmd {
         return roleName;
     }
 
-    public RoleType getRoleType() {
-        return RoleType.fromString(roleType);
-    }
-
-    public String getRoleDescription() {
-        return roleDescription;
+    public Long getRoleId() {
+        return roleId;
     }
 
     /////////////////////////////////////////////////////
@@ -85,11 +77,39 @@ public class CreateRoleCmd extends RoleCmd {
 
     @Override
     public void execute() {
-        CallContext.current().setEventDetails("Role: " + getRoleName() + ", type:" + getRoleType() + ", description: " + getRoleDescription());
-        final Role role = roleService.createRole(getRoleName(), getRoleType(), getRoleDescription());
+        validateRoleParameters();
+
+        Role role = null;
+        if (getRoleId() != null) {
+            Role existingRole = roleService.findRole(getRoleId());
+            if (existingRole == null) {
+                throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role id provided");
+            }
+
+            CallContext.current().setEventDetails("Role: " + getRoleName() + ", from role: " + getRoleId() + ", description: " + getRoleDescription());
+            role = roleService.createRole(getRoleName(), existingRole, getRoleDescription());
+        } else {
+            CallContext.current().setEventDetails("Role: " + getRoleName() + ", type: " + getRoleType() + ", description: " + getRoleDescription());
+            role = roleService.createRole(getRoleName(), getRoleType(), getRoleDescription());
+        }
+
         if (role == null) {
             throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create role");
         }
         setupResponse(role);
     }
+
+    private void validateRoleParameters() {
+        if (getRoleType() == null && getRoleId() == null) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Neither role type nor role ID is provided");
+        }
+
+        if (getRoleType() != null && getRoleId() != null) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Both role type and role ID should not be specified");
+        }
+
+        if (getRoleId() != null && getRoleId() < 1L) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role id provided");
+        }
+    }
 }
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ImportRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ImportRoleCmd.java
new file mode 100644
index 0000000..3afac9f
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ImportRoleCmd.java
@@ -0,0 +1,149 @@
+// 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.
+
+package org.apache.cloudstack.api.command.admin.acl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.Role;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.acl.Rule;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiArgValidator;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.ApiServerService;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.RoleResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.commons.collections.MapUtils;
+
+import com.cloud.user.Account;
+import com.google.common.base.Strings;
+
+@APICommand(name = ImportRoleCmd.APINAME, description = "Imports a role based on provided map of rule permissions", responseObject = RoleResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15.0",
+        authorized = {RoleType.Admin})
+public class ImportRoleCmd extends RoleCmd {
+    public static final String APINAME = "importRole";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true,
+            description = "Creates a role with this unique name", validations = {ApiArgValidator.NotNullOrEmpty})
+    private String roleName;
+
+    @Parameter(name = ApiConstants.RULES, type = CommandType.MAP, required = true,
+            description = "Rules param list, rule and permission is must. Example: rules[0].rule=create*&rules[0].permission=allow&rules[0].description=create%20rule&rules[1].rule=list*&rules[1].permission=allow&rules[1].description=listing")
+    private Map rules;
+
+    @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN,
+            description = "Force create a role with the same name. This overrides the role type, description and rule permissions for the existing role. Default is false.")
+    private Boolean forced;
+
+    @Inject
+    ApiServerService _apiServer;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getRoleName() {
+        return roleName;
+    }
+
+    // Returns list of rule maps. Each map corresponds to a rule with the details in the keys: rule, permission & description
+    public List<Map<String, Object>> getRules() {
+        if (MapUtils.isEmpty(rules)) {
+            return null;
+        }
+
+        List<Map<String, Object>> rulesDetails = new ArrayList<>();
+        Collection rulesCollection = rules.values();
+        Iterator iter = rulesCollection.iterator();
+        while (iter.hasNext()) {
+            HashMap<String, String> detail = (HashMap<String, String>)iter.next();
+            Map<String, Object> ruleDetails = new HashMap<>();
+            String rule = detail.get(ApiConstants.RULE);
+            if (Strings.isNullOrEmpty(rule)) {
+                throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Empty rule provided in rules param");
+            }
+            if (!rule.contains("*") && !_apiServer.isValidApiName(rule)) {
+                throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid api name: " + rule + " provided in rules param");
+            }
+            ruleDetails.put(ApiConstants.RULE, new Rule(rule));
+
+            String permission = detail.get(ApiConstants.PERMISSION);
+            if (Strings.isNullOrEmpty(permission)) {
+                throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid permission: "+ permission + " provided in rules param");
+            }
+            ruleDetails.put(ApiConstants.PERMISSION, roleService.getRolePermission(permission));
+
+            String description = detail.get(ApiConstants.DESCRIPTION);
+            if (!Strings.isNullOrEmpty(permission)) {
+                ruleDetails.put(ApiConstants.DESCRIPTION, description);
+            }
+
+            rulesDetails.add(ruleDetails);
+        }
+        return rulesDetails;
+    }
+
+    public boolean isForced() {
+        return (forced != null) ? forced : false;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+
+    @Override
+    public void execute() {
+        if (getRoleType() == null) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role type provided");
+        }
+
+        CallContext.current().setEventDetails("Role: " + getRoleName() + ", type: " + getRoleType() + ", description: " + getRoleDescription());
+        Role role = roleService.importRole(getRoleName(), getRoleType(), getRoleDescription(), getRules(), isForced());
+        if (role == null) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to import role");
+        }
+        setupResponse(role);
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java
index 9025e89..24b8a3e 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java
@@ -97,6 +97,7 @@ public class ListRolesCmd extends BaseCmd {
             roleResponse.setRoleName(role.getName());
             roleResponse.setRoleType(role.getRoleType());
             roleResponse.setDescription(role.getDescription());
+            roleResponse.setIsDefault(role.isDefault());
             roleResponse.setObjectName("role");
             roleResponses.add(roleResponse);
         }
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java
index 9054ff5..440278b 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java
@@ -18,11 +18,41 @@
 package org.apache.cloudstack.api.command.admin.acl;
 
 import org.apache.cloudstack.acl.Role;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
 import org.apache.cloudstack.api.response.RoleResponse;
 
+import com.google.common.base.Strings;
+
 public abstract class RoleCmd extends BaseCmd {
 
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "The type of the role, valid options are: Admin, ResourceAdmin, DomainAdmin, User")
+    private String roleType;
+
+    @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the role")
+    private String roleDescription;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public RoleType getRoleType() {
+        if (!Strings.isNullOrEmpty(roleType)) {
+            return RoleType.fromString(roleType);
+        }
+        return null;
+    }
+
+    public String getRoleDescription() {
+        return roleDescription;
+    }
+
     protected void setupResponse(final Role role) {
         final RoleResponse response = new RoleResponse();
         response.setId(role.getUuid());
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java
index f9519ae..0137d1e 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java
@@ -18,7 +18,6 @@
 package org.apache.cloudstack.api.command.admin.acl;
 
 import com.cloud.user.Account;
-import com.google.common.base.Strings;
 import org.apache.cloudstack.acl.Role;
 import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.api.APICommand;
@@ -49,9 +48,6 @@ public class UpdateRoleCmd extends RoleCmd {
     @Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, description = "creates a role with this unique name")
     private String roleName;
 
-    @Parameter(name = ApiConstants.TYPE, type = BaseCmd.CommandType.STRING, description = "The type of the role, valid options are: Admin, ResourceAdmin, DomainAdmin, User")
-    private String roleType;
-
     @Parameter(name = ApiConstants.DESCRIPTION, type = BaseCmd.CommandType.STRING, description = "The description of the role")
     private String roleDescription;
 
@@ -67,17 +63,6 @@ public class UpdateRoleCmd extends RoleCmd {
         return roleName;
     }
 
-    public RoleType getRoleType() {
-        if (!Strings.isNullOrEmpty(roleType)) {
-            return RoleType.fromString(roleType);
-        }
-        return null;
-    }
-
-    public String getRoleDescription() {
-        return roleDescription;
-    }
-
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RoleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RoleResponse.java
index fd4bf28..88154fa 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/RoleResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/RoleResponse.java
@@ -43,6 +43,10 @@ public class RoleResponse extends BaseResponse {
     @Param(description = "the description of the role")
     private String roleDescription;
 
+    @SerializedName(ApiConstants.IS_DEFAULT)
+    @Param(description = "true if role is default, false otherwise")
+    private Boolean isDefault;
+
     public void setId(String id) {
         this.id = id;
     }
@@ -60,4 +64,8 @@ public class RoleResponse extends BaseResponse {
     public void setDescription(String description) {
         this.roleDescription = description;
     }
+
+    public void setIsDefault(Boolean isDefault) {
+        this.isDefault = isDefault;
+    }
 }
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java
new file mode 100644
index 0000000..a910de7
--- /dev/null
+++ b/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java
@@ -0,0 +1,102 @@
+// 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.
+
+package org.apache.cloudstack.api.command.test;
+
+import org.apache.cloudstack.acl.Role;
+import org.apache.cloudstack.acl.RoleService;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.command.admin.acl.CreateRoleCmd;
+import org.apache.cloudstack.api.response.RoleResponse;
+import org.apache.cloudstack.api.ServerApiException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static org.mockito.Mockito.when;
+
+public class CreateRoleCmdTest {
+    private CreateRoleCmd createRoleCmd;
+    private RoleService roleService;
+    private Role role;
+
+    @Before
+    public void setUp() {
+        roleService = Mockito.spy(RoleService.class);
+        createRoleCmd = new CreateRoleCmd();
+        ReflectionTestUtils.setField(createRoleCmd,"roleService",roleService);
+        ReflectionTestUtils.setField(createRoleCmd,"roleName","testuser");
+        ReflectionTestUtils.setField(createRoleCmd,"roleDescription","User test");
+        role = Mockito.mock(Role.class);
+    }
+
+    @Test
+    public void testCreateRoleWithRoleType() {
+        ReflectionTestUtils.setField(createRoleCmd,"roleType", "User");
+        when(role.getId()).thenReturn(1L);
+        when(role.getUuid()).thenReturn("12345-abcgdkajd");
+        when(role.getDescription()).thenReturn("User test");
+        when(role.getName()).thenReturn("testuser");
+        when(role.getRoleType()).thenReturn(RoleType.User);
+        when(roleService.createRole(createRoleCmd.getRoleName(), createRoleCmd.getRoleType(), createRoleCmd.getRoleDescription())).thenReturn(role);
+        createRoleCmd.execute();
+        RoleResponse response = (RoleResponse) createRoleCmd.getResponseObject();
+        Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleName"), role.getName());
+        Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleDescription"), role.getDescription());
+    }
+
+    @Test
+    public void testCreateRoleWithExistingRole() {
+        ReflectionTestUtils.setField(createRoleCmd,"roleId",1L);
+        when(roleService.findRole(createRoleCmd.getRoleId())).thenReturn(role);
+        Role newRole = Mockito.mock(Role.class);
+        when(newRole.getId()).thenReturn(2L);
+        when(newRole.getUuid()).thenReturn("67890-xyztestid");
+        when(newRole.getDescription()).thenReturn("User test");
+        when(newRole.getName()).thenReturn("testuser");
+        when(newRole.getRoleType()).thenReturn(RoleType.User);
+        when(roleService.createRole(createRoleCmd.getRoleName(), role, createRoleCmd.getRoleDescription())).thenReturn(newRole);
+        createRoleCmd.execute();
+        RoleResponse response = (RoleResponse) createRoleCmd.getResponseObject();
+        Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleName"), newRole.getName());
+        Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleDescription"), newRole.getDescription());
+    }
+
+    @Test(expected = ServerApiException.class)
+    public void testCreateRoleWithNonExistingRole() {
+        ReflectionTestUtils.setField(createRoleCmd,"roleId",1L);
+        when(roleService.findRole(createRoleCmd.getRoleId())).thenReturn(null);
+        createRoleCmd.execute();
+        Assert.fail("An exception should have been thrown: " + ServerApiException.class);
+    }
+
+    @Test(expected = ServerApiException.class)
+    public void testCreateRoleValidateNeitherRoleIdNorTypeParameters() {
+        createRoleCmd.execute();
+        Assert.fail("An exception should have been thrown: " + ServerApiException.class);
+    }
+
+    @Test(expected = ServerApiException.class)
+    public void testCreateRoleValidateBothRoleIdAndTypeParameters() {
+        ReflectionTestUtils.setField(createRoleCmd,"roleId",1L);
+        ReflectionTestUtils.setField(createRoleCmd,"roleType", "User");
+        createRoleCmd.execute();
+        Assert.fail("An exception should have been thrown: " + ServerApiException.class);
+    }
+}
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java
new file mode 100644
index 0000000..8de0148
--- /dev/null
+++ b/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java
@@ -0,0 +1,131 @@
+// 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.
+
+package org.apache.cloudstack.api.command.test;
+
+import org.apache.cloudstack.acl.Role;
+import org.apache.cloudstack.acl.RoleService;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.command.admin.acl.ImportRoleCmd;
+import org.apache.cloudstack.api.response.RoleResponse;
+import org.apache.cloudstack.api.ServerApiException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyCollection;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.cloud.exception.InvalidParameterValueException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+
+public class ImportRoleCmdTest {
+    private ImportRoleCmd importRoleCmd;
+    private RoleService roleService;
+    private Role role;
+
+    @Before
+    public void setUp() {
+        roleService = Mockito.spy(RoleService.class);
+        importRoleCmd = new ImportRoleCmd();
+        ReflectionTestUtils.setField(importRoleCmd,"roleService",roleService);
+        ReflectionTestUtils.setField(importRoleCmd,"roleName","Test User");
+        ReflectionTestUtils.setField(importRoleCmd,"roleType", "User");
+        ReflectionTestUtils.setField(importRoleCmd,"roleDescription","test user imported");
+        role = Mockito.mock(Role.class);
+    }
+
+    @Test
+    public void testImportRoleSuccess() {
+        Map<String, Map<String, String>> rules = new HashMap<String, Map<String, String>>();
+
+        //Rule 1
+        Map<String, String> rule1 = new HashMap<String, String>();
+        rule1.put(ApiConstants.RULE, "list*");
+        rule1.put(ApiConstants.PERMISSION, "allow");
+        rule1.put(ApiConstants.DESCRIPTION, "listing apis");
+        rules.put("key1", rule1);
+
+        //Rule 2
+        Map<String, String> rule2 = new HashMap<String, String>();
+        rule2.put(ApiConstants.RULE, "update*");
+        rule2.put(ApiConstants.PERMISSION, "deny");
+        rule2.put(ApiConstants.DESCRIPTION, "no update allowed");
+        rules.put("key2", rule2);
+
+        //Rule 3
+        Map<String, String> rule3 = new HashMap<String, String>();
+        rule3.put(ApiConstants.RULE, "get*");
+        rule3.put(ApiConstants.PERMISSION, "allow");
+        rule3.put(ApiConstants.DESCRIPTION, "get details");
+        rules.put("key3", rule3);
+
+        ReflectionTestUtils.setField(importRoleCmd,"rules",rules);
+
+        when(role.getUuid()).thenReturn("12345-abcgdkajd");
+        when(role.getDescription()).thenReturn("test user imported");
+        when(role.getName()).thenReturn("Test User");
+        when(role.getRoleType()).thenReturn(RoleType.User);
+        when(roleService.importRole(anyString(),any(), anyString(), any(), anyBoolean())).thenReturn(role);
+
+        importRoleCmd.execute();
+        RoleResponse response = (RoleResponse) importRoleCmd.getResponseObject();
+        Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleName"), role.getName());
+        Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleDescription"), role.getDescription());
+    }
+
+    @Test(expected = InvalidParameterValueException.class)
+    public void testImportRoleInvalidRule() {
+        Map<String, Map<String, String>> rules = new HashMap<String, Map<String, String>>();
+        Map<String, String> rule = new HashMap<String, String>();
+        rule.put(ApiConstants.RULE, "*?+test*");
+        rule.put(ApiConstants.PERMISSION, "allow");
+        rule.put(ApiConstants.DESCRIPTION, "listing apis");
+        rules.put("key1", rule);
+        ReflectionTestUtils.setField(importRoleCmd,"rules",rules);
+
+        importRoleCmd.execute();
+        Assert.fail("An exception should have been thrown: " + InvalidParameterValueException.class);
+    }
+
+    @Test(expected = ServerApiException.class)
+    public void testImportRoleInvalidPermission() {
+        Map<String, Map<String, String>> rules = new HashMap<String, Map<String, String>>();
+        Map<String, String> rule = new HashMap<String, String>();
+        rule.put(ApiConstants.RULE, "list*");
+        rule.put(ApiConstants.PERMISSION, "pass");
+        rule.put(ApiConstants.DESCRIPTION, "listing apis");
+        rules.put("key1", rule);
+        ReflectionTestUtils.setField(importRoleCmd,"rules",rules);
+
+        importRoleCmd.execute();
+        Assert.fail("An exception should have been thrown: " + ServerApiException.class);
+    }
+}
diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41400to41500.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41400to41500.java
index 13c67d9..e3fe602 100644
--- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41400to41500.java
+++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41400to41500.java
@@ -22,8 +22,11 @@ import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -65,6 +68,7 @@ public class Upgrade41400to41500 implements DbUpgrade {
     @Override
     public void performDataMigration(Connection conn) {
         updateSystemVmTemplates(conn);
+        addRolePermissionsForNewReadOnlyAndSupportRoles(conn);
     }
 
     @SuppressWarnings("serial")
@@ -235,6 +239,288 @@ public class Upgrade41400to41500 implements DbUpgrade {
         LOG.debug("Updating System Vm Template IDs Complete");
     }
 
+    private void addRolePermissionsForNewReadOnlyAndSupportRoles(final Connection conn) {
+        addRolePermissionsForReadOnlyAdmin(conn);
+        addRolePermissionsForReadOnlyUser(conn);
+        addRolePermissionsForSupportAdmin(conn);
+        addRolePermissionsForSupportUser(conn);
+    }
+
+    private void addRolePermissionsForReadOnlyAdmin(final Connection conn) {
+        LOG.debug("Adding role permissions for new read-only admin role");
+        try {
+            PreparedStatement pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`roles` WHERE name = 'Read-Only Admin - Default' AND is_default = 1");
+            ResultSet rs = pstmt.executeQuery();
+            if (rs.next()) {
+                long readOnlyAdminRoleId = rs.getLong(1);
+                int readOnlyAdminSortOrder = 0;
+                Map<String, String> readOnlyAdminRules = new LinkedHashMap<>();
+                readOnlyAdminRules.put("list*", "ALLOW");
+                readOnlyAdminRules.put("getUploadParamsFor*", "DENY");
+                readOnlyAdminRules.put("get*", "ALLOW");
+                readOnlyAdminRules.put("cloudianIsEnabled", "ALLOW");
+                readOnlyAdminRules.put("queryAsyncJobResult", "ALLOW");
+                readOnlyAdminRules.put("quotaIsEnabled", "ALLOW");
+                readOnlyAdminRules.put("quotaTariffList", "ALLOW");
+                readOnlyAdminRules.put("quotaSummary", "ALLOW");
+                readOnlyAdminRules.put("*", "DENY");
+
+                for (Map.Entry<String, String> readOnlyAdminRule : readOnlyAdminRules.entrySet()) {
+                    pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, ?, ?) ON DUPLICATE KEY UPDATE rule=rule");
+                    pstmt.setLong(1, readOnlyAdminRoleId);
+                    pstmt.setString(2, readOnlyAdminRule.getKey());
+                    pstmt.setString(3, readOnlyAdminRule.getValue());
+                    pstmt.setLong(4, readOnlyAdminSortOrder++);
+                    pstmt.executeUpdate();
+                }
+            }
+
+            if (rs != null && !rs.isClosed())  {
+                rs.close();
+            }
+            if (pstmt != null && !pstmt.isClosed())  {
+                pstmt.close();
+            }
+            LOG.debug("Successfully added role permissions for new read-only admin role");
+        } catch (final SQLException e) {
+            LOG.error("Exception while adding role permissions for read-only admin role: " + e.getMessage());
+            throw new CloudRuntimeException("Exception while adding role permissions for read-only admin role: " + e.getMessage(), e);
+        }
+    }
+
+    private void addRolePermissionsForReadOnlyUser(final Connection conn) {
+        LOG.debug("Adding role permissions for new read-only user role");
+        try {
+            PreparedStatement pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`roles` WHERE name = 'Read-Only User - Default' AND is_default = 1");
+            ResultSet rs = pstmt.executeQuery();
+            if (rs.next()) {
+                long readOnlyUserRoleId = rs.getLong(1);
+                int readOnlyUserSortOrder = 0;
+
+                pstmt = conn.prepareStatement("SELECT rule FROM `cloud`.`role_permissions` WHERE role_id = 4 AND permission = 'ALLOW' AND rule LIKE 'list%' ORDER BY sort_order");
+                ResultSet rsRolePermissions = pstmt.executeQuery();
+
+                while (rsRolePermissions.next()) {
+                    String rule = rsRolePermissions.getString(1);
+                    pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, 'ALLOW', ?) ON DUPLICATE KEY UPDATE rule=rule");
+                    pstmt.setLong(1, readOnlyUserRoleId);
+                    pstmt.setString(2, rule);
+                    pstmt.setLong(3, readOnlyUserSortOrder++);
+                    pstmt.executeUpdate();
+                }
+
+                pstmt = conn.prepareStatement("SELECT rule FROM `cloud`.`role_permissions` WHERE role_id = 4 AND permission = 'ALLOW' AND rule LIKE 'get%' AND rule NOT LIKE 'getUploadParamsFor%' ORDER BY sort_order");
+                rsRolePermissions = pstmt.executeQuery();
+
+                while (rsRolePermissions.next()) {
+                    String rule = rsRolePermissions.getString(1);
+                    pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, 'ALLOW', ?) ON DUPLICATE KEY UPDATE rule=rule");
+                    pstmt.setLong(1, readOnlyUserRoleId);
+                    pstmt.setString(2, rule);
+                    pstmt.setLong(3, readOnlyUserSortOrder++);
+                    pstmt.executeUpdate();
+                }
+
+                List<String> readOnlyUserRulesAllowed = new ArrayList<>();
+                readOnlyUserRulesAllowed.add("cloudianIsEnabled");
+                readOnlyUserRulesAllowed.add("queryAsyncJobResult");
+                readOnlyUserRulesAllowed.add("quotaIsEnabled");
+                readOnlyUserRulesAllowed.add("quotaTariffList");
+                readOnlyUserRulesAllowed.add("quotaSummary");
+
+                for(String readOnlyUserRule : readOnlyUserRulesAllowed) {
+                    pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, 'ALLOW', ?) ON DUPLICATE KEY UPDATE rule=rule");
+                    pstmt.setLong(1, readOnlyUserRoleId);
+                    pstmt.setString(2, readOnlyUserRule);
+                    pstmt.setLong(3, readOnlyUserSortOrder++);
+                    pstmt.executeUpdate();
+                }
+
+                pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, '*', 'DENY', ?) ON DUPLICATE KEY UPDATE rule=rule");
+                pstmt.setLong(1, readOnlyUserRoleId);
+                pstmt.setLong(2, readOnlyUserSortOrder);
+                pstmt.executeUpdate();
+
+                if (rsRolePermissions != null && !rsRolePermissions.isClosed())  {
+                    rsRolePermissions.close();
+                }
+            }
+
+            if (rs != null && !rs.isClosed())  {
+                rs.close();
+            }
+            if (pstmt != null && !pstmt.isClosed())  {
+                pstmt.close();
+            }
+            LOG.debug("Successfully added role permissions for new read-only user role");
+        } catch (final SQLException e) {
+            LOG.error("Exception while adding role permissions for read-only user role: " + e.getMessage());
+            throw new CloudRuntimeException("Exception while adding role permissions for read-only user role: " + e.getMessage(), e);
+        }
+    }
+
+    private void addRolePermissionsForSupportAdmin(final Connection conn) {
+        LOG.debug("Adding role permissions for new support admin role");
+        try {
+            PreparedStatement pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`roles` WHERE name = 'Support Admin - Default' AND is_default = 1");
+            ResultSet rs = pstmt.executeQuery();
+            if (rs.next()) {
+                long supportAdminRoleId = rs.getLong(1);
+                int supportAdminSortOrder = 0;
+
+                pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`roles` WHERE name = 'Read-Only Admin - Default' AND is_default = 1");
+                ResultSet rsReadOnlyAdmin = pstmt.executeQuery();
+                if (rsReadOnlyAdmin.next()) {
+                    long readOnlyAdminRoleId = rsReadOnlyAdmin.getLong(1);
+                    pstmt = conn.prepareStatement("SELECT rule FROM `cloud`.`role_permissions` WHERE role_id = ? AND permission = 'ALLOW' ORDER BY sort_order");
+                    pstmt.setLong(1, readOnlyAdminRoleId);
+                    ResultSet rsRolePermissions = pstmt.executeQuery();
+
+                    while (rsRolePermissions.next()) {
+                        String rule = rsRolePermissions.getString(1);
+                        pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, 'ALLOW', ?) ON DUPLICATE KEY UPDATE rule=rule");
+                        pstmt.setLong(1, supportAdminRoleId);
+                        pstmt.setString(2, rule);
+                        pstmt.setLong(3, supportAdminSortOrder++);
+                        pstmt.executeUpdate();
+                    }
+
+                    List<String> supportAdminRulesAllowed = new ArrayList<>();
+                    supportAdminRulesAllowed.add("prepareHostForMaintenance");
+                    supportAdminRulesAllowed.add("cancelHostMaintenance");
+                    supportAdminRulesAllowed.add("enableStorageMaintenance");
+                    supportAdminRulesAllowed.add("cancelStorageMaintenance");
+                    supportAdminRulesAllowed.add("createServiceOffering");
+                    supportAdminRulesAllowed.add("createDiskOffering");
+                    supportAdminRulesAllowed.add("createNetworkOffering");
+                    supportAdminRulesAllowed.add("createVPCOffering");
+                    supportAdminRulesAllowed.add("startVirtualMachine");
+                    supportAdminRulesAllowed.add("stopVirtualMachine");
+                    supportAdminRulesAllowed.add("rebootVirtualMachine");
+                    supportAdminRulesAllowed.add("startKubernetesCluster");
+                    supportAdminRulesAllowed.add("stopKubernetesCluster");
+                    supportAdminRulesAllowed.add("createVolume");
+                    supportAdminRulesAllowed.add("attachVolume");
+                    supportAdminRulesAllowed.add("detachVolume");
+                    supportAdminRulesAllowed.add("uploadVolume");
+                    supportAdminRulesAllowed.add("attachIso");
+                    supportAdminRulesAllowed.add("detachIso");
+                    supportAdminRulesAllowed.add("registerTemplate");
+                    supportAdminRulesAllowed.add("registerIso");
+
+                    for(String supportAdminRule : supportAdminRulesAllowed) {
+                        pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, 'ALLOW', ?) ON DUPLICATE KEY UPDATE rule=rule");
+                        pstmt.setLong(1, supportAdminRoleId);
+                        pstmt.setString(2, supportAdminRule);
+                        pstmt.setLong(3, supportAdminSortOrder++);
+                        pstmt.executeUpdate();
+                    }
+
+                    pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, '*', 'DENY', ?) ON DUPLICATE KEY UPDATE rule=rule");
+                    pstmt.setLong(1, supportAdminRoleId);
+                    pstmt.setLong(2, supportAdminSortOrder);
+                    pstmt.executeUpdate();
+
+                    if (rsRolePermissions != null && !rsRolePermissions.isClosed())  {
+                        rsRolePermissions.close();
+                    }
+                }
+
+                if (rsReadOnlyAdmin != null && !rsReadOnlyAdmin.isClosed())  {
+                    rsReadOnlyAdmin.close();
+                }
+            }
+
+            if (rs != null && !rs.isClosed())  {
+                rs.close();
+            }
+            if (pstmt != null && !pstmt.isClosed())  {
+                pstmt.close();
+            }
+            LOG.debug("Successfully added role permissions for new support admin role");
+        } catch (final SQLException e) {
+            LOG.error("Exception while adding role permissions for support admin role: " + e.getMessage());
+            throw new CloudRuntimeException("Exception while adding role permissions for support admin role: " + e.getMessage(), e);
+        }
+    }
+
+    private void addRolePermissionsForSupportUser(final Connection conn) {
+        LOG.debug("Adding role permissions for new support user role");
+        try {
+            PreparedStatement pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`roles` WHERE name = 'Support User - Default' AND is_default = 1");
+            ResultSet rs = pstmt.executeQuery();
+            if (rs.next()) {
+                long supportUserRoleId = rs.getLong(1);
+                int supportUserSortOrder = 0;
+
+                pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`roles` WHERE name = 'Read-Only User - Default' AND is_default = 1");
+                ResultSet rsReadOnlyUser = pstmt.executeQuery();
+                if (rsReadOnlyUser.next()) {
+                    long readOnlyUserRoleId = rsReadOnlyUser.getLong(1);
+                    pstmt = conn.prepareStatement("SELECT rule FROM `cloud`.`role_permissions` WHERE role_id = ? AND permission = 'ALLOW' ORDER BY sort_order");
+                    pstmt.setLong(1, readOnlyUserRoleId);
+                    ResultSet rsRolePermissions = pstmt.executeQuery();
+                    while (rsRolePermissions.next()) {
+                        String rule = rsRolePermissions.getString(1);
+                        pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, 'ALLOW', ?) ON DUPLICATE KEY UPDATE rule=rule");
+                        pstmt.setLong(1, supportUserRoleId);
+                        pstmt.setString(2, rule);
+                        pstmt.setLong(3, supportUserSortOrder++);
+                        pstmt.executeUpdate();
+                    }
+
+                    List<String> supportUserRulesAllowed = new ArrayList<>();
+                    supportUserRulesAllowed.add("startVirtualMachine");
+                    supportUserRulesAllowed.add("stopVirtualMachine");
+                    supportUserRulesAllowed.add("rebootVirtualMachine");
+                    supportUserRulesAllowed.add("startKubernetesCluster");
+                    supportUserRulesAllowed.add("stopKubernetesCluster");
+                    supportUserRulesAllowed.add("createVolume");
+                    supportUserRulesAllowed.add("attachVolume");
+                    supportUserRulesAllowed.add("detachVolume");
+                    supportUserRulesAllowed.add("uploadVolume");
+                    supportUserRulesAllowed.add("attachIso");
+                    supportUserRulesAllowed.add("detachIso");
+                    supportUserRulesAllowed.add("registerTemplate");
+                    supportUserRulesAllowed.add("registerIso");
+                    supportUserRulesAllowed.add("getUploadParamsFor*");
+
+                    for(String supportUserRule : supportUserRulesAllowed) {
+                        pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, 'ALLOW', ?) ON DUPLICATE KEY UPDATE rule=rule");
+                        pstmt.setLong(1, supportUserRoleId);
+                        pstmt.setString(2, supportUserRule);
+                        pstmt.setLong(3, supportUserSortOrder++);
+                        pstmt.executeUpdate();
+                    }
+
+                    pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, '*', 'DENY', ?) ON DUPLICATE KEY UPDATE rule=rule");
+                    pstmt.setLong(1, supportUserRoleId);
+                    pstmt.setLong(2, supportUserSortOrder);
+                    pstmt.executeUpdate();
+
+                    if (rsRolePermissions != null && !rsRolePermissions.isClosed())  {
+                        rsRolePermissions.close();
+                    }
+                }
+
+                if (rsReadOnlyUser != null && !rsReadOnlyUser.isClosed())  {
+                    rsReadOnlyUser.close();
+                }
+            }
+
+            if (rs != null && !rs.isClosed())  {
+                rs.close();
+            }
+            if (pstmt != null && !pstmt.isClosed())  {
+                pstmt.close();
+            }
+            LOG.debug("Successfully added role permissions for new support user role");
+        } catch (final SQLException e) {
+            LOG.error("Exception while adding role permissions for support user role: " + e.getMessage());
+            throw new CloudRuntimeException("Exception while adding role permissions for support user role: " + e.getMessage(), e);
+        }
+    }
+
     @Override
     public InputStream[] getCleanupScripts() {
         final String scriptFile = "META-INF/db/schema-41400to41500-cleanup.sql";
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java
index f3404ab..823dc69 100644
--- a/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java
@@ -51,6 +51,9 @@ public class RoleVO implements Role {
     @Column(name = "description")
     private String description;
 
+    @Column(name = "is_default")
+    private boolean isDefault = false;
+
     @Column(name = GenericDao.REMOVED_COLUMN)
     private Date removed;
 
@@ -75,6 +78,10 @@ public class RoleVO implements Role {
         return uuid;
     }
 
+    public void setUuid(String uuid) {
+        this.uuid = uuid;
+    }
+
     @Override
     public long getId() {
         return id;
@@ -103,4 +110,8 @@ public class RoleVO implements Role {
     public void setDescription(String description) {
         this.description = description;
     }
+
+    public boolean isDefault() {
+        return isDefault;
+    }
 }
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java
index e53654d..4a53965 100644
--- a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java
@@ -18,6 +18,7 @@
 package org.apache.cloudstack.acl.dao;
 
 import com.cloud.utils.db.GenericDao;
+
 import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.acl.RoleVO;
 
@@ -26,4 +27,6 @@ import java.util.List;
 public interface RoleDao extends GenericDao<RoleVO, Long> {
     List<RoleVO> findAllByName(String roleName);
     List<RoleVO> findAllByRoleType(RoleType type);
+    List<RoleVO> findByName(String roleName);
+    RoleVO findByNameAndType(String roleName, RoleType type);
 }
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java
index e3dd266..4dc11fc 100644
--- a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java
@@ -20,6 +20,7 @@ package org.apache.cloudstack.acl.dao;
 import com.cloud.utils.db.GenericDaoBase;
 import com.cloud.utils.db.SearchBuilder;
 import com.cloud.utils.db.SearchCriteria;
+
 import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.acl.RoleVO;
 import org.springframework.stereotype.Component;
@@ -30,6 +31,7 @@ import java.util.List;
 public class RoleDaoImpl extends GenericDaoBase<RoleVO, Long> implements RoleDao {
     private final SearchBuilder<RoleVO> RoleByNameSearch;
     private final SearchBuilder<RoleVO> RoleByTypeSearch;
+    private final SearchBuilder<RoleVO> RoleByNameAndTypeSearch;
 
     public RoleDaoImpl() {
         super();
@@ -41,6 +43,11 @@ public class RoleDaoImpl extends GenericDaoBase<RoleVO, Long> implements RoleDao
         RoleByTypeSearch = createSearchBuilder();
         RoleByTypeSearch.and("roleType", RoleByTypeSearch.entity().getRoleType(), SearchCriteria.Op.EQ);
         RoleByTypeSearch.done();
+
+        RoleByNameAndTypeSearch = createSearchBuilder();
+        RoleByNameAndTypeSearch.and("roleName", RoleByNameAndTypeSearch.entity().getName(), SearchCriteria.Op.EQ);
+        RoleByNameAndTypeSearch.and("roleType", RoleByNameAndTypeSearch.entity().getRoleType(), SearchCriteria.Op.EQ);
+        RoleByNameAndTypeSearch.done();
     }
 
     @Override
@@ -56,4 +63,19 @@ public class RoleDaoImpl extends GenericDaoBase<RoleVO, Long> implements RoleDao
         sc.setParameters("roleType", type);
         return listBy(sc);
     }
+
+    @Override
+    public List<RoleVO> findByName(String roleName) {
+        SearchCriteria<RoleVO> sc = RoleByNameSearch.create();
+        sc.setParameters("roleName", roleName);
+        return listBy(sc);
+    }
+
+    @Override
+    public RoleVO findByNameAndType(String roleName, RoleType type) {
+        SearchCriteria<RoleVO> sc = RoleByNameAndTypeSearch.create();
+        sc.setParameters("roleName", roleName);
+        sc.setParameters("roleType", type);
+        return findOneBy(sc);
+    }
 }
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RolePermissionsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RolePermissionsDao.java
index c9aeba1..dec1251 100644
--- a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RolePermissionsDao.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RolePermissionsDao.java
@@ -56,4 +56,12 @@ public interface RolePermissionsDao extends GenericDao<RolePermissionVO, Long> {
      * @return returns list of role permissions
      */
     List<RolePermissionVO> findAllByRoleIdSorted(Long roleId);
+
+    /**
+     * Returns role permission for a given role and rule
+     * @param roleId the ID of the role
+     * @param roleId rule for the role
+     * @return returns role permission
+     */
+    RolePermissionVO findByRoleIdAndRule(Long roleId, String rule);
 }
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RolePermissionsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RolePermissionsDaoImpl.java
index 68b6abf..5b4f7a7 100644
--- a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RolePermissionsDaoImpl.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RolePermissionsDaoImpl.java
@@ -44,12 +44,18 @@ import java.util.Set;
 public class RolePermissionsDaoImpl extends GenericDaoBase<RolePermissionVO, Long> implements RolePermissionsDao {
     protected static final Logger LOGGER = Logger.getLogger(RolePermissionsDaoImpl.class);
 
+    private final SearchBuilder<RolePermissionVO> RolePermissionsSearchByRoleAndRule;
     private final SearchBuilder<RolePermissionVO> RolePermissionsSearch;
     private Attribute sortOrderAttribute;
 
     public RolePermissionsDaoImpl() {
         super();
 
+        RolePermissionsSearchByRoleAndRule = createSearchBuilder();
+        RolePermissionsSearchByRoleAndRule.and("roleId", RolePermissionsSearchByRoleAndRule.entity().getRoleId(), SearchCriteria.Op.EQ);
+        RolePermissionsSearchByRoleAndRule.and("rule", RolePermissionsSearchByRoleAndRule.entity().getRule(), SearchCriteria.Op.EQ);
+        RolePermissionsSearchByRoleAndRule.done();
+
         RolePermissionsSearch = createSearchBuilder();
         RolePermissionsSearch.and("uuid", RolePermissionsSearch.entity().getUuid(), SearchCriteria.Op.EQ);
         RolePermissionsSearch.and("roleId", RolePermissionsSearch.entity().getRoleId(), SearchCriteria.Op.EQ);
@@ -174,4 +180,12 @@ public class RolePermissionsDaoImpl extends GenericDaoBase<RolePermissionVO, Lon
         }
         return rolePermissionList;
     }
+
+    @Override
+    public RolePermissionVO findByRoleIdAndRule(final Long roleId, final String rule) {
+        final SearchCriteria<RolePermissionVO> sc = RolePermissionsSearchByRoleAndRule.create();
+        sc.setParameters("roleId", roleId);
+        sc.setParameters("rule", rule);
+        return findOneBy(sc);
+    }
 }
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41400to41500-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-41400to41500-cleanup.sql
index fe04728..e9df02a 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41400to41500-cleanup.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41400to41500-cleanup.sql
@@ -18,3 +18,6 @@
 --;
 -- Schema upgrade cleanup from 4.14.0.0 to 4.15.0.0
 --;
+
+-- remove the old NetApp storage APIs (unsupported since 4.12) from role_permissions
+DELETE from `cloud`.`role_permissions` WHERE rule IN ('createPool', 'modifyPool', 'deletePool', 'listPools', 'associateLun', 'dissociateLun', 'createLunOnFiler', 'destroyLunOnFiler', 'listLunsOnFiler', 'createVolumeOnFiler', 'destroyVolumeOnFiler', 'listVolumesOnFiler');
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41400to41500.sql b/engine/schema/src/main/resources/META-INF/db/schema-41400to41500.sql
index 5ec50cd..c8d280a 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41400to41500.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41400to41500.sql
@@ -18,3 +18,12 @@
 --;
 -- Schema upgrade from 4.14.0.0 to 4.15.0.0
 --;
+
+ALTER TABLE `cloud`.`roles` ADD COLUMN `is_default` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'is this a default role';
+UPDATE `cloud`.`roles` SET `is_default` = 1 WHERE id IN (1, 2, 3, 4);
+
+-- Updated Default CloudStack roles with read-only and support admin and user roles
+INSERT INTO `cloud`.`roles` (`uuid`, `name`, `role_type`, `description`, `is_default`) VALUES (UUID(), 'Read-Only Admin - Default', 'Admin', 'Default read-only admin role', 1);
+INSERT INTO `cloud`.`roles` (`uuid`, `name`, `role_type`, `description`, `is_default`) VALUES (UUID(), 'Read-Only User - Default', 'User', 'Default read-only user role', 1);
+INSERT INTO `cloud`.`roles` (`uuid`, `name`, `role_type`, `description`, `is_default`) VALUES (UUID(), 'Support Admin - Default', 'Admin', 'Default support admin role', 1);
+INSERT INTO `cloud`.`roles` (`uuid`, `name`, `role_type`, `description`, `is_default`) VALUES (UUID(), 'Support User - Default', 'User', 'Default support user role', 1);
diff --git a/server/src/main/java/com/cloud/api/ApiServer.java b/server/src/main/java/com/cloud/api/ApiServer.java
index 7d096b8..a0a71d0 100644
--- a/server/src/main/java/com/cloud/api/ApiServer.java
+++ b/server/src/main/java/com/cloud/api/ApiServer.java
@@ -1204,6 +1204,17 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
         }
     }
 
+    @Override
+    public boolean isValidApiName(String apiName) {
+        if (apiName == null || apiName.isEmpty())
+            return false;
+
+        if (!s_apiNameCmdClassMap.containsKey(apiName))
+            return false;
+
+        return true;
+    }
+
     // FIXME: rather than isError, we might was to pass in the status code to give more flexibility
     private void writeResponse(final HttpResponse resp, final String responseText, final int statusCode, final String responseType, final String reasonPhrase) {
         try {
diff --git a/server/src/main/java/org/apache/cloudstack/acl/RoleManagerImpl.java b/server/src/main/java/org/apache/cloudstack/acl/RoleManagerImpl.java
index ae471b2..a51680c 100644
--- a/server/src/main/java/org/apache/cloudstack/acl/RoleManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/acl/RoleManagerImpl.java
@@ -20,17 +20,20 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import javax.inject.Inject;
 
 import org.apache.cloudstack.acl.dao.RoleDao;
 import org.apache.cloudstack.acl.dao.RolePermissionsDao;
+import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.ApiErrorCode;
 import org.apache.cloudstack.api.ServerApiException;
 import org.apache.cloudstack.api.command.admin.acl.CreateRoleCmd;
 import org.apache.cloudstack.api.command.admin.acl.CreateRolePermissionCmd;
 import org.apache.cloudstack.api.command.admin.acl.DeleteRoleCmd;
 import org.apache.cloudstack.api.command.admin.acl.DeleteRolePermissionCmd;
+import org.apache.cloudstack.api.command.admin.acl.ImportRoleCmd;
 import org.apache.cloudstack.api.command.admin.acl.ListRolePermissionsCmd;
 import org.apache.cloudstack.api.command.admin.acl.ListRolesCmd;
 import org.apache.cloudstack.api.command.admin.acl.UpdateRoleCmd;
@@ -54,6 +57,7 @@ import com.cloud.utils.component.PluggableService;
 import com.cloud.utils.db.Transaction;
 import com.cloud.utils.db.TransactionCallback;
 import com.cloud.utils.db.TransactionStatus;
+import com.cloud.utils.exception.CloudRuntimeException;
 import com.google.common.base.Strings;
 
 public class RoleManagerImpl extends ManagerBase implements RoleService, Configurable, PluggableService {
@@ -124,11 +128,12 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
     }
 
     @Override
-    public RolePermission findRolePermissionByUuid(final String uuid) {
-        if (Strings.isNullOrEmpty(uuid)) {
+    public RolePermission findRolePermissionByRoleIdAndRule(final Long roleId, final String rule) {
+        if (roleId == null || Strings.isNullOrEmpty(rule)) {
             return null;
         }
-        return rolePermissionsDao.findByUuid(uuid);
+
+        return rolePermissionsDao.findByRoleIdAndRule(roleId, rule);
     }
 
     @Override
@@ -147,9 +152,98 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
     }
 
     @Override
+    @ActionEvent(eventType = EventTypes.EVENT_ROLE_CREATE, eventDescription = "creating role by cloning another role")
+    public Role createRole(String name, Role role, String description) {
+        checkCallerAccess();
+        return Transaction.execute(new TransactionCallback<RoleVO>() {
+            @Override
+            public RoleVO doInTransaction(TransactionStatus status) {
+                RoleVO newRoleVO = roleDao.persist(new RoleVO(name, role.getRoleType(), description));
+                if (newRoleVO == null) {
+                    throw new CloudRuntimeException("Unable to create the role: " + name + ", failed to persist in DB");
+                }
+
+                List<RolePermissionVO> rolePermissions = rolePermissionsDao.findAllByRoleIdSorted(role.getId());
+                if (rolePermissions != null && !rolePermissions.isEmpty()) {
+                    for (RolePermissionVO permission : rolePermissions) {
+                        rolePermissionsDao.persist(new RolePermissionVO(newRoleVO.getId(), permission.getRule().toString(), permission.getPermission(), permission.getDescription()));
+                    }
+                }
+
+                return newRoleVO;
+            }
+        });
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_ROLE_IMPORT, eventDescription = "importing Role")
+    public Role importRole(String name, RoleType type, String description, List<Map<String, Object>> rules, boolean forced) {
+        checkCallerAccess();
+        if (Strings.isNullOrEmpty(name)) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role name provided");
+        }
+        if (type == null || type == RoleType.Unknown) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role type provided");
+        }
+
+        List<RoleVO> existingRoles = roleDao.findByName(name);
+        if (CollectionUtils.isNotEmpty(existingRoles) && !forced) {
+            throw new CloudRuntimeException("Role already exists");
+        }
+
+        return Transaction.execute(new TransactionCallback<RoleVO>() {
+            @Override
+            public RoleVO doInTransaction(TransactionStatus status) {
+                RoleVO newRole = null;
+                RoleVO existingRole = roleDao.findByNameAndType(name, type);
+                if (existingRole != null) {
+                    if (existingRole.isDefault()) {
+                        throw new CloudRuntimeException("Failed to import the role: " + name + ", default role cannot be overriden");
+                    }
+
+                    //Cleanup old role permissions
+                    List<? extends RolePermission> rolePermissions = rolePermissionsDao.findAllByRoleIdSorted(existingRole.getId());
+                    if (rolePermissions != null && !rolePermissions.isEmpty()) {
+                        for (RolePermission rolePermission : rolePermissions) {
+                            rolePermissionsDao.remove(rolePermission.getId());
+                        }
+                    }
+
+                    existingRole.setName(name);
+                    existingRole.setRoleType(type);
+                    existingRole.setDescription(description);
+                    roleDao.update(existingRole.getId(), existingRole);
+
+                    newRole = existingRole;
+                } else {
+                    newRole = roleDao.persist(new RoleVO(name, type, description));
+                }
+
+                if (newRole == null) {
+                    throw new CloudRuntimeException("Unable to import the role: " + name + ", failed to persist in DB");
+                }
+
+                if (rules != null && !rules.isEmpty()) {
+                    for (Map<String, Object> ruleDetail : rules) {
+                        Rule rule = (Rule)ruleDetail.get(ApiConstants.RULE);
+                        RolePermission.Permission rulePermission = (RolePermission.Permission) ruleDetail.get(ApiConstants.PERMISSION);
+                        String ruleDescription = (String) ruleDetail.get(ApiConstants.DESCRIPTION);
+
+                        rolePermissionsDao.persist(new RolePermissionVO(newRole.getId(), rule.toString(), rulePermission, ruleDescription));
+                    }
+                }
+                return newRole;
+            }
+        });
+    }
+
+    @Override
     @ActionEvent(eventType = EventTypes.EVENT_ROLE_UPDATE, eventDescription = "updating Role")
     public Role updateRole(final Role role, final String name, final RoleType roleType, final String description) {
         checkCallerAccess();
+        if (role.isDefault()) {
+            throw new PermissionDeniedException("Default roles cannot be updated");
+        }
 
         if (roleType != null && roleType == RoleType.Unknown) {
             throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unknown is not a valid role type");
@@ -159,9 +253,6 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
             roleVO.setName(name);
         }
         if (roleType != null) {
-            if (role.getId() <= RoleType.User.getId()) {
-                throw new PermissionDeniedException("The role type of default roles cannot be changed");
-            }
             List<? extends Account> accounts = accountDao.findAccountsByRole(role.getId());
             if (accounts == null || accounts.isEmpty()) {
                 roleVO.setRoleType(roleType);
@@ -184,7 +275,7 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
         if (role == null) {
             return false;
         }
-        if (role.getId() <= RoleType.User.getId()) {
+        if (role.isDefault()) {
             throw new PermissionDeniedException("Default roles cannot be deleted");
         }
         List<? extends Account> accounts = accountDao.findAccountsByRole(role.getId());
@@ -214,6 +305,14 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
     @ActionEvent(eventType = EventTypes.EVENT_ROLE_PERMISSION_CREATE, eventDescription = "creating Role Permission")
     public RolePermission createRolePermission(final Role role, final Rule rule, final RolePermission.Permission permission, final String description) {
         checkCallerAccess();
+        if (role.isDefault()) {
+            throw new PermissionDeniedException("Role permission cannot be added for Default roles");
+        }
+
+        if (findRolePermissionByRoleIdAndRule(role.getId(), rule.toString()) != null) {
+            throw new PermissionDeniedException("Rule already exists for the role: " + role.getName());
+        }
+
         return Transaction.execute(new TransactionCallback<RolePermissionVO>() {
             @Override
             public RolePermissionVO doInTransaction(TransactionStatus status) {
@@ -226,12 +325,18 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
     @ActionEvent(eventType = EventTypes.EVENT_ROLE_PERMISSION_UPDATE, eventDescription = "updating Role Permission order")
     public boolean updateRolePermission(final Role role, final List<RolePermission> newOrder) {
         checkCallerAccess();
+        if (role.isDefault()) {
+            throw new PermissionDeniedException("Role permission cannot be updated for Default roles");
+        }
         return role != null && newOrder != null && rolePermissionsDao.update(role, newOrder);
     }
 
     @Override
     public boolean updateRolePermission(Role role, RolePermission rolePermission, RolePermission.Permission permission) {
         checkCallerAccess();
+        if (role.isDefault()) {
+            throw new PermissionDeniedException("Role permission cannot be updated for Default roles");
+        }
         return role != null && rolePermissionsDao.update(role, rolePermission, permission);
     }
 
@@ -239,6 +344,10 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
     @ActionEvent(eventType = EventTypes.EVENT_ROLE_PERMISSION_DELETE, eventDescription = "deleting Role Permission")
     public boolean deleteRolePermission(final RolePermission rolePermission) {
         checkCallerAccess();
+        Role role = findRole(rolePermission.getRoleId());
+        if (role.isDefault()) {
+            throw new PermissionDeniedException("Role permission cannot be deleted for Default roles");
+        }
         return rolePermission != null && rolePermissionsDao.remove(rolePermission.getId());
     }
 
@@ -277,7 +386,6 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
                 rolesIterator.remove();
             }
         }
-
     }
 
     @Override
@@ -306,6 +414,18 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
     }
 
     @Override
+    public RolePermission.Permission getRolePermission(String permission) {
+        if (Strings.isNullOrEmpty(permission)) {
+            return null;
+        }
+        if (!permission.equalsIgnoreCase(RolePermission.Permission.ALLOW.toString()) &&
+                !permission.equalsIgnoreCase(RolePermission.Permission.DENY.toString())) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Values for permission parameter should be: allow or deny");
+        }
+        return permission.equalsIgnoreCase(RolePermission.Permission.ALLOW.toString()) ? RolePermission.Permission.ALLOW : RolePermission.Permission.DENY;
+    }
+
+    @Override
     public String getConfigComponentName() {
         return RoleService.class.getSimpleName();
     }
@@ -319,6 +439,7 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
     public List<Class<?>> getCommands() {
         final List<Class<?>> cmdList = new ArrayList<>();
         cmdList.add(CreateRoleCmd.class);
+        cmdList.add(ImportRoleCmd.class);
         cmdList.add(ListRolesCmd.class);
         cmdList.add(UpdateRoleCmd.class);
         cmdList.add(DeleteRoleCmd.class);
diff --git a/test/integration/smoke/test_dynamicroles.py b/test/integration/smoke/test_dynamicroles.py
index 61e1d19..2ce7001 100644
--- a/test/integration/smoke/test_dynamicroles.py
+++ b/test/integration/smoke/test_dynamicroles.py
@@ -45,6 +45,14 @@ class TestData(object):
                 "type": "User",
                 "description": "Fake Role created by Marvin test"
             },
+            "importrole": {
+                "name": "MarvinFake Import Role ",
+                "type": "User",
+                "description": "Fake Import User Role created by Marvin test",
+                "rules" : [{"rule":"list*", "permission":"allow","description":"Listing apis"},
+                           {"rule":"get*", "permission":"allow","description":"Get apis"},
+                           {"rule":"update*", "permission":"deny","description":"Update apis"}]
+            },
             "roleadmin": {
                 "name": "MarvinFake Admin Role ",
                 "type": "Admin",
@@ -201,6 +209,91 @@ class TestDynamicRoles(cloudstackTestCase):
             msg="Role type does not match the test data"
         )
 
+    @attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
+    def test_role_lifecycle_clone(self):
+        """
+            Tests create role from existing role
+        """
+        # Use self.role created in setUp()
+        role_to_be_cloned = {
+            "name": "MarvinFake Clone Role ",
+            "roleid": self.role.id,
+            "description": "Fake Role cloned by Marvin test"
+        }
+
+        try:
+            role_cloned = Role.create(
+                self.apiclient,
+                role_to_be_cloned
+            )
+            self.cleanup.append(role_cloned)
+        except CloudstackAPIException as e:
+            self.fail("Failed to create the role: %s" % e)
+
+        list_role_cloned= Role.list(self.apiclient, id=role_cloned.id)
+        self.assertEqual(
+            isinstance(list_role_cloned, list),
+            True,
+            "List Roles response was not a valid list"
+        )
+        self.assertEqual(
+            len(list_role_cloned),
+            1,
+            "List Roles response size was not 1"
+        )
+        self.assertEqual(
+            list_role_cloned[0].name,
+            role_to_be_cloned["name"],
+            msg="Role name does not match the test data"
+        )
+        self.assertEqual(
+            list_role_cloned[0].type,
+            self.testdata["role"]["type"],
+            msg="Role type does not match the test data"
+        )
+
+        list_rolepermissions = RolePermission.list(self.apiclient, roleid=self.role.id)
+        self.validate_permissions_list(list_rolepermissions, role_cloned.id)
+
+    @attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
+    def test_role_lifecycle_import(self):
+        """
+            Tests import role with the rules
+        """
+        # use importrole from testdata
+        self.testdata["importrole"]["name"] += self.getRandomString()
+        try:
+            role_imported = Role.importRole(
+                self.apiclient,
+                self.testdata["importrole"]
+            )
+            self.cleanup.append(role_imported)
+        except CloudstackAPIException as e:
+            self.fail("Failed to import the role: %s" % e)
+
+        list_role_imported = Role.list(self.apiclient, id=role_imported.id)
+        self.assertEqual(
+            isinstance(list_role_imported, list),
+            True,
+            "List Roles response was not a valid list"
+        )
+        self.assertEqual(
+            len(list_role_imported),
+            1,
+            "List Roles response size was not 1"
+        )
+        self.assertEqual(
+            list_role_imported[0].name,
+            self.testdata["importrole"]["name"],
+            msg="Role name does not match the test data"
+        )
+        self.assertEqual(
+            list_role_imported[0].type,
+            self.testdata["importrole"]["type"],
+            msg="Role type does not match the test data"
+        )
+
+        self.validate_permissions_dict(self.testdata["importrole"]["rules"], role_imported.id)
 
     @attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
     def test_role_lifecycle_update(self):
@@ -360,44 +453,23 @@ class TestDynamicRoles(cloudstackTestCase):
             self.cleanup.append(permission)
             permissions.append(permission)
 
-
-        def validate_permissions_list(permissions):
-            list_rolepermissions = RolePermission.list(self.apiclient, roleid=self.role.id)
-            self.assertEqual(
-                len(list_rolepermissions),
-                len(permissions),
-                msg="List of role permissions do not match created list of permissions"
-            )
-
-            for idx, rolepermission in enumerate(list_rolepermissions):
-                self.assertEqual(
-                    rolepermission.rule,
-                    permissions[idx].rule,
-                    msg="Rule permission don't match with expected item at the index"
-                )
-                self.assertEqual(
-                    rolepermission.permission,
-                    permissions[idx].permission,
-                    msg="Rule permission don't match with expected item at the index"
-                )
-
         # Move last item to the top
         rule = permissions.pop(len(permissions)-1)
         permissions = [rule] + permissions
         rule.update(self.apiclient, ruleorder=",".join(map(lambda x: x.id, permissions)))
-        validate_permissions_list(permissions)
+        self.validate_permissions_list(permissions, self.role.id)
 
         # Move to the bottom
         rule = permissions.pop(0)
         permissions = permissions + [rule]
         rule.update(self.apiclient, ruleorder=",".join(map(lambda x: x.id, permissions)))
-        validate_permissions_list(permissions)
+        self.validate_permissions_list(permissions, self.role.id)
 
         # Random shuffles
         for _ in range(3):
             shuffle(permissions)
             rule.update(self.apiclient, ruleorder=",".join(map(lambda x: x.id, permissions)))
-            validate_permissions_list(permissions)
+            self.validate_permissions_list(permissions, self.role.id)
 
     @attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
     def test_rolepermission_lifecycle_update_permission(self):
@@ -580,3 +652,43 @@ class TestDynamicRoles(cloudstackTestCase):
 
         # Perform actual API call for allow API
         self.checkApiCall(apiConfig, userApiClient)
+
+    def validate_permissions_list(self, permissions, roleid):
+        list_rolepermissions = RolePermission.list(self.apiclient, roleid=roleid)
+        self.assertEqual(
+            len(list_rolepermissions),
+            len(permissions),
+            msg="List of role permissions do not match created list of permissions"
+        )
+
+        for idx, rolepermission in enumerate(list_rolepermissions):
+            self.assertEqual(
+                rolepermission.rule,
+                permissions[idx].rule,
+                msg="Rule permission don't match with expected item at the index"
+            )
+            self.assertEqual(
+                rolepermission.permission,
+                permissions[idx].permission,
+                msg="Rule permission don't match with expected item at the index"
+            )
+
+    def validate_permissions_dict(self, permissions, roleid):
+        list_rolepermissions = RolePermission.list(self.apiclient, roleid=roleid)
+        self.assertEqual(
+            len(list_rolepermissions),
+            len(permissions),
+            msg="List of role permissions do not match created list of permissions"
+        )
+
+        for idx, rolepermission in enumerate(list_rolepermissions):
+            self.assertEqual(
+                rolepermission.rule,
+                permissions[idx]["rule"],
+                msg="Rule permission don't match with expected item at the index"
+            )
+            self.assertEqual(
+                rolepermission.permission,
+                permissions[idx]["permission"],
+                msg="Rule permission don't match with expected item at the index"
+            )
diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py
index 7ed52df..231f31a 100755
--- a/tools/marvin/marvin/lib/base.py
+++ b/tools/marvin/marvin/lib/base.py
@@ -102,12 +102,29 @@ class Role:
         """Create role"""
         cmd = createRole.createRoleCmd()
         cmd.name = services["name"]
-        cmd.type = services["type"]
+        if "type" in services:
+            cmd.type = services["type"]
+        if "roleid" in services:
+            cmd.roleid = services["roleid"]
         if "description" in services:
             cmd.description = services["description"]
 
         return Role(apiclient.createRole(cmd).__dict__)
 
+    @classmethod
+    def importRole(cls, apiclient, services, domainid=None):
+        """Import role"""
+        cmd = importRole.importRoleCmd()
+        cmd.name = services["name"]
+        cmd.type = services["type"]
+        cmd.rules = services["rules"]
+        if "description" in services:
+            cmd.description = services["description"]
+        if "forced" in services:
+            cmd.type = services["forced"]
+
+        return Role(apiclient.importRole(cmd).__dict__)
+
     def delete(self, apiclient):
         """Delete Role"""