$known_actions []= "delete-interface-tags";
// expects: interface_id & interface_tag_ids
+//////////////////////////////////////// nodegroups
+$known_actions []= "update-nodegroup";
+// expects nodegroup_id groupname value
+$known_actions []= "add-nodegroup";
+// expects groupname, tag_type_id, value
+$known_actions []= 'delete-nodegroups';
+// expects nodegroup_ids
+
////////////////////////////////////////////////////////////
$interface_details= array ('method','type', 'ip', 'gateway', 'network',
'broadcast', 'netmask', 'dns1', 'dns2',
else
drupal_set_error ("Could not update tag type $tag_type_id\n".$api->error());
plc_redirect(l_tag($tag_type_id));
+ break;
}
case 'add-tag-type': {
else
drupal_set_error ("Could not create tag type $tagname");
plc_redirect( l_tags());
+ break;
}
case 'delete-tag-types': {
plc_redirect(l_interface($_POST['interface_id']));
}
+//////////////////////////////////////// nodegroups
+ case 'update-nodegroup': {
+ $nodegroup_id = $_POST['nodegroup_id'];
+ $groupname = $_POST['groupname'];
+ $value = $_POST['value'];
+
+ $fields=array();
+ $fields['groupname']=$groupname;
+ $fields['value']=$value;
+ if ( $api->UpdateNodeGroup($nodegroup_id,$fields) == 1)
+ drupal_set_message ('Nodegroup updated');
+ else
+ drupal_set_error ("Could not update nodegroup $groupname");
+
+ plc_redirect(l_nodegroup($nodegroup_id));
+
+ }
+
+ case 'add-nodegroup': {
+ $groupname=$_POST['groupname'];
+ if ( ! $groupname ) {
+ drupal_set_error ('Empty groupname');
+ plc_redirect (l_nodegroups());
+ }
+ $tag_type_id=intval($_POST['tag_type_id']);
+ if ( ! $tag_type_id ) {
+ drupal_set_error ('You must select a tag in the dropdown list');
+ plc_redirect (l_nodegroups());
+ }
+ $value=$_POST['value'];
+ if ( ! $value ) {
+ drupal_set_message ("Empty value.. let's see ..");
+ }
+ if ( $api->AddNodeGroup ($groupname,$tag_type_id,$value) > 0)
+ drupal_set_message ("Nodegroup $groupname created");
+ else
+ drupal_set_error ("Could not create nodegroup $groupname");
+
+ plc_redirect (l_nodegroups());
+ break;
+ }
+
+ case 'delete-nodegroups': {
+ $nodegroup_ids=$_POST['nodegroup_ids'];
+ if ( ! $nodegroup_ids ) {
+ drupal_set_message("action=delete-nodegroups - No group selected");
+ plc_redirect(l_nodegroups());
+ }
+ $success=true;
+ $counter=0;
+ foreach ($nodegroup_ids as $nodegroup_id)
+ if ($api->DeleteNodeGroup(intval($nodegroup_id)) != 1)
+ $success=false;
+ else
+ $counter++;
+ if ($success)
+ drupal_set_message ("Deleted $counter group(s)");
+ else
+ drupal_set_error ("Could not delete all selected groups, only $counter were removed");
+ plc_redirect (l_nodegroups());
+ break;
+ }
////////////////////////////////////////
/* internal toggles */
div#toggle-container-my-slice-persons-current,
+div#toggle-container-my-slice-nodes-current {
+ background: #f0f0f0;
+}
+
div#toggle-container-my-slice-persons-add,
-div#toggle-container-my-slice-nodes-current,
div#toggle-container-my-slice-nodes-add {
-/* background:#fcf7ff; */
- background: #ccffc7;
+ background: #f0e0e0;
}
+/* qualify enough so that this wins over the even/odd row patterns in tables */
+tbody>tr>td.node-ok {
+ background: #a0f0e6;
+}
+tbody>tr>td.node-ko {
+ background: #ffa7a8;
+}
function l_slices () { return "/db/slices/index.php"; }
function l_slices_peer ($peer_id) { return "/db/slices/index.php?peerscope=$peer_id"; }
function l_slice ($slice_id) { return "/db/slices/index.php?id=$slice_id"; }
+function l_slice_nodes ($slice_id) { return "/db/slices/index.php?id=$slice_id&show_details=0&show_nodes=1&show_nodes_current=1&show_nodes_add=1"; }
function l_slice_t ($slice_id,$text) { return href (l_slice($slice_id),$text); }
function l_slice_add () { return "/db/slices/slice_add.php"; }
function l_slices_site($site_id) { return "/db/slices/index.php?site_id=$site_id"; }
function l_nodegroup ($nodegroup_id) { return "/db/tags/nodegroup.php?id=$nodegroup_id"; }
function l_nodegroup_t ($nodegroup_id,$text) {
return href(l_nodegroup($nodegroup_id),$text); }
+function l_nodegroup_obj ($nodegroup) {
+ return href(l_nodegroup($nodegroup['nodegroup_id']),$nodegroup['groupname']); }
function l_events () { return "/db/events/index.php"; }
function l_event ($type,$param,$id) { return "/db/events/index.php?type=$type&$param=$id"; }
}
return $text;
}
-function html_div ($text,$class="") {
- $html="<div";
+// generates <(atom) class=(class)> (text) </(atom)>
+function html_atom ($atom,$text,$class="") {
+ $html="<$atom";
if ($class) $html .= " class='$class'";
- $html .= ">$text</div>";
+ $html .= ">$text</$atom>";
return $html;
}
+function html_div ($text,$class="") { return html_atom ('div',$text,$class); }
+function html_span ($text,$class="") { return html_atom ('span',$text,$class); }
// should use the same channel as the php errors..
function plc_error_html ($text) { return html_div ($text,'plc-error'); }
}
}
-function plc_warning_html ($text) { return "<span class='plc-warning'>" . $text . "</span>";}
+function plc_warning_html ($text) { return html_span($text,'plc-warning'); }
function plc_warning ($text) { print plc_warning_html("Warning " . $text); }
-function bold_html ($text) { return "<span class='bold'>$text</span>"; }
+function bold_html ($text) { return html_span($text,'bold'); }
// shows a php variable verbatim with a heading message
function plc_debug ($message,$object) {
global $DAY, $WEEK, $MAX_WEEKS, $GRACE_DAYS, $NOW;
$current_exp=$slice['expires'];
+ $current_text = gmstrftime("%A %b-%d-%y %T %Z", $current_exp);
$max_exp= $NOW + ($MAX_WEEKS * $WEEK); // seconds since epoch
+ $max_text = gmstrftime("%A %b-%d-%y %T %Z", $max_exp);
// xxx some extra code needed to enable this area only if the slice description is OK:
// description and url must be non void
$toggle=
- new PlekitToggle('renew',"Renew this slice",
+ new PlekitToggle('renew',"Expires $current_text - Renew this slice",
array("bubble"=>
"Enter this zone if you wish to renew your slice",
'visible'=>$visible));
if ( empty( $selectors ) ) {
print <<< EOF
-<div class='plc-warning renewal'>
-Slice cannot be renewed any further into the future, try again closer to expiration date.
+<div class='my-slice-renewal'>
+Slices annot be renewed more than $MAX_WEEKS weeks from now, i.e. not beyond $max_text.
+For this reason, the current slice cannot be renewed any further into the future, try again closer to expiration date.
</div>
EOF;
} else {
}
$toggle->end();
-//////////////////// nodes
+//////////////////////////////////////////////////////////// Nodes
+// the nodes details to display here
+// (1) we search for the tag types for which 'category' matches 'node*/ui*'
+// all these tags will then be tentatively displayed in this area
+// (2) further information can also be optionally specified in the category:
+// (.) we split the category with '/' and search for assignments of the form var=value
+// (.) header can be set to supersede the column header (default is tagname)
+// (.) rank can be used for ordering the columns (default is tagname)
+// (.) type is passed to the javascript table, for sorting (default is 'string')
+
// minimal list as a start
-$node_columns = array('hostname','node_id','arch','peer_id','slice_ids_whitelist');
+$node_fixed_columns = array('hostname','node_id','peer_id','slice_ids_whitelist','run_level','boot_state');
+// scan tag types to find relevant additional columns
+$nodeui_tag_types = $api->GetTagTypes(array('category'=>'node*/ui*'));
+// extract tagname
+$node_tag_columns = array_map(create_function('$tt','return $tt["tagname"];'),$nodeui_tag_types);
+// build an ordered list of chunks {'tagname','header','rank','description'}
+$more_tags = array();
+foreach ($nodeui_tag_types as $tag_type) {
+ $tagname=$tag_type['tagname'];
+ $chunk=array();
+ $chunk['tagname']=$tagname;
+ $chunk['header']=$tagname;
+ $chunk['rank']=$tagname;
+ $chunk['type']='string';
+ $chunk['description']=$tag_type['description'];
+ $category_tokens=split('/',$tag_type['category']);
+ foreach ($category_tokens as $token) {
+ $assign=split('=',$token);
+ if (count($assign)==2)
+ $chunk[$assign[0]]=$assign[1];
+ }
+ $more_tags []= $chunk;
+}
+
+function sort_chunks ($ch1, $ch2) { return strcmp($ch1['rank'],$ch2['rank']); }
+usort ($more_tags, sort_chunks);
+
+//plc_debug('ordered additional tags',$more_tags);
+
+$node_columns=array_merge($node_fixed_columns,$node_tag_columns);
$nodes=$api->GetNodes(array('node_id'=>$slice['node_ids']),$node_columns);
$potential_nodes=$api->GetNodes(array('~node_id'=>$slice['node_ids']),$node_columns);
$count=count($nodes);
$toggle_nodes->start();
$headers=array();
+$notes=array();
$headers['peer']='string';
$headers['hostname']='string';
-$headers['arch']='string';
+$headers['S']='string';
+$notes[]='S = last known status';
+foreach ($more_tags as $chunk) {
+ $header=$chunk['header'];
+ $headers[$header]=$chunk['type'];
+ if ($header != $chunk['tagname']) $notes []= $header . ' = ' . $chunk['description'];
+}
if ($privileges) $headers[plc_delete_icon()]="none";
-$table_options = array('notes_area'=>false,
+$table_options = array('notes'=>$notes,
'search_width'=>15,
'pagesize'=>20);
-$table=new PlekitTable('nodes',$headers,'0',$table_options);
+$table=new PlekitTable('nodes',$headers,'1',$table_options);
$form=new PlekitForm(l_actions(),array('slice_id'=>$slice['slice_id']));
$form->start();
$table->row_start();
$peers->cell($table,$node['peer_id']);
$table->cell(l_node_obj($node));
- $table->cell($node['arch']);
+ $run_level=$node['run_level'];
+ if ( empty($run_level)) $run_level=$node['boot_state'];
+ $class=($run_level == 'boot') ? 'node-ok' : 'node-ko';
+ $table->cell($run_level,array('class'=>$class));
+ foreach ($more_tags as $chunk) $table->cell($node[$chunk['tagname']]);
+
if ($privileges) $table->cell ($form->checkbox_html('node_ids[]',$node['node_id']));
$table->row_end();
}
$count=count($potential_nodes);
$toggle_nodes=new PlekitToggle('my-slice-nodes-add',
"$count more nodes available",
- array('visible'=>get_arg('show_persons_add',false)));
+ array('visible'=>get_arg('show_nodes_add',false)));
$toggle_nodes->start();
if ( ! $potential_nodes ) {
echo "<p class='not-relevant'>No node to add</p>";
} else {
$headers=array();
+ $notes=array();
$headers['peer']='string';
$headers['hostname']='string';
- $headers['arch']='string';
+ $headers['S']='string';
+ $notes[]='S = last known status';
+ foreach ($more_tags as $chunk) {
+ $header=$chunk['header'];
+ $headers[$header]=$chunk['type'];
+ if ($header != $chunk['tagname']) $notes []= $header . ' = ' . $chunk['description'];
+ }
$headers['+']="none";
$table=new PlekitTable('add_nodes',$headers,'1', $table_options);
$table->row_start();
$peers->cell($table,$node['peer_id']);
$table->cell(l_node_obj($node));
- $table->cell($node['arch']);
+ $run_level=$node['run_level'];
+ if ( empty($run_level)) $run_level=$node['boot_state'];
+ $class=($run_level == 'boot') ? 'node-ok' : 'node-ko';
+ $table->cell($run_level,array('class'=>$class));
+ foreach ($more_tags as $chunk) $table->cell($node[$chunk['tagname']]);
$table->cell ($form->checkbox_html('node_ids[]',$node['node_id']));
$table->row_end();
}
$table->row_start();
if (plc_is_admin()) $table->cell (l_slice_t($slice_id,$slice_id));
$peers->cell($table,$peer_id);
- $table->cell (l_slice_obj($slice));
+ $table->cell (href(l_slice_nodes($slice_id),$slice['name']));
$table->cell ($users);
$table->cell(href(l_persons_slice($slice_id),count($slice['person_ids'])));
$table->cell (href(l_nodes_slice($slice_id),count($slice['node_ids'])));
drupal_set_title("Details for node group " . $nodegroup['groupname']);
plekit_linetabs($tabs);
-$details=new PlekitDetails(false);
+$toggle=new PlekitToggle('details','Details');
+$toggle->start();
+$details=new PlekitDetails(plc_is_admin());
$details->start();
-$details->th_td ("Node group name",$nodegroup['groupname']);
+$details->form_start(l_actions(),array("action"=>"update-nodegroup", "nodegroup_id"=>$nodegroup_id));
+$details->th_td ("Node group name",$nodegroup['groupname'],'groupname');
+// can't change the target tag
$details->th_td ("Based on tag",href(l_tag($nodegroup['tag_type_id']),$tagname));
-$details->th_td("Matching value",$nodegroup['value']);
+$details->th_td("Matching value",$nodegroup['value'],'value');
$details->th_td("# nodes",count($nodegroup['node_ids']));
+$details->tr_submit("submit","Update Nodegroup");
+$details->form_end();
$details->end();
+$toggle->end();
+
// xxx : add & delete buttons would make sense here too
$toggle=new PlekitToggle('nodes',"Nodes");
$toggle->start();
require_once 'plc_functions.php';
require_once 'linetabs.php';
require_once 'table.php';
+require_once 'form.php';
// --------------------
// recognized URL arguments
return;
}
+$headers=array();
+$notes=array();
-$headers = array ( "Name"=>"string",
- "Tag"=>"string",
- "Value"=>"string",
- "Nodes"=>"int");
+$headers['group name']='string';
+$headers['tag name']='string';
+$headers['tag value']='string';
+$headers['# N']='int';
+$notes []= '# N = number of nodes in the group';
+$headers["Id"]="int";
+if (plc_is_admin()) $headers[plc_delete_icon()]="none";
+
+$form=new PlekitForm(l_actions(),NULL);
+$form->start();
# initial sort on groupname
-$table=new PlekitTable("nodegroups",$headers,0);
+$table=new PlekitTable("nodegroups",$headers,0,array('notes'=>$notes));
$table->start();
foreach ($nodegroups as $nodegroup) {
$table->row_start();
- $table->cell (href(l_nodegroup($nodegroup['nodegroup_id']),$nodegroup['groupname']));
+ $nodegroup_id=$nodegroup['nodegroup_id'];
+ $table->cell (href(l_nodegroup($nodegroup_id),$nodegroup['groupname']));
// yes, a nodegroup is not a tag, but knows enough for this to work
$table->cell (l_tag_obj($nodegroup));
$table->cell ($nodegroup['value']);
$table->cell (count($nodegroup['node_ids']));
+ $table->cell ($nodegroup_id);
+ $table->cell ($form->checkbox_html('nodegroup_ids[]',$nodegroup_id));
$table->row_end();
}
+$table->tfoot_start();
+
+$table->row_start();
+$table->cell($form->submit_html ("delete-nodegroups","Remove groups"),
+ array('hfill'=>true,'align'=>'right'));
+$table->row_end();
+
+// an inline area to add a tag type
+$table->row_start();
+
+// build the tagname selector
+$relevant_tags = $api->GetTagTypes( array("category"=>'*node*'));
+function selector_argument ($tt) { return array('display'=>$tt['tagname'],"value"=>$tt['tag_type_id']); }
+$selectors=array_map("selector_argument",$relevant_tags);
+$tagname_input=$form->select_html("tag_type_id",$selectors,array('label'=>"Tag Name"));
+
+
+$table->cell($form->text_html('groupname',''));
+$table->cell($tagname_input);
+$table->cell($form->text_html('value',''));
+$table->cell($form->submit_html("add-nodegroup","Add"),3);
+$table->row_end();
+
$table->end();
+$form->end();
//plekit_linetabs ($tabs,"bottom");
$filter=array('tag_type_id'=>$tag_type_id);
$node_tags=$api->GetNodeTags($filter);
$interface_tags=$api->GetInterfaceTags($filter);
-$slice_tags=$api->GetSliceTags(array_merge($filter,array("node_id"=>array())));
-$sliver_tags=$api->GetSliceTags(array_merge($filter,array("~node_id"=>array())));
+// split slice tags into 3 families, whether this applies to the whole slice, or a nodegroup, or a node
+// using filters for this purpose does not work out very well, maybe a bug in the filter stuff
+// anyway this is more efficient, and we compute the related node(groups) in the same pass
+$slice_tags=$api->GetSliceTags(array_merge($filter));
+$count_slice=0;
+$count_nodegroup=0;
+$nodegroup_ids=array();
+$count_node=0;
+$node_ids=array();
+foreach ($slice_tags as $slice_tag) {
+ if ($slice_tag['node_id']) {
+ $node_ids []= $slice_tag['node_id'];
+ $count_node += 1;
+ } else if ($slice_tag['nodegroup_id']) {
+ $nodegroup_ids []= $slice_tag['nodegroup_id'];
+ $count_nodegroup += 1;
+ } else {
+ $count_slice += 1;
+ }
+}
+
+$nodes=$api->GetNodes($node_ids,array('hostname','node_id'));
+$node_hash=array();
+foreach ($nodes as $node) $node_hash[$node['node_id']]=$node;
+$nodegroups=$api->GetNodeGroups($nodegroup_ids,array('groupname','nodegroup_id'));
+$nodegroup_hash=array();
+foreach ($nodegroups as $nodegroup) $nodegroup_hash[$nodegroup['nodegroup_id']]=$nodegroup;
+
drupal_set_title("Details for tag type $tagname");
plekit_linetabs($tabs);
// ----------
+$toggle = new PlekitToggle ('details','Details');
+$toggle->start();
$can_update=plc_is_admin();
$details=new PlekitDetails ($can_update);
"tag_type_id"=>$tag_type_id));
$details->start();
$details->th_td("Name",$tagname,"tagname");
-$details->th_td("Category",$category,"category");
-$details->th_td("Description",$description,"description");
+$details->th_td("Category",$category,"category",array('width'=>30));
+$details->th_td("Description",$description,"description",array('width'=>40));
if ($can_update) {
// select the option corresponding with min_role_id
$details->space();
$details->th_td("Used in nodes",count($node_tags));
$details->th_td("Used in interfaces",count($interface_tags));
-$details->th_td("Used in slices",count($slice_tags));
-$details->th_td("Used in slivers",count($sliver_tags));
+$details->th_td("Used in slices/node",$count_node);
+$details->th_td("Used in slices/nodegroup",$count_nodegroup);
+$details->th_td("Used in slices",$count_slice);
$details->end();
$details->form_end();
+$toggle->end();
// common options for tables below
$table_options=array('notes_area'=>false, 'pagesize_area'=>false, 'search_width'=>10);
$toggle->end();
}
-// grouping both kinds of slice tags
-// xxx don't show hostnames yet
-$slice_tags = array_merge ($slice_tags,$sliver_tags);
if (count ($slice_tags)) {
- $toggle=new PlekitToggle('tag_slices',"Slice and sliver tags");
+ $toggle=new PlekitToggle('tag_slices',"Slice tags");
$toggle->start();
- $table=new PlekitTable ("tag_slices",array("Slice"=>"string","value"=>"string","Node id"=>"int"),0,$table_options);
+ $headers=array();
+ $headers["slice"]='string';
+ $headers["value"]='string';
+ $headers["node"]='string';
+ $headers["nodegroup"]='string';
+ $table=new PlekitTable ("tag_slices",$headers,0,$table_options);
$table->start();
foreach ($slice_tags as $slice_tag) {
$table->row_start();
$table->cell(href(l_slice($slice_tag['slice_id']),$slice_tag['name']));
$table->cell($slice_tag['value']);
+
$node_text="all";
- // sliver tag
- if ($slice_tag['node_id'])
- $node_text=l_node($slice_tag['node_id'],$slice_tag['node_id']);
+ if ($slice_tag['node_id']) {
+ $node_id=$slice_tag['node_id'];
+ $node=$node_hash[$node_id];
+ $node_text=l_node_obj($node);
+ }
$table->cell($node_text);
+
+ $nodegroup_text="all";
+ if ($slice_tag['nodegroup_id']) {
+ $nodegroup_id=$slice_tag['nodegroup_id'];
+ $nodegroup=$nodegroup_hash[$nodegroup_id];
+ $nodegroup_text=l_nodegroup_obj($nodegroup);
+ }
+ $table->cell($nodegroup_text);
+
$table->row_end();
}
$table->end();
$tag_types= $api->GetTagTypes($tag_type_filter, $tag_type_columns);
$headers=array();
+$notes=array();
// delete button
$headers['Name']="string";
$headers['Description']="string";
$headers['Category']="string";
-$headers['Min role']="string";
-// xxx ref count would be helpful
-//if (plc_is_admin()) $headers['#']='int';
+$headers['MR']="string";
+$notes []= "MR: Min Role, needed to manage this tag";
+
+// xxx ref count would be helpful but seem too expensive to compute at this stage
+// the individual tag page show those ref counts per type
+
$headers["Id"]="int";
if (plc_is_admin()) $headers[plc_delete_icon()]="none";
$form=new PlekitForm(l_actions(),NULL);
$form->start();
-$table = new PlekitTable("tags",$headers,0);
+$table = new PlekitTable("tags",$headers,0,array('notes'=>$notes));
$table->start();
$roles_hash=plc_role_global_hash($api);
$table->cell($form->textarea_html('description','',$description_width,2));
$table->cell($form->text_html('category',''));
$table->cell($role_input);
- $table->cell($form->submit_html("add-tag-type","Add Type"),2);
+ $table->cell($form->submit_html("add-tag-type","Add"),2);
$table->row_end();
}