1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-27 10:50:10 +03:00

Merge branch 'feature-3541'

This commit is contained in:
Ruben S. Montero 2015-02-04 15:45:11 +01:00
commit aff2965106
25 changed files with 1229 additions and 184 deletions

View File

@ -75,6 +75,26 @@ public:
return del_collection_id(id);
}
/**
* Adds a User to the admin set. ACL Rules are updated only for this user.
*
* @param user_id ID of the user
* @param error_msg Returns the error reason, if any
*
* @return 0 on success
*/
int add_admin(int user_id, string& error_msg);
/**
* Deletes a User from the admin set. ACL Rules are updated only for this user.
*
* @param user_id ID of the user
* @param error_msg Returns the error reason, if any
*
* @return 0 on success
*/
int del_admin(int user_id, string& error_msg);
/**
* Object quotas, provides set and check interface
*/
@ -113,7 +133,8 @@ private:
Group(int id, const string& name):
PoolObjectSQL(id,GROUP,name,-1,-1,"","",table),
ObjectCollection("USERS"),
quota()
quota(),
admins("ADMINS")
{
// Allow users in this group to see it
group_u = 1;
@ -126,6 +147,18 @@ private:
delete obj_template;
};
// *************************************************************************
// Administrators
// *************************************************************************
/**
* Stores a collection with the admin users
*/
ObjectCollection admins;
void add_admin_rules(int user_id);
void del_admin_rules(int user_id);
// *************************************************************************
// DataBase implementation (Private)
// *************************************************************************

View File

@ -85,6 +85,16 @@ public:
return set<int> (collection_set);
};
/**
* Returns true if the collection contains the given id
* @param id ID to search
* @return true if the collection contains the given id
*/
bool collection_contains(int id)
{
return collection_set.count(id) > 0;
}
private:
/**

View File

@ -66,6 +66,66 @@ public:
RequestAttributes& att);
};
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
class GroupEditAdmin : public Request
{
public:
void request_execute(xmlrpc_c::paramList const& _paramList,
RequestAttributes& att);
protected:
GroupEditAdmin( const string& method_name,
const string& help,
const string& params)
:Request(method_name,params,help)
{
Nebula& nd = Nebula::instance();
pool = nd.get_gpool();
upool = nd.get_upool();
auth_object = PoolObjectSQL::GROUP;
auth_op = AuthRequest::ADMIN;
};
UserPool* upool;
virtual int edit_admin(Group* group, int user_id, string& error_msg) = 0;
};
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
class GroupAddAdmin : public GroupEditAdmin
{
public:
GroupAddAdmin():
GroupEditAdmin( "GroupAddAdmin",
"Adds a user to the group admin set",
"A:sii"){};
~GroupAddAdmin(){};
int edit_admin(Group* group, int user_id, string& error_msg);
};
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
class GroupDelAdmin : public GroupEditAdmin
{
public:
GroupDelAdmin():
GroupEditAdmin( "GroupDelAdmin",
"Removes a user from the group admin set",
"A:sii"){};
~GroupDelAdmin(){};
int edit_admin(Group* group, int user_id, string& error_msg);
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

View File

@ -14,6 +14,13 @@
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="ADMINS">
<xs:complexType>
<xs:sequence>
<xs:element name="ID" type="xs:integer" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="DATASTORE_QUOTA" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>

View File

@ -18,6 +18,13 @@
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="ADMINS">
<xs:complexType>
<xs:sequence>
<xs:element name="ID" type="xs:integer" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>

View File

@ -169,15 +169,6 @@ EOT
"Which resources can be created by group users "<<
"(VM+NET+IMAGE+TEMPLATE by default)",
:format => String
},
{
:name => 'admin_resources',
:large => '--admin_resources res_str',
:short => "-o",
:description =>
"Which resources can be created by the admin user "<<
"(VM+NET+IMAGE+TEMPLATE by default)",
:format => String
}
]

View File

@ -286,11 +286,21 @@ class OneGroupHelper < OpenNebulaHelper::OneHelper
puts group.template_str
puts
CLIHelper.print_header(str_h1 % "USERS", false)
CLIHelper.print_header("%-15s" % ["ID"])
group.user_ids.each do |uid|
puts "%-15s" % [uid]
end
admin_ids = group.admin_ids
CLIHelper::ShowTable.new(nil, self) do
column :"USER ID", "", :right, :size=>7 do |d|
d
end
column :"ADMIN", "", :left, :size=>5 do |d|
if (group.admin_ids.include?(d))
"*"
else
""
end
end
end.show(group.user_ids, {})
group_hash = group.to_hash

View File

@ -61,24 +61,26 @@ cmd=CommandParser::CmdParser.new(ARGV) do
helper.list_to_id(arg)
end
set :format, :userid, OpenNebulaHelper.rname_to_id_desc("USER") do |arg|
OpenNebulaHelper.rname_to_id(arg, "USER")
end
########################################################################
# Commands
########################################################################
create_desc = <<-EOT.unindent
Creates a new Group. A group name can be passed as the only argument,
or a file containing a group template or via command line arguments
or via command line arguments
Examples:
- create a group with admin user and allow group users
to only create new templates and VMs, and group admin to manage
templates, vms, images and virtual networks
- create a group with an admin user and allow group users
to only create new templates and VMs
onegroup create --name groupA
--admin_user admin_userA --admin_password somestr
--resources TEMPLATE+VM
--admin_resources TEMPLATE+VM+IMAGE+NET
EOT
@ -87,7 +89,8 @@ cmd=CommandParser::CmdParser.new(ARGV) do
if !args[0] && options.empty?
STDERR.puts "Please use either a group name or command arguments"
STDERR.puts "Please use either a group name or command arguments."<<
" Run onegroup create -h for more information"
next -1
end
@ -101,7 +104,6 @@ cmd=CommandParser::CmdParser.new(ARGV) do
admin_user[:name] = options[:admin_user]
admin_user[:password] = options[:admin_password]
admin_user[:auth_driver]= options[:admin_driver] if options[:admin_driver]
admin_user[:resources] = options[:admin_resources] if options[:admin_resources]
options[:group_admin] = admin_user
end
@ -159,6 +161,26 @@ cmd=CommandParser::CmdParser.new(ARGV) do
helper.show_resource(group,options)
end
add_admin_desc = <<-EOT.unindent
Adds a User to the Group administrators set
EOT
command :addadmin, add_admin_desc, [:range, :groupid_list], :userid do
helper.perform_actions(args[0],options,"admin added") do |obj|
obj.add_admin(args[1].to_i)
end
end
del_admin_desc = <<-EOT.unindent
Removes a User from the Group administrators set
EOT
command :deladmin, del_admin_desc, [:range, :groupid_list], :userid do
helper.perform_actions(args[0],options,"admin deleted") do |obj|
obj.del_admin(args[1].to_i)
end
end
quota_desc = <<-EOT.unindent
Set the quota limits for the group. If a path is not provided the
editor will be launched to modify the current quotas.

View File

@ -214,6 +214,7 @@ string& Group::to_xml_extended(string& xml, bool extended) const
{
ostringstream oss;
string collection_xml;
string admins_xml;
string template_xml;
ObjectCollection::to_xml(collection_xml);
@ -223,7 +224,8 @@ string& Group::to_xml_extended(string& xml, bool extended) const
"<ID>" << oid << "</ID>" <<
"<NAME>" << name << "</NAME>" <<
obj_template->to_xml(template_xml) <<
collection_xml;
collection_xml <<
admins.to_xml(admins_xml);
if (extended)
{
@ -276,6 +278,19 @@ int Group::from_xml(const string& xml)
ObjectXML::free_nodes(content);
content.clear();
// Set of Admin IDs
ObjectXML::get_nodes("/GROUP/ADMINS", content);
if (content.empty())
{
return -1;
}
rc += admins.from_xml_node(content[0]);
ObjectXML::free_nodes(content);
content.clear();
// Get associated metadata for the group
ObjectXML::get_nodes("/GROUP/TEMPLATE", content);
@ -296,3 +311,181 @@ int Group::from_xml(const string& xml)
return 0;
}
/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */
int Group::add_admin(int user_id, string& error_msg)
{
int rc;
ostringstream oss;
if ( collection_contains(user_id) == false )
{
oss << "User " << user_id << " is not part of Group "
<< oid << ".";
error_msg = oss.str();
return -1;
}
rc = admins.add_collection_id(user_id);
if (rc == -1)
{
oss << "User " << user_id << " is already an administrator of Group "
<< oid << ".";
error_msg = oss.str();
return -1;
}
add_admin_rules(user_id);
return 0;
}
/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */
void Group::add_admin_rules(int user_id)
{
int rc;
string error_msg;
AclManager* aclm = Nebula::instance().get_aclm();
// #<uid> USER/@<gid> USE+MANAGE+ADMIN+CREATE *
rc = aclm->add_rule(
AclRule::INDIVIDUAL_ID |
user_id,
PoolObjectSQL::USER |
AclRule::GROUP_ID |
oid,
AuthRequest::USE |
AuthRequest::MANAGE |
AuthRequest::ADMIN |
AuthRequest::CREATE,
AclRule::ALL_ID,
error_msg);
if (rc < 0)
{
NebulaLog::log("GROUP",Log::ERROR,error_msg);
}
// #<uid> VM+NET+IMAGE+TEMPLATE+DOCUMENT+SECGROUP/@<gid> USE+MANAGE *
rc = aclm->add_rule(
AclRule::INDIVIDUAL_ID |
user_id,
PoolObjectSQL::VM |
PoolObjectSQL::NET |
PoolObjectSQL::IMAGE |
PoolObjectSQL::TEMPLATE |
PoolObjectSQL::DOCUMENT |
PoolObjectSQL::SECGROUP |
AclRule::GROUP_ID |
oid,
AuthRequest::USE |
AuthRequest::MANAGE,
AclRule::ALL_ID,
error_msg);
if (rc < 0)
{
NebulaLog::log("GROUP",Log::ERROR,error_msg);
}
}
/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */
int Group::del_admin(int user_id, string& error_msg)
{
int rc = admins.del_collection_id(user_id);
if (rc == -1)
{
ostringstream oss;
oss << "User " << user_id << " is not an administrator of Group "
<< oid << ".";
error_msg = oss.str();
return -1;
}
del_admin_rules(user_id);
return 0;
}
/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */
void Group::del_admin_rules(int user_id)
{
int rc;
string error_msg;
AclManager* aclm = Nebula::instance().get_aclm();
// #<uid> USER/@<gid> USE+MANAGE+ADMIN+CREATE *
rc = aclm->del_rule(
AclRule::INDIVIDUAL_ID |
user_id,
PoolObjectSQL::USER |
AclRule::GROUP_ID |
oid,
AuthRequest::USE |
AuthRequest::MANAGE |
AuthRequest::ADMIN |
AuthRequest::CREATE,
AclRule::ALL_ID,
error_msg);
if (rc < 0)
{
NebulaLog::log("GROUP",Log::ERROR,error_msg);
}
// #<uid> VM+NET+IMAGE+TEMPLATE+DOCUMENT+SECGROUP/@<gid> USE+MANAGE *
rc = aclm->del_rule(
AclRule::INDIVIDUAL_ID |
user_id,
PoolObjectSQL::VM |
PoolObjectSQL::NET |
PoolObjectSQL::IMAGE |
PoolObjectSQL::TEMPLATE |
PoolObjectSQL::DOCUMENT |
PoolObjectSQL::SECGROUP |
AclRule::GROUP_ID |
oid,
AuthRequest::USE |
AuthRequest::MANAGE,
AclRule::ALL_ID,
error_msg);
if (rc < 0)
{
NebulaLog::log("GROUP",Log::ERROR,error_msg);
}
}

View File

@ -33,6 +33,8 @@ public class Group extends PoolElement{
private static final String DELETE = METHOD_PREFIX + "delete";
private static final String QUOTA = METHOD_PREFIX + "quota";
private static final String UPDATE = METHOD_PREFIX + "update";
private static final String ADD_ADMIN = METHOD_PREFIX + "addadmin";
private static final String DEL_ADMIN = METHOD_PREFIX + "deladmin";
/**
* Creates a new Group representation.
@ -124,6 +126,32 @@ public class Group extends PoolElement{
return client.call(UPDATE, id, new_template, append ? 1 : 0);
}
/**
* Adds a User to the Group administrators set
*
* @param client XML-RPC Client.
* @param id The group id of the target group we want to modify.
* @param uid User ID
* @return If successful the message contains the group id.
*/
public static OneResponse addAdmin(Client client, int id, int uid)
{
return client.call(ADD_ADMIN, id, uid);
}
/**
* Removes a User from the Group administrators set
*
* @param client XML-RPC Client.
* @param id The group id of the target group we want to modify.
* @param uid User ID
* @return If successful the message contains the group id.
*/
public static OneResponse delAdmin(Client client, int id, int uid)
{
return client.call(DEL_ADMIN, id, uid);
}
// =================================
// Instanced object XML-RPC methods
// =================================
@ -185,6 +213,28 @@ public class Group extends PoolElement{
return update(client, id, new_template, append);
}
/**
* Adds a User to the Group administrators set
*
* @param uid User ID
* @return If successful the message contains the group id.
*/
public OneResponse addAdmin(int uid)
{
return addAdmin(client, id, uid);
}
/**
* Removes a User from the Group administrators set
*
* @param uid User ID
* @return If successful the message contains the group id.
*/
public OneResponse delAdmin(int uid)
{
return delAdmin(client, id, uid);
}
// =================================
// Helpers
// =================================
@ -200,4 +250,16 @@ public class Group extends PoolElement{
String res = xpath("USERS/ID[.="+uid+"]");
return res != null && res.equals(""+uid);
}
/**
* Returns whether or not the user is an admin of this group
*
* @param uid The user ID.
* @return Whether or not the user is an admin of this group
*/
public boolean containsAdmin(int uid)
{
String res = xpath("ADMINS/ID[.="+uid+"]");
return res != null && res.equals(""+uid);
}
}

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
@ -29,13 +29,16 @@ import org.opennebula.client.OneResponse;
import org.opennebula.client.OneSystem;
import org.opennebula.client.group.Group;
import org.opennebula.client.group.GroupPool;
import org.opennebula.client.user.*;
import org.w3c.dom.Node;
import java.util.Hashtable;
public class GroupTest
{
private static Group group;
private static GroupPool groupPool;
private static User user;
private static Client client;
@ -69,8 +72,17 @@ public class GroupTest
{
res = Group.allocate(client, group_name);
assertTrue( res.getErrorMessage(), !res.isError() );
int group_id = res.isError() ? -1 : Integer.parseInt(res.getMessage());
group = new Group(group_id, client);
res = User.allocate(client, "new_test_user", "new_test_password");
assertTrue( res.getErrorMessage(), !res.isError() );
int uid = Integer.parseInt(res.getMessage());
user = new User(uid, client);
}
/**
@ -79,6 +91,7 @@ public class GroupTest
@After
public void tearDown() throws Exception
{
user.delete();
group.delete();
}
@ -160,8 +173,68 @@ public class GroupTest
}
}
// Commented out, secondary groups do not exist any more
/*
@Test
public void addAdmin()
{
res = group.info();
assertTrue( res.getErrorMessage(), !res.isError() );
assertFalse( group.contains(user.id()) );
assertFalse( group.containsAdmin(user.id()) );
res = group.addAdmin( user.id() );
assertTrue( res.isError() );
group.info();
assertFalse( group.contains(user.id()) );
assertFalse( group.containsAdmin(user.id()) );
res = user.chgrp( group.id() );
assertTrue( res.getErrorMessage(), !res.isError() );
group.info();
assertTrue( group.contains(user.id()) );
assertFalse( group.containsAdmin(user.id()) );
res = group.addAdmin( user.id() );
assertTrue( res.getErrorMessage(), !res.isError() );
group.info();
assertTrue( group.contains(user.id()) );
assertTrue( group.containsAdmin(user.id()) );
}
@Test
public void delAdmin()
{
res = group.info();
assertTrue( res.getErrorMessage(), !res.isError() );
res = group.delAdmin( user.id() );
assertTrue( res.isError() );
res = user.chgrp( group.id() );
assertTrue( res.getErrorMessage(), !res.isError() );
res = group.delAdmin( user.id() );
assertTrue( res.isError() );
res = group.addAdmin( user.id() );
assertTrue( res.getErrorMessage(), !res.isError() );
group.info();
assertTrue( group.contains(user.id()) );
assertTrue( group.containsAdmin(user.id()) );
res = group.delAdmin( user.id() );
assertTrue( res.getErrorMessage(), !res.isError() );
group.info();
assertTrue( group.contains(user.id()) );
assertFalse( group.containsAdmin(user.id()) );
}
@Test
public void userGroupRelations()
{
@ -211,26 +284,6 @@ public class GroupTest
assertTrue( !g.info().isError() );
}
assertTrue( users.get("a").isPartOf( groups.get("a").id() ) );
assertTrue( users.get("a").isPartOf( groups.get("b").id() ) );
assertFalse( users.get("a").isPartOf( groups.get("c").id() ) );
assertFalse( users.get("a").isPartOf( groups.get("d").id() ) );
assertFalse( users.get("b").isPartOf( groups.get("a").id() ) );
assertTrue( users.get("b").isPartOf( groups.get("b").id() ) );
assertFalse( users.get("b").isPartOf( groups.get("c").id() ) );
assertFalse( users.get("b").isPartOf( groups.get("d").id() ) );
assertFalse( users.get("c").isPartOf( groups.get("a").id() ) );
assertTrue( users.get("c").isPartOf( groups.get("b").id() ) );
assertTrue( users.get("c").isPartOf( groups.get("c").id() ) );
assertTrue( users.get("c").isPartOf( groups.get("d").id() ) );
assertFalse( users.get("d").isPartOf( groups.get("a").id() ) );
assertTrue( users.get("d").isPartOf( groups.get("b").id() ) );
assertTrue( users.get("d").isPartOf( groups.get("c").id() ) );
assertTrue( users.get("d").isPartOf( groups.get("d").id() ) );
assertTrue( groups.get("a").contains( users.get("a").id() ) );
assertFalse( groups.get("a").contains( users.get("b").id() ) );
assertFalse( groups.get("a").contains( users.get("c").id() ) );
@ -250,5 +303,4 @@ public class GroupTest
assertTrue( groups.get("d").contains( users.get("c").id() ) );
assertTrue( groups.get("d").contains( users.get("d").id() ) );
}
*/
}

View File

@ -28,7 +28,9 @@ module OpenNebula
:allocate => "group.allocate",
:update => "group.update",
:delete => "group.delete",
:quota => "group.quota"
:quota => "group.quota",
:add_admin => "group.addadmin",
:del_admin => "group.deladmin",
}
# Flag for requesting connected user's group info
@ -83,6 +85,10 @@ module OpenNebula
# in SUNSTONE_VIEWS
# group_hash[:default_view] Default sunstone view name, to be stored
# in DEFAULT_VIEW
# group_hash[:admin_views] Array of sunstone view names, to be stored
# in GROUP_ADMIN_VIEWS
# group_hash[:default_admin_view] Default sunstone view name, to be stored
# in DEFAULT_ADMIN_DEFAULT_VIEW
#
def create(group_hash)
# Check arguments
@ -134,24 +140,31 @@ module OpenNebula
end
str = ""
update = false
# Add Sunstone views for the group
if group_hash[:views]
str += "SUNSTONE_VIEWS=\"#{group_hash[:views].join(",")}\"\n"
update = true
end
# Add Sunstone views for the group
if group_hash[:default_view]
str += "DEFAULT_VIEW=\"#{group_hash[:default_view]}\"\n"
update = true
end
if update
self.update(str, true)
# And the admin views
if group_hash[:admin_views]
str += "GROUP_ADMIN_VIEWS=\"#{group_hash[:admin_views].join(",")}\"\n"
else
str += "GROUP_ADMIN_VIEWS=#{GROUP_ADMIN_SUNSTONE_VIEWS}\n"
end
if group_hash[:default_admin_view]
str += "GROUP_ADMIN_DEFAULT_VIEW=\"#{group_hash[:default_admin_view]}\"\n"
else
str += "GROUP_ADMIN_DEFAULT_VIEW=#{GROUP_ADMIN_SUNSTONE_VIEWS}"
end
self.update(str, true)
return 0
end
@ -193,6 +206,24 @@ module OpenNebula
return rc
end
# Adds a User to the Group administrators set
# @param user_id [Integer] User ID
#
# @return [nil, OpenNebula::Error] nil in case of success, Error
# otherwise
def add_admin(user_id)
return call(GROUP_METHODS[:add_admin], @pe_id, user_id.to_i)
end
# Removes a User from the Group administrators set
# @param user_id [Integer] User ID
#
# @return [nil, OpenNebula::Error] nil in case of success, Error
# otherwise
def del_admin(user_id)
return call(GROUP_METHODS[:del_admin], @pe_id, user_id.to_i)
end
# ---------------------------------------------------------------------
# Helpers to get information
# ---------------------------------------------------------------------
@ -206,15 +237,31 @@ module OpenNebula
return id_array != nil && id_array.include?(uid.to_s)
end
# Returns whether or not the user with id 'uid' is an admin of this group
def contains_admin(uid)
#This doesn't work in ruby 1.8.5
#return self["ADMINS/ID[.=#{uid}]"] != nil
id_array = retrieve_elements('ADMINS/ID')
return id_array != nil && id_array.include?(uid.to_s)
end
# Returns an array with the numeric user ids
def user_ids
array = Array.new
ids = self.retrieve_elements("USERS/ID")
return [] if ids.nil?
self.each("USERS/ID") do |id|
array << id.text.to_i
end
return ids.collect! {|x| x.to_i}
end
return array
# Returns an array with the numeric admin user ids
def admin_ids
ids = self.retrieve_elements("ADMINS/ID")
return [] if ids.nil?
return ids.collect! {|x| x.to_i}
end
private
@ -260,10 +307,6 @@ module OpenNebula
# gdef[:group_admin][:name] username for group admin
# gdef[:group_admin][:password] password for group admin
# gdef[:group_admin][:auth_driver] auth driver for group admin
# gdef[:group_admin][:resources] resources that group admin manage
# gdef[:group_admin][:manage_resources] whether group admin manages
# group users
# gdef[:resources] resources that group users manage
#
# @return [nil, OpenNebula::Error] nil in case of success, Error
def create_admin_user(gdef)
@ -299,37 +342,13 @@ module OpenNebula
return rc
end
# Set the default admin view to groupadmin
group_admin.update("DEFAULT_VIEW=#{GROUP_ADMIN_SUNSTONE_VIEWS}", true)
#Create admin group acls
acls = Array.new
acls_str = (gdef[:group_admin][:resources] || \
gdef[:resources] || GROUP_DEFAULT_ACLS)
manage_users = gdef[:group_admin][:manage_users] || "YES"
if manage_users.upcase == "YES"
acls << "##{group_admin.id} USER/@#{self.id} CREATE+USE+MANAGE+ADMIN"
end
acls << "##{group_admin.id} #{acls_str}/@#{self.id} " +
"CREATE+USE+MANAGE"
rc, tmp = create_group_acls(acls)
rc = self.add_admin(group_admin.id)
if OpenNebula.is_error?(rc)
group_admin.delete
return rc
end
#Set Sunstone Views for the group
gtmpl = "GROUP_ADMINS=#{gdef[:group_admin][:name]}\n"
gtmpl << "GROUP_ADMIN_VIEWS=#{GROUP_ADMIN_SUNSTONE_VIEWS}\n"
self.update(gtmpl, true)
return nil
end
end

View File

@ -470,6 +470,22 @@ EOT
end
end
slave_admins_elem = slave_doc.root.at_xpath("ADMINS")
master_admins_elem = master_doc.root.at_xpath("ADMINS")
slave_admins_elem.xpath("ID").each do |id|
user = users[id.text.to_i]
if !user.nil?
user_id = user[:oid]
if master_admins_elem.at_xpath("ID [.=#{user_id}]").nil?
master_admins_elem.add_child(
master_doc.create_element("ID")).content = user_id
end
end
end
slave_template = slave_doc.root.at_xpath("TEMPLATE")
master_template = master_doc.root.at_xpath("TEMPLATE")
@ -501,18 +517,34 @@ EOT
users_elem = slave_doc.root.at_xpath("USERS")
users_elem.remove
new_elem = slave_doc.create_element("USERS")
new_users_elem = slave_doc.create_element("USERS")
users_elem.xpath("ID").each do |id|
user = users[id.text.to_i]
if !user.nil?
new_elem.add_child(slave_doc.create_element("ID")).
new_users_elem.add_child(slave_doc.create_element("ID")).
content = user[:oid]
end
end
slave_doc.root.add_child(new_elem)
slave_doc.root.add_child(new_users_elem)
admins_elem = slave_doc.root.at_xpath("ADMINS")
admins_elem.remove
new_admins_elem = slave_doc.create_element("ADMINS")
admins_elem.xpath("ID").each do |id|
user = users[id.text.to_i]
if !user.nil?
new_admins_elem.add_child(slave_doc.create_element("ID")).
content = user[:oid]
end
end
slave_doc.root.add_child(new_admins_elem)
# Update resource providers
slave_doc.root.xpath("RESOURCE_PROVIDER").each do |elem|

View File

@ -86,6 +86,40 @@ EOT
end
end
admin_v_elem = doc.at_xpath("/GROUP/TEMPLATE/GROUP_ADMIN_VIEWS")
if (!admin_v_elem.nil?)
aux_e = doc.at_xpath("/GROUP/TEMPLATE/GROUP_ADMIN_DEFAULT_VIEW")
aux_e.remove if !aux_e.nil?
doc.at_xpath("/GROUP/TEMPLATE").add_child(
doc.create_element("GROUP_ADMIN_DEFAULT_VIEW")).
add_child(Nokogiri::XML::CDATA.new(
doc,
admin_v_elem.text))
end
admins_elem = doc.root.add_child( doc.create_element("ADMINS") )
elem = doc.at_xpath("/GROUP/TEMPLATE/GROUP_ADMINS")
if (!elem.nil?)
elem.remove
elem.text.split(",").each do |uname|
@db.fetch("SELECT oid FROM user_pool \
WHERE name=\"#{uname.strip}\"") do |user_row|
# Check that user is part of this group first
if !doc.at_xpath("/GROUP/USERS/ID[.=#{user_row[:oid]}]").nil?
admins_elem.add_child(
doc.create_element("ID") ).content =
user_row[:oid]
end
end
end
end
res_providers = doc.xpath("/GROUP/RESOURCE_PROVIDER")
res_providers.each do |provider|

View File

@ -484,23 +484,31 @@ void RequestManager::register_xml_methods()
xmlrpc_c::method * group_allocate_pt;
xmlrpc_c::method * group_update_pt;
xmlrpc_c::method * group_delete_pt;
xmlrpc_c::method * group_add_admin_pt;
xmlrpc_c::method * group_del_admin_pt;
if (nebula.is_federation_slave())
{
group_allocate_pt = new RequestManagerProxy("one.group.allocate");
group_delete_pt = new RequestManagerProxy("one.group.delete");
group_update_pt = new RequestManagerProxy("one.group.update");
group_add_admin_pt = new RequestManagerProxy("one.group.addadmin");
group_del_admin_pt = new RequestManagerProxy("one.group.deladmin");
}
else
{
group_allocate_pt = new GroupAllocate();
group_delete_pt = new GroupDelete();
group_update_pt = new GroupUpdateTemplate();
group_add_admin_pt = new GroupAddAdmin();
group_del_admin_pt = new GroupDelAdmin();
}
xmlrpc_c::methodPtr group_allocate(group_allocate_pt);
xmlrpc_c::methodPtr group_delete(group_delete_pt);
xmlrpc_c::methodPtr group_update(group_update_pt);
xmlrpc_c::methodPtr group_add_admin(group_add_admin_pt);
xmlrpc_c::methodPtr group_del_admin(group_del_admin_pt);
xmlrpc_c::methodPtr group_info(new GroupInfo());
xmlrpc_c::methodPtr group_set_quota(new GroupSetQuota());
@ -513,6 +521,8 @@ void RequestManager::register_xml_methods()
RequestManagerRegistry.addMethod("one.group.info", group_info);
RequestManagerRegistry.addMethod("one.group.quota", group_set_quota);
RequestManagerRegistry.addMethod("one.group.update", group_update);
RequestManagerRegistry.addMethod("one.group.addadmin", group_add_admin);
RequestManagerRegistry.addMethod("one.group.deladmin", group_del_admin);
RequestManagerRegistry.addMethod("one.grouppool.info", grouppool_info);

View File

@ -81,3 +81,113 @@ void GroupSetQuota::
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void GroupEditAdmin::request_execute(
xmlrpc_c::paramList const& paramList,
RequestAttributes& att)
{
int group_id = xmlrpc_c::value_int(paramList.getInt(1));
int user_id = xmlrpc_c::value_int(paramList.getInt(2));
PoolObjectAuth group_perms;
PoolObjectAuth user_perms;
string group_name;
string user_name;
string error_str;
Group* group;
int rc;
// -------------------------------------------------------------------------
// Authorize the action
// -------------------------------------------------------------------------
rc = get_info(pool, group_id, PoolObjectSQL::GROUP,
att, group_perms, group_name, true);
if ( rc == -1 )
{
return;
}
rc = get_info(upool, user_id, PoolObjectSQL::USER, att, user_perms,
user_name, false);
if ( rc == -1 )
{
failure_response(NO_EXISTS, get_error(object_name(PoolObjectSQL::USER),
user_id), att);
return;
}
if ( att.uid != 0 )
{
AuthRequest ar(att.uid, att.group_ids);
ar.add_auth(AuthRequest::ADMIN, group_perms); // MANAGE GROUP
ar.add_auth(AuthRequest::ADMIN, user_perms); // MANAGE USER
if (UserPool::authorize(ar) == -1)
{
failure_response(AUTHORIZATION,
authorization_error(ar.message, att),
att);
return;
}
}
group = static_cast<GroupPool*>(pool)->get(group_id, true);
if ( group == 0 )
{
failure_response(NO_EXISTS,
get_error(object_name(auth_object),group_id),
att);
return;
}
rc = edit_admin(group, user_id, error_str);
if (rc == 0)
{
pool->update(group);
}
group->unlock();
if (rc != 0)
{
failure_response(INTERNAL,
request_error("Cannot edit group", error_str),
att);
return;
}
success_response(group_id, att);
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int GroupAddAdmin::edit_admin(Group* group, int user_id, string& error_msg)
{
return group->add_admin(user_id, error_msg);
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int GroupDelAdmin::edit_admin(Group* group, int user_id, string& error_msg)
{
return group->del_admin(user_id, error_msg);
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

View File

@ -98,6 +98,7 @@ tabs:
Group.create_dialog: true
Group.quotas_dialog: true
Group.delete: true
Group.edit_admins: true
vdcs-tab:
panel_tabs:
vdc_info_tab: true

View File

@ -96,6 +96,7 @@ tabs:
Group.create_dialog: true
Group.quotas_dialog: true
Group.delete: true
Group.edit_admins: true
acls-tab:
panel_tabs:
table_columns:

View File

@ -97,6 +97,7 @@ tabs:
Group.create_dialog: true
Group.quotas_dialog: true
Group.delete: true
Group.edit_admins: true
acls-tab:
panel_tabs:
table_columns:

View File

@ -39,6 +39,8 @@ module OpenNebulaJSON
when "chown" then self.chown(action_hash['params'])
when "update" then self.update_json(action_hash['params'])
when "set_quota" then self.set_quota(action_hash['params'])
when "add_admin" then self.add_admin_json(action_hash['params'])
when "del_admin" then self.del_admin_json(action_hash['params'])
else
error_msg = "#{action_hash['perform']} action not " <<
" available for this resource"
@ -59,5 +61,13 @@ module OpenNebulaJSON
quota_template = template_to_str(quota_json)
super(quota_template)
end
def add_admin_json(params=Hash.new)
add_admin(params['admin_id'].to_i)
end
def del_admin_json(params=Hash.new)
del_admin(params['admin_id'].to_i)
end
end
end

View File

@ -87,10 +87,10 @@ class SunstoneViews
available << @views_config['groups'][group.name]
end
gadmins = group["TEMPLATE/GROUP_ADMINS"]
gadmins = group.admin_ids
gadmins_views = group["TEMPLATE/GROUP_ADMIN_VIEWS"]
if gadmins && gadmins.split(',').include?(user_name) && gadmins_views
if gadmins && gadmins.include?(user.id) && gadmins_views
views_array = gadmins_views.split(",")
available << views_array.each{|v| v.strip!}
end

View File

@ -1141,6 +1141,14 @@ var OpenNebula = {
},
"accounting" : function(params){
OpenNebula.Action.monitor(params,OpenNebula.Group.resource,false);
},
"add_admin" : function(params){
var action_obj = params.data.extra_param;
OpenNebula.Action.simple_action(params,OpenNebula.Group.resource,"add_admin",action_obj);
},
"del_admin" : function(params){
var action_obj = params.data.extra_param;
OpenNebula.Action.simple_action(params,OpenNebula.Group.resource,"del_admin",action_obj);
}
},

View File

@ -66,7 +66,7 @@ function create_group_tmpl(dialog_name){
<div class="large-12 columns">\
<p class="subheader">'
+tr("Allow users in this group to use the following Sunstone views")+
'&emsp;<span class="tip">'+tr("Views available to the group users")+'</span>\
'&emsp;<span class="tip">'+tr("Views available to the group users. If the default is unset, the one set in sunstone-views.yaml will be used")+'</span>\
</p>\
</div>\
</div>\
@ -75,19 +75,6 @@ function create_group_tmpl(dialog_name){
insert_views(dialog_name)
+'</div>\
</div>\
<div class="row">\
<div class="large-12 columns">\
<p class="subheader">'
+tr("Set the default Sunstone view")+
'&emsp;<span class="tip">'+tr("Default view for the group users. If it is unset, the default is set in sunstone-views.yaml")+'</span>\
</p>\
</div>\
</div>\
<div class="row">\
<div class="large-12 columns">'+
insert_views_default(dialog_name)
+'</div>\
</div>\
</div>\
<div id="administrators" class=" content">\
<div class="row">\
@ -97,7 +84,7 @@ function create_group_tmpl(dialog_name){
<label>\
<input type="checkbox" id="admin_user" name="admin_user" value="YES" />\
'+tr("Create an administrator user")+'\
<span class="tip">'+tr("You can create now an administrator user that will be assigned to the new regular group, with the administrator group as a secondary one.")+'</span>\
<span class="tip">'+tr("You can create now an administrator user. More administrators can be added later.")+'</span>\
</label>\
</div>\
</div>' +
@ -119,7 +106,7 @@ function create_group_tmpl(dialog_name){
<div class="large-12 columns">\
<p class="subheader">'
+tr("Allow users in this group to create the following resources")+
'&emsp;<span class="tip">'+tr("This will create new ACL Rules to define which virtual resources this group's users will be able to create. You can set different resources for the administrator group, and decide if the administrators will be allowed to create new users.")+'</span>\
'&emsp;<span class="tip">'+tr("This will create new ACL Rules to define which virtual resources this group's users will be able to create.")+'</span>\
</p>\
</div>\
</div>\
@ -127,7 +114,6 @@ function create_group_tmpl(dialog_name){
<div class="large-12 columns">\
<table class="dataTable" style="table-layout:fixed">\
<thead><tr>\
<th/>\
<th>'+tr("VMs")+'</th>\
<th>'+tr("VNets")+'</th>\
<th>'+tr("Security Groups")+'</th>\
@ -137,7 +123,6 @@ function create_group_tmpl(dialog_name){
</tr></thead>\
<tbody>\
<tr>\
<th>'+tr("Users")+'</th>\
<td><input type="checkbox" id="group_res_vm" name="group_res_vm" class="resource_cb" value="VM"></input></td>\
<td><input type="checkbox" id="group_res_net" name="group_res_net" class="resource_cb" value="NET"></input></td>\
<td><input type="checkbox" id="group_res_sg" name="group_res_sg" class="resource_cb" value="SECGROUP"></input></td>\
@ -146,15 +131,6 @@ function create_group_tmpl(dialog_name){
<td><input type="checkbox" id="group_res_document" name="group_res_document" class="resource_cb" value="DOCUMENT"></input></td>\
<td/>\
</tr>\
<tr>\
<th>'+tr("Admins")+'</th>\
<td><input type="checkbox" id="group_admin_res_vm" name="group_admin_res_vm" class="resource_cb" value="VM"></input></td>\
<td><input type="checkbox" id="group_admin_res_net" name="group_admin_res_net" class="resource_cb" value="NET"></input></td>\
<td><input type="checkbox" id="group_admin_res_sg" name="group_admin_res_sg" class="resource_cb" value="SECGROUP"></input></td>\
<td><input type="checkbox" id="group_admin_res_image" name="group_admin_res_image" class="resource_cb" value="IMAGE"></input></td>\
<td><input type="checkbox" id="group_admin_res_template" name="group_admin_res_template" class="resource_cb" value="TEMPLATE"></input></td>\
<td><input type="checkbox" id="group_admin_res_document" name="group_admin_res_document" class="resource_cb" value="DOCUMENT"></input></td>\
</tr>\
</tbody>\
</table>\
</div>\
@ -319,6 +295,24 @@ var group_actions = {
//plot_graph(response,'#group_acct_tabTab','group_acct_', info);
},
error: onError
},
"Group.add_admin" : {
type: "single",
call : OpenNebula.Group.add_admin,
callback : function (req) {
Sunstone.runAction('Group.show',req.request.data[0][0]);
},
error : onError
},
"Group.del_admin" : {
type: "single",
call : OpenNebula.Group.del_admin,
callback : function (req) {
Sunstone.runAction('Group.show',req.request.data[0][0]);
},
error : onError
}
}
@ -396,33 +390,62 @@ Sunstone.addMainTab('groups-tab',groups_tab);
Sunstone.addInfoPanel("group_info_panel",group_info_panel);
function insert_views(dialog_name){
var views_checks_str = "";
var views_array = config['all_views'];
for (var i = 0; i < views_array.length; i++)
{
var checked = views_array[i] == 'cloud' ? "checked" : "";
var headers = "<tr><th/>";
var row = "<tr><td>"+tr("Users")+"</td>";
var admin_row = "<tr><td>"+tr("Admins")+"</td>";
var default_row = "<tr><td>"+tr("Users default")+"</td>";
var default_admin_row = "<tr><td>"+tr("Admins default")+"</td>";
views_checks_str = views_checks_str +
'<input type="checkbox" id="group_view_'+dialog_name+'_'+views_array[i]+
'" value="'+views_array[i]+'" '+checked+'/>' +
'<label for="group_view_'+dialog_name+'_'+views_array[i]+'">'+views_array[i]+
'</label>'
}
return views_checks_str;
}
var views_array = config['all_views'];
function insert_views_default(dialog_name){
var views_checks_str = "";
var views_array = config['all_views'];
for (var i = 0; i < views_array.length; i++)
{
views_checks_str = views_checks_str +
'<input type="radio" name="group_default_view_'+dialog_name+'" id="group_default_view_'+dialog_name+'_'+views_array[i]+
'" value="'+views_array[i]+'"/>' +
'<label for="group_default_view_'+dialog_name+'_'+views_array[i]+'">'+views_array[i]+
'</label>'
}
return views_checks_str;
for (var i = 0; i < views_array.length; i++){
headers += "<th>"+views_array[i]+"</th>";
var checked = views_array[i] == 'cloud' ? "checked" : "";
row += '<td>\
<input type="checkbox" \
id="group_view_'+dialog_name+'_'+views_array[i]+'" \
value="'+views_array[i]+'" '+checked+'/>\
</td>';
checked = views_array[i] == 'groupadmin' ? "checked" : "";
admin_row += '<td>\
<input type="checkbox" \
id="group_admin_view_'+dialog_name+'_'+views_array[i]+'" \
value="'+views_array[i]+'" '+checked+'/>\
</td>';
default_row += '<td>\
<input type="radio" \
name="group_default_view_'+dialog_name+'" \
id="group_default_view_'+dialog_name+'_'+views_array[i]+'" \
value="'+views_array[i]+'"/>\
</td>'
default_admin_row += '<td>\
<input type="radio" \
name="group_default_admin_view_'+dialog_name+'" \
id="group_default_admin_view_'+dialog_name+'_'+views_array[i]+'" \
value="'+views_array[i]+'"/>\
</td>'
}
headers += "</tr>";
row += "</tr>";
admin_row += "</tr>";
default_row += "</tr>";
default_admin_row += "</tr>";
return '<table class="dataTable" style="table-layout:fixed">'+
headers+
row+
default_row+
headers+
admin_row+
default_admin_row+
"</table>";
}
function groupElements(){
@ -557,6 +580,12 @@ function updateGroupInfo(request,group){
</div>'
}
var users_tab = {
title : tr("Users"),
icon: "fa-users",
content: group_users_tab_content(info)
};
var default_group_quotas = Quotas.default_quotas(info.DEFAULT_GROUP_QUOTAS);
var quotas_html = initQuotasPanel(info, default_group_quotas,
@ -577,6 +606,7 @@ function updateGroupInfo(request,group){
Sunstone.updateInfoPanelTab("group_info_panel","group_info_tab",info_tab);
Sunstone.updateInfoPanelTab("group_info_panel","group_users_tab",users_tab);
Sunstone.updateInfoPanelTab("group_info_panel","group_quotas_tab",quotas_tab);
Sunstone.updateInfoPanelTab("group_info_panel","group_accounting_tab",accounting_tab);
@ -592,6 +622,8 @@ function updateGroupInfo(request,group){
Sunstone.popUpInfoPanel("group_info_panel", 'groups-tab');
setup_group_users_tab_content(info);
if (Config.isFeatureEnabled("showback")) {
showbackGraphs(
$("#group_showback","#group_info_panel"),
@ -615,6 +647,126 @@ function updateGroupInfo(request,group){
"Group");
}
function group_users_tab_content(group_info){
var html = "";
if (Config.isTabActionEnabled("groups-tab", "Group.edit_admins")) {
html +=
'<div class="row">\
<div class="large-4 columns right">\
<span>\
<button class="button secondary small radius" id="edit_admins_button" style="width: 100%;">\
<span class="fa fa-pencil-square-o"></span> '+tr("Edit administrators")+'\
</button>\
<button class="button alert small radius" id="cancel_admins_button" style="display: none">\
'+tr("Cancel")+'\
</button>\
<button class="button success small radius" id="submit_admins_button" style="display: none">\
'+tr("Apply")+'\
</button>\
</span>\
</div>\
</div>';
}
html += '<div class="group_users_info_table">\
'+generateUserTableSelect("group_users_list")+'\
</div>';
return html;
}
function setup_group_users_tab_content(group_info){
var users = [];
if (group_info.USERS.ID != undefined){
users = group_info.USERS.ID;
if (!$.isArray(users)){
users = [users];
}
}
var admins = [];
if (group_info.ADMINS.ID != undefined){
admins = group_info.ADMINS.ID;
if (!$.isArray(admins)){
admins = [admins];
}
}
var opts = {
read_only: true,
fixed_ids: users,
admin_ids: admins
}
setupUserTableSelect($("#group_info_panel"), "group_users_list", opts);
refreshUserTableSelect($("#group_info_panel"), "group_users_list");
if (Config.isTabActionEnabled("groups-tab", "Group.edit_admins")) {
$("#group_info_panel").off("click", "#edit_admins_button");
$("#group_info_panel").on("click", "#edit_admins_button", function() {
$("#edit_admins_button", "#group_info_panel").hide();
$("#cancel_admins_button", "#group_info_panel").show();
$("#submit_admins_button", "#group_info_panel").show();
$("#group_info_panel div.group_users_info_table").html(
generateUserTableSelect("group_users_edit_list") );
var opts = {
multiple_choice: true,
fixed_ids: users,
admin_ids: admins
}
setupUserTableSelect($("#group_info_panel"), "group_users_edit_list", opts);
selectUserTableSelect($("#group_info_panel"), "group_users_edit_list", { ids : admins });
return false;
});
$("#group_info_panel").off("click", "#cancel_admins_button");
$("#group_info_panel").on("click", "#cancel_admins_button", function() {
Sunstone.runAction("Group.show", group_info.ID);
return false;
});
$("#group_info_panel").off("click", "#submit_admins_button");
$("#group_info_panel").on("click", "#submit_admins_button", function() {
// Add/delete admins
var selected_admins_list = retrieveUserTableSelect(
$("#group_info_panel"), "group_users_edit_list");
$.each(selected_admins_list, function(i,admin_id){
if (admins.indexOf(admin_id) == -1){
Sunstone.runAction("Group.add_admin",
group_info.ID, {admin_id : admin_id});
}
});
$.each(admins, function(i,admin_id){
if (selected_admins_list.indexOf(admin_id) == -1){
Sunstone.runAction("Group.del_admin",
group_info.ID, {admin_id : admin_id});
}
});
return false;
});
}
}
function disableAdminUser(dialog){
$('#username',dialog).attr('disabled','disabled');
$('#pass',dialog).attr('disabled','disabled');
@ -665,16 +817,8 @@ function setupCreateGroupDialog(){
var dialog = $create_group_dialog;
if ($(this).prop('checked')) {
enableAdminUser(dialog);
$.each($('[id^="group_admin_res"]', dialog), function(){
$(this).removeAttr("disabled");
});
} else {
disableAdminUser(dialog);
$.each($('[id^="group_admin_res"]', dialog), function(){
$(this).attr('disabled', 'disabled');
});
}
});
@ -684,13 +828,7 @@ function setupCreateGroupDialog(){
$(this).prop("checked", true);
});
$.each($('[id^="group_admin_res"]', dialog), function(){
$(this).attr('disabled', 'disabled');
$(this).prop("checked", true);
});
$("#group_res_net", dialog).prop("checked", false);
$("#group_admin_res_net", dialog).prop("checked", false);
$('#create_group_form',dialog).submit(function(){
var name = $('#name',this).val();
@ -730,18 +868,6 @@ function setupCreateGroupDialog(){
group_json['group']['shared_resources'] = "VM+DOCUMENT";
}
if (user_json){
resources = "";
separator = "";
$.each($('[id^="group_admin_res"]:checked', dialog), function(){
resources += (separator + $(this).val());
separator = "+";
});
group_json["group"]["group_admin"]["resources"] = resources;
}
group_json['group']['views'] = [];
$.each($('[id^="group_view"]:checked', dialog), function(){
@ -753,6 +879,17 @@ function setupCreateGroupDialog(){
group_json['group']['default_view'] = default_view;
}
group_json['group']['admin_views'] = [];
$.each($('[id^="group_admin_view"]:checked', dialog), function(){
group_json['group']['admin_views'].push($(this).val());
});
var default_view = $('[id^="group_default_admin_view"]:checked', dialog).val();
if (default_view != undefined){
group_json['group']['default_admin_view'] = default_view;
}
Sunstone.runAction("Group.create",group_json);
return false;
});
@ -820,38 +957,92 @@ function popUpUpdateGroupDialog(group, dialog)
var views_str = "";
$('input[id^="group_view"]', dialog).removeAttr('checked');
if (group.TEMPLATE.SUNSTONE_VIEWS){
views_str = group.TEMPLATE.SUNSTONE_VIEWS;
var views = views_str.split(",");
$.each(views, function(){
$('input[id^="group_view"][value="'+this.trim()+'"]', dialog).attr('checked','checked');
$('input[id^="group_view"][value="'+this.trim()+'"]',
dialog).attr('checked','checked');
});
}
$('input[id^="group_default_view"]', dialog).removeAttr('checked');
if (group.TEMPLATE.DEFAULT_VIEW){
$('input[id^="group_default_view"][value="'+group.TEMPLATE.DEFAULT_VIEW.trim()+'"]',
dialog).attr('checked','checked');
}
$('input[id^="group_admin_view"]', dialog).removeAttr('checked');
if (group.TEMPLATE.GROUP_ADMIN_VIEWS){
views_str = group.TEMPLATE.GROUP_ADMIN_VIEWS;
var views = views_str.split(",");
$.each(views, function(){
$('input[id^="group_admin_view"][value="'+this.trim()+'"]',
dialog).attr('checked','checked');
});
}
$('input[id^="group_default_admin_view"]', dialog).removeAttr('checked');
if (group.TEMPLATE.GROUP_ADMIN_DEFAULT_VIEW){
$('input[id^="group_default_admin_view"][value="'+group.TEMPLATE.GROUP_ADMIN_DEFAULT_VIEW.trim()+'"]',
dialog).attr('checked','checked');
}
$(dialog).off("click", 'button#update_group_submit');
$(dialog).on("click", 'button#update_group_submit', function(){
// Update Views
//-------------------------------------
var new_views_str = "";
var separator = "";
var template_json = group.TEMPLATE;
delete template_json["SUNSTONE_VIEWS"];
delete template_json["DEFAULT_VIEW"];
delete template_json["GROUP_ADMIN_VIEWS"];
delete template_json["GROUP_ADMIN_DEFAULT_VIEW"];
var views = [];
$.each($('[id^="group_view"]:checked', dialog), function(){
new_views_str += (separator + $(this).val());
separator = ",";
views.push($(this).val());
});
if (new_views_str != views_str){
var template_json = group.TEMPLATE;
delete template_json["SUNSTONE_VIEWS"];
template_json["SUNSTONE_VIEWS"] = new_views_str;
var template_str = convert_template_to_string(template_json);
Sunstone.runAction("Group.update_template",group.ID,template_str);
if (views.length != 0){
template_json["SUNSTONE_VIEWS"] = views.join(",");
}
var default_view = $('[id^="group_default_view"]:checked', dialog).val();
if (default_view != undefined){
template_json["DEFAULT_VIEW"] = default_view;
}
var admin_views = [];
$.each($('[id^="group_admin_view"]:checked', dialog), function(){
admin_views.push($(this).val());
});
if (admin_views.length != 0){
template_json["GROUP_ADMIN_VIEWS"] = admin_views.join(",");
}
var default_admin_view = $('[id^="group_default_admin_view"]:checked', dialog).val();
if (default_admin_view != undefined){
template_json["GROUP_ADMIN_DEFAULT_VIEW"] = default_admin_view;
}
var template_str = convert_template_to_string(template_json);
Sunstone.runAction("Group.update_template",group.ID,template_str);
// Close the dialog
//-------------------------------------

View File

@ -6990,6 +6990,185 @@ function selectGroupTableSelect(section, context_id, opts){
return selectResourceTableSelect(section, context_id, opts);
}
function generateUserTableSelect(context_id){
var columns = [
"",
tr("ID"),
tr("Name"),
tr("Group"),
tr("Auth driver"),
tr("VMs"),
tr("Memory"),
tr("CPU"),
tr("Group ID"),
tr("Hidden User Data")
];
var options = {
"id_index": 1,
"name_index": 2,
"select_resource": tr("Please select a User from the list"),
"you_selected": tr("You selected the following User:"),
"select_resource_multiple": tr("Please select one or more users from the list"),
"you_selected_multiple": tr("You selected the following users:")
};
return generateResourceTableSelect(context_id, columns, options);
}
// opts.bVisible: dataTable bVisible option. If not set, the .yaml visibility will be used
// opts.filter_fn: boolean function to filter which elements to show
// opts.select_callback(aData, options): function called after a row is selected
// opts.multiple_choice: boolean true to enable multiple element selection
// opts.read_only: boolean true so user is not asked to select elements
// opts.fixed_ids: Array of IDs to show. Any other ID will be filtered out. If
// an ID is not returned by the pool, it will be included as a
// blank row
// opts.admin_ids: Array of User IDs that will be marked as admin
function setupUserTableSelect(section, context_id, opts){
if(opts == undefined){
opts = {};
}
if(opts.bVisible == undefined){
// Use the settings in the conf, but removing the checkbox
var config = Config.tabTableColumns('users-tab').slice(0);
var i = config.indexOf(0);
if(i != -1){
config.splice(i,1);
}
opts.bVisible = config;
}
if(opts.multiple_choice == undefined){
opts.multiple_choice = false;
}
var fixed_ids_map_orig = {};
if(opts.fixed_ids != undefined){
$.each(opts.fixed_ids,function(){
fixed_ids_map_orig[this] = true;
});
}
var admin_ids_map = {};
if(opts.admin_ids != undefined){
$.each(opts.admin_ids,function(){
admin_ids_map[this] = true;
});
}
var options = {
"dataTable_options": {
"bAutoWidth":false,
"iDisplayLength": 4,
"sDom" : '<"H">t<"F"p>',
"bRetrieve": true,
"bSortClasses" : false,
"bDeferRender": true,
"aoColumnDefs": [
{ "sWidth": "35px", "aTargets": [0] },
{ "bVisible": true, "aTargets": opts.bVisible},
{ "bVisible": false, "aTargets": ['_all']}
]
},
"multiple_choice": opts.multiple_choice,
"read_only": opts.read_only,
"fixed_ids": opts.fixed_ids,
"id_index": 1,
"name_index": 2,
"update_fn": function(datatable){
OpenNebula.User.list({
timeout: true,
success: function (request, resource_list){
var list_array = [];
var fixed_ids_map = $.extend({}, fixed_ids_map_orig);
$.each(resource_list,function(){
var add = true;
if(opts.filter_fn){
add = opts.filter_fn(this.USER);
}
if(opts.fixed_ids != undefined){
add = (add && fixed_ids_map[this.USER.ID]);
}
if(add){
var user_arr = userElementArray(this);
if (opts.admin_ids != undefined){
if (admin_ids_map[this.USER.ID]){
user_arr[2] =
('<i class="fa fa-star fa-fw"></i> ' +
user_arr[2]) // NAME INDEX
} else {
user_arr[2] =
('<i class="fa fa-fw"></i> ' +
user_arr[2]) // NAME INDEX
}
}
list_array.push(user_arr);
delete fixed_ids_map[this.USER.ID];
}
});
var n_columns = 10; // SET FOR EACH RESOURCE
$.each(fixed_ids_map, function(id,v){
var empty = [];
for(var i=0; i<=n_columns; i++){
empty.push("");
}
empty[1] = id; // SET FOR EACH RESOURCE, id_index
list_array.push(empty);
});
updateView(list_array, datatable);
},
error: onError
});
},
"select_callback": opts.select_callback
};
return setupResourceTableSelect(section, context_id, options);
}
// Clicks the refresh button
function refreshUserTableSelect(section, context_id){
return refreshResourceTableSelect(section, context_id);
}
// Returns an ID, or an array of IDs for opts.multiple_choice
function retrieveUserTableSelect(section, context_id){
return retrieveResourceTableSelect(section, context_id);
}
// Clears the current selection, and selects the given IDs
// opts.ids must be a single ID, or an array of IDs for options.multiple_choice
// opts.names must be an array of {name, uname}
function selectUserTableSelect(section, context_id, opts){
return selectResourceTableSelect(section, context_id, opts);
}
function generateClusterTableSelect(context_id){

View File

@ -235,6 +235,8 @@ helpers do
if user['TEMPLATE/DEFAULT_VIEW']
session[:default_view] = user['TEMPLATE/DEFAULT_VIEW']
elsif group.contains_admin(user.id) && group['TEMPLATE/GROUP_ADMIN_DEFAULT_VIEW']
session[:default_view] = group['TEMPLATE/GROUP_ADMIN_DEFAULT_VIEW']
elsif group['TEMPLATE/DEFAULT_VIEW']
session[:default_view] = group['TEMPLATE/DEFAULT_VIEW']
else