1162 lines
38 KiB
Tcl
1162 lines
38 KiB
Tcl
|
#
|
||
|
# Copyright 2010-2013 the Boeing Company.
|
||
|
# See the LICENSE file included in this distribution.
|
||
|
#
|
||
|
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||
|
#
|
||
|
# GUI support for managing CORE node services.
|
||
|
#
|
||
|
|
||
|
#
|
||
|
# Popup a services configuration dialog box. Similar to popupCapabilityConfig
|
||
|
# but customized for configuring node services. This dialog has two uses:
|
||
|
# (1) selecting the default services for a node type (when session != ""), and
|
||
|
# (2) selecting/customizing services for a certain node
|
||
|
#
|
||
|
proc popupServicesConfig { channel node types values captions possible_values groups session } {
|
||
|
global plugin_img_edit
|
||
|
global g_service_ctls
|
||
|
global g_sent_nodelink_definitions
|
||
|
set wi .popupServicesConfig
|
||
|
catch {destroy $wi}
|
||
|
toplevel $wi
|
||
|
|
||
|
# instead of using vals, the activated services are stored in this list
|
||
|
set activated ""
|
||
|
if { $session != "" } {
|
||
|
global g_node_type_services_hint
|
||
|
if { ![info exists g_node_type_services_hint] } {
|
||
|
set g_node_type_services_hint "router"
|
||
|
}
|
||
|
set title "Default services"
|
||
|
set toptitle "Default services for node type $g_node_type_services_hint"
|
||
|
set activated [getNodeTypeServices $g_node_type_services_hint]
|
||
|
set parent .nodesConfig
|
||
|
} else {
|
||
|
set title "Node [getNodeName $node] ($node) services"
|
||
|
set toptitle $title
|
||
|
set activated [getNodeServices $node true]
|
||
|
set parent .popup
|
||
|
}
|
||
|
if { ![winfo exists $parent] } {
|
||
|
set parent "."
|
||
|
}
|
||
|
wm title $wi $title
|
||
|
wm transient $wi $parent
|
||
|
|
||
|
label $wi.top -text $toptitle
|
||
|
pack $wi.top -side top -padx 4 -pady 4
|
||
|
|
||
|
frame $wi.vals -relief raised -borderwidth 1
|
||
|
|
||
|
set g_sent_nodelink_definitions 0 ;# only send node/link defs once
|
||
|
set g_service_ctls {} ;# list of checkboxes
|
||
|
|
||
|
set n 0
|
||
|
set gn 0
|
||
|
set lastgn -1
|
||
|
foreach type $types {
|
||
|
# group values into frames based on groups TLV
|
||
|
set groupinfo [popupCapabilityConfigGroup $groups [expr {$n + 1}]]
|
||
|
set gn [lindex $groupinfo 0]
|
||
|
set groupcaption [lindex $groupinfo 1]
|
||
|
if { $lastgn != $gn } {
|
||
|
labelframe $wi.vals.$gn -text $groupcaption \
|
||
|
-borderwidth 1 -padx 4 -pady 4
|
||
|
}
|
||
|
frame $wi.vals.$gn.item$n
|
||
|
if {$type != 11} { ;# boolean value
|
||
|
puts "warning: skipping service config [lindex $captions $n]"
|
||
|
incr n
|
||
|
continue
|
||
|
}
|
||
|
set servicename [lindex $captions $n]
|
||
|
global $wi.vals.$gn.item$n.entval
|
||
|
checkbutton $wi.vals.$gn.item$n.ent -width 12 -wraplength 90 \
|
||
|
-variable $wi.vals.$gn.item$n.entval -text $servicename \
|
||
|
-offrelief flat -indicatoron false -overrelief raised
|
||
|
lappend g_service_ctls $wi.vals.$gn.item$n.ent
|
||
|
|
||
|
if { [lsearch -exact $activated [lindex $captions $n]] == -1 } {
|
||
|
set value 0 ;# not in the activated list
|
||
|
} else {
|
||
|
set value 1
|
||
|
}
|
||
|
set $wi.vals.$gn.item$n.entval $value
|
||
|
if { $session == "" } {
|
||
|
set needcustom false
|
||
|
if { $n < [llength $possible_values] } {
|
||
|
if { [lindex $possible_values $n] == 1 } { set needcustom true }
|
||
|
}
|
||
|
set btn $wi.vals.$gn.item$n.custom
|
||
|
button $btn -image $plugin_img_edit \
|
||
|
-command "customizeService $wi $node $servicename $btn"
|
||
|
setCustomButtonColor $btn $node $servicename $needcustom
|
||
|
pack $wi.vals.$gn.item$n.custom -side right -padx 4 -pady 4
|
||
|
# this causes the button for services that require customization to
|
||
|
# turn yellow when the service is selected
|
||
|
$wi.vals.$gn.item$n.ent configure -command \
|
||
|
"setCustomButtonColor $btn $node $servicename $needcustom"
|
||
|
}
|
||
|
pack $wi.vals.$gn.item$n.ent -side right -padx 4 -pady 4
|
||
|
pack $wi.vals.$gn.item$n -side top -anchor e
|
||
|
if { $lastgn != $gn } {
|
||
|
pack $wi.vals.$gn -side left -anchor n -fill both
|
||
|
set lastgn $gn
|
||
|
}
|
||
|
incr n
|
||
|
}; # end foreach
|
||
|
pack $wi.vals.$gn -side left -anchor n -fill both
|
||
|
pack $wi.vals -side top -padx 4 -pady 4
|
||
|
|
||
|
# Apply / Cancel buttons
|
||
|
set apply_cmd "popupServicesConfigApply $wi $channel $node {$session}"
|
||
|
set cancel_cmd "destroy $wi"
|
||
|
frame $wi.btn
|
||
|
button $wi.btn.apply -text "Apply" -command $apply_cmd
|
||
|
button $wi.btn.def -text "Defaults" -command \
|
||
|
"popupServicesConfigDefaults $wi $node {$types} {$captions} {$groups}"
|
||
|
button $wi.btn.cancel -text "Cancel" -command $cancel_cmd
|
||
|
set buttons [list $wi.btn.apply $wi.btn.cancel]
|
||
|
if { $session == "" } {
|
||
|
set buttons [linsert $buttons 1 $wi.btn.def]
|
||
|
}
|
||
|
foreach b $buttons { pack $b -side left -padx 4 -pady 4 }
|
||
|
pack $wi.btn -side bottom
|
||
|
bind $wi <Key-Return> $apply_cmd
|
||
|
bind $wi <Key-Escape> $cancel_cmd
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Save the selection of activated services with the node or in the g_node_types
|
||
|
# array when configuring node type defaults.
|
||
|
#
|
||
|
proc popupServicesConfigApply { wi channel node session } {
|
||
|
set vals [getSelectedServices]
|
||
|
|
||
|
# save default services for a node type into the g_node_types array
|
||
|
if { $session != "" } {
|
||
|
global g_node_types g_node_type_services_hint
|
||
|
set type $g_node_type_services_hint
|
||
|
set idx [getNodeTypeIndex $type]
|
||
|
if { $idx < 0 } {
|
||
|
puts "warning: skipping unknown node type $type"
|
||
|
} else {
|
||
|
set typedata $g_node_types($idx)
|
||
|
set typedata [lreplace $typedata 3 3 $vals]
|
||
|
array set g_node_types [list $idx $typedata]
|
||
|
}
|
||
|
# save the services configured for a specific node
|
||
|
} else {
|
||
|
setNodeServices $node $vals
|
||
|
}
|
||
|
|
||
|
destroy $wi
|
||
|
}
|
||
|
|
||
|
# load the default set of services for this node type
|
||
|
proc popupServicesConfigDefaults { wi node types captions groups } {
|
||
|
set type [getNodeModel $node]
|
||
|
set defaults [getNodeTypeServices $type]
|
||
|
for { set n 0 } { $n < [llength $types] } { incr n } {
|
||
|
set groupinfo [popupCapabilityConfigGroup $groups [expr {$n + 1}]]
|
||
|
set gn [lindex $groupinfo 0]
|
||
|
|
||
|
set val 0
|
||
|
set valname [lindex $captions $n]
|
||
|
if { [lsearch $defaults $valname] != -1 } { set val 1 }
|
||
|
global $wi.vals.$gn.item$n.entval
|
||
|
set $wi.vals.$gn.item$n.entval $val
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Popup a service customization dialog for a given service on a node
|
||
|
# The customize/edit button next to a service has been pressed
|
||
|
#
|
||
|
proc customizeService { wi node service btn } {
|
||
|
global g_sent_nodelink_definitions
|
||
|
global plugin_img_add plugin_img_del plugin_img_folder
|
||
|
global plugin_img_open plugin_img_save
|
||
|
global eventtypes
|
||
|
set selected [getSelectedServices]
|
||
|
# if service is not enabled, enable it here
|
||
|
if { [lsearch -exact $selected $service] == -1 } {
|
||
|
set i [string last ".custom" $btn]
|
||
|
set entval [string replace $btn $i end ".entval"]
|
||
|
global $entval
|
||
|
set $entval 1
|
||
|
lappend selected $service
|
||
|
}
|
||
|
|
||
|
# inform the CORE services about all nodes and links, so it can build
|
||
|
# custom configurations for services
|
||
|
if { $g_sent_nodelink_definitions == 0 } {
|
||
|
set g_sent_nodelink_definitions 1
|
||
|
set sock [lindex [getEmulPlugin $node] 2]
|
||
|
sendEventMessage $sock $eventtypes(definition_state) -1 "" "" 0
|
||
|
sendNodeLinkDefinitions $sock
|
||
|
}
|
||
|
|
||
|
set w .popupServicesCustomize
|
||
|
catch {destroy $w}
|
||
|
toplevel $w
|
||
|
wm transient $w .popupServicesConfig
|
||
|
wm title $w "$service on node [getNodeName $node] ($node)"
|
||
|
|
||
|
ttk::frame $w.top
|
||
|
ttk::label $w.top.lab -text "$service service"
|
||
|
|
||
|
ttk::frame $w.top.meta
|
||
|
ttk::label $w.top.meta.lab -text "Meta-data"
|
||
|
ttk::entry $w.top.meta.ent -width 40
|
||
|
pack $w.top.lab -side top
|
||
|
pack $w.top.meta.lab -side left -padx 4 -pady 4
|
||
|
pack $w.top.meta.ent -fill x -side left -padx 4 -pady 4
|
||
|
pack $w.top.meta -side top
|
||
|
pack $w.top -side top -padx 4 -pady 4
|
||
|
|
||
|
ttk::notebook $w.note
|
||
|
pack $w.note -fill both -expand true -padx 4 -pady 4
|
||
|
ttk::notebook::enableTraversal $w.note
|
||
|
|
||
|
set enableapplycmd "$w.btn.apply configure -state normal"
|
||
|
|
||
|
### Custom ###
|
||
|
# services may define custom popup configuration dialogs invoked here
|
||
|
set custom_popup "popupServiceConfig_$service"
|
||
|
if { [info commands $custom_popup] == $custom_popup } {
|
||
|
$custom_popup $wi $w $node $service $btn
|
||
|
}
|
||
|
|
||
|
### Files ###
|
||
|
ttk::frame $w.note.files
|
||
|
set fileshelp "Config files and scripts that are generated for this"
|
||
|
set fileshelp "$fileshelp service."
|
||
|
ttk::label $w.note.files.help -text $fileshelp
|
||
|
pack $w.note.files.help -side top -anchor w -padx 4 -pady 4
|
||
|
$w.note add $w.note.files -text "Files" -underline 0
|
||
|
|
||
|
ttk::frame $w.note.files.name
|
||
|
ttk::label $w.note.files.name.lab -text "File name:"
|
||
|
set combo $w.note.files.name.combo
|
||
|
ttk::combobox $combo -width 30
|
||
|
set helpercmd "customizeServiceFileHelper $w"
|
||
|
ttk::button $w.note.files.name.add -image $plugin_img_add \
|
||
|
-command "listboxAddDelHelper add $combo $combo true; $helpercmd false"
|
||
|
ttk::button $w.note.files.name.del -image $plugin_img_del \
|
||
|
-command "listboxAddDelHelper del $combo $combo true; $helpercmd true"
|
||
|
pack $w.note.files.name.lab -side left
|
||
|
pack $w.note.files.name.combo -side left -fill x -expand true
|
||
|
foreach c [list add del] {
|
||
|
pack $w.note.files.name.$c -side left
|
||
|
}
|
||
|
pack $w.note.files.name -side top -anchor w -padx 4 -pady 4 -fill x
|
||
|
|
||
|
# copy source file
|
||
|
global g_service_configs_opt
|
||
|
set g_service_configs_opt "use"
|
||
|
set f ${w}.note.files.copy
|
||
|
ttk::frame $f
|
||
|
ttk::radiobutton $f.opt -text "Copy this source file: " \
|
||
|
-value "copy" -variable g_service_configs_opt \
|
||
|
-command "customizeServiceFileOpt $w copy true"
|
||
|
ttk::entry $f.name -width 45
|
||
|
ttk::button $f.btn -image $plugin_img_open -command \
|
||
|
"customizeServiceFileOpt $w copy true; fileButtonPopup $f.name {}"
|
||
|
pack $f.opt $f.name $f.btn -side left -anchor w
|
||
|
pack $f -side top -anchor w -padx 4 -pady 4 -fill x
|
||
|
bind $f.btn <Button> "customizeServiceFileOpt $w copy true"
|
||
|
|
||
|
# use file text
|
||
|
set f ${w}.note.files.use
|
||
|
ttk::frame $f
|
||
|
ttk::radiobutton $f.opt -text "Use text below for file contents:" \
|
||
|
-value "use" -variable g_service_configs_opt \
|
||
|
-command "customizeServiceFileOpt $w use true"
|
||
|
pack $f.opt -side left
|
||
|
foreach c [list open save] {
|
||
|
ttk::button $f.$c -image [set plugin_img_$c] -command \
|
||
|
"customizeServiceFileOpt $w use true; genericOpenSaveButtonPress $c $w.note.files.txt $w.note.files.name.combo"
|
||
|
pack $f.$c -side left
|
||
|
}
|
||
|
pack $f -side top -anchor w -padx 4 -pady 4 -fill x
|
||
|
|
||
|
text $w.note.files.txt -bg white -width 80 -height 10 \
|
||
|
-yscrollcommand "$w.note.files.scroll set" -undo 1
|
||
|
ttk::scrollbar $w.note.files.scroll -command "$w.note.files.txt yview"
|
||
|
|
||
|
pack $w.note.files.txt -side left -fill both -expand true
|
||
|
pack $w.note.files.scroll -side right -fill y
|
||
|
bind $w.note.files.txt <KeyPress> $enableapplycmd
|
||
|
|
||
|
global g_service_configs_tmp g_service_configs_last
|
||
|
if { [info exists g_service_configs_tmp ] } {
|
||
|
array unset g_service_configs_tmp
|
||
|
}
|
||
|
array set g_service_configs_tmp {}
|
||
|
set g_service_configs_last ""
|
||
|
bind $w.note.files.name.combo <<ComboboxSelected>> "$helpercmd true"
|
||
|
bind $w.note.files.name.combo <KeyPress> $enableapplycmd
|
||
|
|
||
|
### Directories ###
|
||
|
ttk::frame $w.note.dirs
|
||
|
$w.note add $w.note.dirs -text "Directories" -underline 0
|
||
|
set helptxt "Directories required by this service that are"
|
||
|
set helptxt "$helptxt unique for each node."
|
||
|
ttk::label $w.note.dirs.help -text $helptxt
|
||
|
pack $w.note.dirs.help -side top -anchor w -padx 4 -pady 4
|
||
|
|
||
|
ttk::treeview $w.note.dirs.tree -height 3 -selectmode browse
|
||
|
$w.note.dirs.tree heading \#0 -text "Per-node directories"
|
||
|
$w.note.dirs.tree insert {} end -id root -text "/" -open true \
|
||
|
-image $plugin_img_folder
|
||
|
ttk::button $w.note.dirs.add -image $plugin_img_add \
|
||
|
-command "customizeServiceDirectoryHelper $w add; $enableapplycmd"
|
||
|
ttk::button $w.note.dirs.del -image $plugin_img_del \
|
||
|
-command "customizeServiceDirectoryHelper $w del; $enableapplycmd"
|
||
|
|
||
|
pack $w.note.dirs.tree -side top -fill both -expand true -padx 4 -pady 4
|
||
|
pack $w.note.dirs.del $w.note.dirs.add -side right
|
||
|
|
||
|
### Startup/shutdown ###
|
||
|
ttk::frame $w.note.ss
|
||
|
$w.note add $w.note.ss -text "Startup/shutdown" -underline 0
|
||
|
|
||
|
global g_service_startup_index
|
||
|
set g_service_startup_index 50
|
||
|
ttk::frame $w.note.ss.si
|
||
|
ttk::label $w.note.ss.si.idxlab -text "Startup index:"
|
||
|
ttk::entry $w.note.ss.si.idxval -width 5 \
|
||
|
-textvariable g_service_startup_index
|
||
|
ttk::scale $w.note.ss.si.idx -from 0 -to 100 -orient horizontal \
|
||
|
-variable g_service_startup_index \
|
||
|
-command "$enableapplycmd; scaleresolution 1 g_service_startup_index"
|
||
|
pack $w.note.ss.si.idxlab $w.note.ss.si.idxval -side left -padx 4 -pady 4
|
||
|
pack $w.note.ss.si.idx -side left -expand true -fill x -padx 4 -pady 4
|
||
|
pack $w.note.ss.si -side top -padx 4 -pady 4 -fill x
|
||
|
|
||
|
global g_service_startup_time
|
||
|
set g_service_startup_time ""
|
||
|
ttk::frame $w.note.ss.st
|
||
|
ttk::label $w.note.ss.st.timelab -text "Start time:"
|
||
|
ttk::entry $w.note.ss.st.timeval -width 5 \
|
||
|
-textvariable g_service_startup_time
|
||
|
set txt "(seconds after runtime; leave empty for default)"
|
||
|
ttk::label $w.note.ss.st.help -text $txt
|
||
|
pack $w.note.ss.st.timelab $w.note.ss.st.timeval $w.note.ss.st.help \
|
||
|
-side left -padx 4 -pady 4
|
||
|
pack $w.note.ss.st -side top -padx 4 -pady 4 -fill x
|
||
|
bind $w.note.ss.st.timeval <KeyPress> $enableapplycmd
|
||
|
|
||
|
|
||
|
set captions "Startup Shutdown Validate"
|
||
|
foreach c "up down val" {
|
||
|
set fr $w.note.ss
|
||
|
set caption [lindex $captions 0]
|
||
|
set captions [lreplace $captions 0 0]
|
||
|
entrylistbox $fr $c "$caption Commands" $enableapplycmd
|
||
|
}
|
||
|
|
||
|
set closecmd "destroy $w; setCustomButtonColor $btn $node $service false"
|
||
|
|
||
|
ttk::frame $w.btn
|
||
|
global g_customize_service_diff_only
|
||
|
set g_customize_service_diff_only 1
|
||
|
ttk::checkbutton $w.btn.diff -variable g_customize_service_diff_only \
|
||
|
-text "only store values that have changed from their defaults"
|
||
|
ttk::button $w.btn.apply -text "Apply" -state disabled \
|
||
|
-command "customizeServiceApply $w $node $service; $closecmd"
|
||
|
ttk::button $w.btn.reset -text "Defaults" \
|
||
|
-command "customizeServiceReset $w $node $service {$selected}; $w.btn.close configure -text Close"
|
||
|
ttk::button $w.btn.copy -text "Copy..." \
|
||
|
-command "customizeServiceCopy $node"
|
||
|
ttk::button $w.btn.close -text "Cancel" -command $closecmd
|
||
|
pack $w.btn.diff -side top
|
||
|
pack $w.btn.apply $w.btn.reset $w.btn.copy $w.btn.close -side left
|
||
|
pack $w.btn -side top -padx 4 -pady 4
|
||
|
|
||
|
# populate dialog values
|
||
|
customizeServiceRefresh $service "$w $node {$selected}"
|
||
|
}
|
||
|
|
||
|
# popup dialog with tree view for copying customized service configuration
|
||
|
# parameters from other nodes
|
||
|
proc customizeServiceCopy { cnode } {
|
||
|
global node_list plugin_img_edit plugin_img_open
|
||
|
|
||
|
set w .popupServicesCopy
|
||
|
catch {destroy $w}
|
||
|
toplevel $w
|
||
|
wm transient $w .popupServicesCustomize
|
||
|
wm title $w "Copy services to node [getNodeName $cnode] ($cnode)"
|
||
|
|
||
|
ttk::frame $w.nodes
|
||
|
ttk::treeview $w.nodes.tree -height 3 -selectmode extended
|
||
|
$w.nodes.tree heading \#0 -text "Service configuration items"
|
||
|
|
||
|
pack $w.nodes.tree -side top -fill both -expand true -padx 4 -pady 4
|
||
|
pack $w.nodes -side top -anchor w -fill both -expand true
|
||
|
|
||
|
ttk::frame $w.btn
|
||
|
ttk::button $w.btn.apply -text "Copy" \
|
||
|
-command "customizeServiceCopyApply $w $cnode"
|
||
|
ttk::button $w.btn.view -text "View" \
|
||
|
-command "customizeServiceCopyView $w $cnode"
|
||
|
ttk::button $w.btn.close -text "Cancel" -command "destroy $w"
|
||
|
pack $w.btn.apply $w.btn.view $w.btn.close -side left
|
||
|
pack $w.btn -side top -padx 4 -pady 4
|
||
|
set tree $w.nodes.tree
|
||
|
|
||
|
foreach node $node_list {
|
||
|
if { $node == $cnode } { continue }
|
||
|
set customCfgList [getCustomConfig $node]
|
||
|
foreach element $customCfgList {
|
||
|
set id [getConfig $element "custom-config-id"]
|
||
|
set parts [split $id :]
|
||
|
if { [lindex $parts 0] != "service" } { continue }
|
||
|
set s [lindex $parts 1]
|
||
|
# insert node into tree
|
||
|
if { ![$tree exists "$node"] } {
|
||
|
set img [getCustomImage $node]
|
||
|
if { $img == "" } {
|
||
|
set model [getNodeModel $node]
|
||
|
set img [getNodeTypeImage $model normal]
|
||
|
}
|
||
|
set img [file tail $img].5
|
||
|
global [set img]
|
||
|
set img [set [set img]]
|
||
|
$tree insert {} end -id "$node" -text $node -open true \
|
||
|
-image $img
|
||
|
}
|
||
|
# insert service name
|
||
|
if { ![$tree exists "$node:$s"] } {
|
||
|
$tree insert $node end -id "$node:$s" -text $s -open true
|
||
|
}
|
||
|
# insert service elements
|
||
|
if { [llength $parts] == 3 } {
|
||
|
set f [lindex $parts 2]
|
||
|
$tree insert "$node:$s" end -id "$node:$s:$f" -text $f \
|
||
|
-image $plugin_img_open
|
||
|
} else {
|
||
|
set cfg [getConfig $element "config"]
|
||
|
foreach c $cfg {
|
||
|
if { [lindex [split $c =] 0] == "files" } { continue }
|
||
|
$tree insert "$node:$s" end -id "$node:$s:$c" -text $c \
|
||
|
-image $plugin_img_edit
|
||
|
}
|
||
|
}
|
||
|
} ;# end foreach element
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# copy selected service configuration items from other nodes to the current
|
||
|
# customize dialog
|
||
|
proc customizeServiceCopyApply { w node } {
|
||
|
global g_service_configs_tmp g_service_configs_last
|
||
|
global g_service_startup_index g_service_startup_time
|
||
|
set tgt .popupServicesCustomize
|
||
|
|
||
|
set tree $w.nodes.tree
|
||
|
set sel [$tree selection]
|
||
|
destroy $w
|
||
|
|
||
|
foreach s $sel {
|
||
|
set parts [split $s :]
|
||
|
set node [lindex $parts 0]; set service [lindex $parts 1]
|
||
|
set item [lindex $parts 2]
|
||
|
# customized file
|
||
|
set f [getCustomService $node "$service:$item"]
|
||
|
if { $f != "" } {
|
||
|
set filedata [join $f "\n"]
|
||
|
array set g_service_configs_tmp [list $item $filedata]
|
||
|
set files [$tgt.note.files.name.combo cget -values]
|
||
|
if { [lsearch $files $item] < 0 } {
|
||
|
lappend files $item
|
||
|
$tgt.note.files.name.combo configure -values $files
|
||
|
}
|
||
|
if { $g_service_configs_last == $item } {
|
||
|
customizeServiceFileDataSet $tgt $filedata
|
||
|
}
|
||
|
# customized parameters
|
||
|
} else {
|
||
|
set kv [splitKeyValue $item]
|
||
|
set key [lindex $kv 0]
|
||
|
set value [lindex $kv 1]
|
||
|
switch -exact -- $key {
|
||
|
meta {
|
||
|
$tgt.top.meta.ent delete 0 end
|
||
|
$tgt.top.meta.ent insert end $value
|
||
|
}
|
||
|
dirs {
|
||
|
foreach dir [tupleStringToList $value] {
|
||
|
set dir [string range $dir 1 end]
|
||
|
treeviewInsert $tgt.note.dirs.tree root [split $dir "/"]
|
||
|
}
|
||
|
}
|
||
|
startidx {
|
||
|
set g_service_startup_index $value
|
||
|
}
|
||
|
cmdup -
|
||
|
cmddown -
|
||
|
cmdval {
|
||
|
set name [string range $key 3 end]
|
||
|
foreach cmd [tupleStringToList $value] {
|
||
|
$tgt.note.ss.$name.cmds.list insert end $cmd
|
||
|
}
|
||
|
}
|
||
|
starttime {
|
||
|
set g_service_startup_time $value
|
||
|
}
|
||
|
default {
|
||
|
puts "warning: didn't copy '$key'"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
# view the customization for comparison with current node
|
||
|
proc customizeServiceCopyView { w node } {
|
||
|
set tree $w.nodes.tree
|
||
|
set sel [$tree selection]
|
||
|
destroy $w
|
||
|
|
||
|
set fn ""
|
||
|
set filedata ""
|
||
|
foreach s $sel {
|
||
|
set parts [split $s :]
|
||
|
set node [lindex $parts 0]; set service [lindex $parts 1]
|
||
|
set item [lindex $parts 2]
|
||
|
# customized file
|
||
|
set f [getCustomService $node "$service:$item"]
|
||
|
if { $f != "" } {
|
||
|
set fn [file join "/tmp" "services.tmp-$node-[file tail $item]"]
|
||
|
set filedata [join $f "\n"]
|
||
|
# customized parameters
|
||
|
} else {
|
||
|
set kv [splitKeyValue $item]
|
||
|
set key [lindex $kv 0]
|
||
|
set value [lindex $kv 1]
|
||
|
set fn [file join "/tmp" "services.tmp-$node-$key"]
|
||
|
set filedata $value
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if { $fn == "" } { return }
|
||
|
|
||
|
if { [catch { set f [open $fn w] } e] } {
|
||
|
puts "error opening file: $fn\n ($e)"
|
||
|
return
|
||
|
}
|
||
|
puts $f $filedata
|
||
|
close $f
|
||
|
|
||
|
popupFileView $fn
|
||
|
}
|
||
|
|
||
|
# helper for add/delete directories from treeview
|
||
|
proc customizeServiceDirectoryHelper { w cmd } {
|
||
|
if { $cmd == "add" } {
|
||
|
set dir [tk_chooseDirectory -mustexist false -initialdir "/" \
|
||
|
-parent $w -title "Add a per-node directory"]
|
||
|
if { $dir == "" } { return }
|
||
|
set dir [string range $dir 1 end] ;# chop off leading slash
|
||
|
treeviewInsert $w.note.dirs.tree root [split $dir "/"]
|
||
|
} elseif { $cmd == "del" } {
|
||
|
set s [$w.note.dirs.tree selection]
|
||
|
if { $s == "root" } { return } ;# may not delete root
|
||
|
$w.note.dirs.tree delete $s ;# delete the current selection
|
||
|
set parents [lreplace [split $s /] end end]
|
||
|
# delete all parents of the selected node if they do not have children
|
||
|
while {[llength $parents] > 1} {
|
||
|
set parent [join $parents "/"]
|
||
|
if { [llength [$w.note.dirs.tree children $parent]] == 0 } {
|
||
|
$w.note.dirs.tree delete $parent
|
||
|
}
|
||
|
set parents [lreplace $parents end end]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# helper for switching files based on combo box selection
|
||
|
proc customizeServiceFileHelper { w clear } {
|
||
|
global g_service_configs_tmp g_service_configs_last
|
||
|
# save old config to array
|
||
|
set cfg [customizeServiceFileDataGet $w]
|
||
|
if { [info exists g_service_configs_last] && \
|
||
|
$g_service_configs_last != "" } {
|
||
|
array set g_service_configs_tmp [list $g_service_configs_last $cfg]
|
||
|
}
|
||
|
set cfgname [$w.note.files.name.combo get]
|
||
|
set g_service_configs_last $cfgname
|
||
|
|
||
|
# populate with new config
|
||
|
if { $clear } {
|
||
|
$w.note.files.txt delete 0.0 end
|
||
|
$w.note.files.copy.name delete 0 end
|
||
|
customizeServiceFileOpt $w "use" false
|
||
|
}
|
||
|
if { ![info exists g_service_configs_tmp($cfgname)] } {
|
||
|
array set g_service_configs_tmp [list $cfgname ""]
|
||
|
} else {
|
||
|
set cfg $g_service_configs_tmp($cfgname)
|
||
|
customizeServiceFileDataSet $w $cfg
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# helper to insert file contents into the text controls
|
||
|
proc customizeServiceFileDataSet { w cfg } {
|
||
|
$w.note.files.txt delete 0.0 end
|
||
|
$w.note.files.copy.name delete 0 end
|
||
|
if { [string range $cfg 0 6] == "file://" } {
|
||
|
customizeServiceFileOpt $w "copy" false
|
||
|
set cfglines [split $cfg "\n"]
|
||
|
set cfg [lindex $cfglines 0] ;# truncate any other lines
|
||
|
$w.note.files.copy.name insert 0 [string range $cfg 7 end]
|
||
|
} else {
|
||
|
customizeServiceFileOpt $w "use" false
|
||
|
$w.note.files.txt insert 0.0 $cfg
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# helper to get file contents from the text controls
|
||
|
proc customizeServiceFileDataGet { w } {
|
||
|
global g_service_configs_opt
|
||
|
if { $g_service_configs_opt == "use" } {
|
||
|
set cfg [$w.note.files.txt get 0.0 end-1c]
|
||
|
} elseif { $g_service_configs_opt == "copy" } {
|
||
|
set cfg [$w.note.files.copy.name get]
|
||
|
set cfg "file://$cfg"
|
||
|
}
|
||
|
return $cfg
|
||
|
}
|
||
|
|
||
|
# helper to set option mode to use/copy
|
||
|
proc customizeServiceFileOpt { w mode enable_apply } {
|
||
|
global g_service_configs_opt
|
||
|
set g_service_configs_opt $mode
|
||
|
if { $mode == "copy" } {
|
||
|
$w.note.files.txt configure -state disabled -bg gray
|
||
|
$w.note.files.copy.name configure -state normal
|
||
|
} else {
|
||
|
$w.note.files.txt configure -state normal -bg white
|
||
|
$w.note.files.copy.name configure -state disabled
|
||
|
}
|
||
|
if { $enable_apply } {
|
||
|
$w.btn.apply configure -state normal
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# create a listbox with a text entry above it, with add/delete buttons and
|
||
|
# a scrollbar
|
||
|
proc entrylistbox { fr name caption extracmd} {
|
||
|
global plugin_img_add plugin_img_del
|
||
|
|
||
|
set c $name
|
||
|
ttk::labelframe $fr.$name -text "$caption"
|
||
|
|
||
|
ttk::frame $fr.$c.edit
|
||
|
ttk::entry $fr.$c.edit.cmd -width 40
|
||
|
ttk::button $fr.$c.edit.add -image $plugin_img_add \
|
||
|
-command "listboxAddDelHelper add $fr.$c.edit.cmd $fr.$c.cmds.list false; $extracmd"
|
||
|
ttk::button $fr.$c.edit.del -image $plugin_img_del \
|
||
|
-command "listboxAddDelHelper del $fr.$c.edit.cmd $fr.$c.cmds.list false; $extracmd"
|
||
|
pack $fr.$c.edit.cmd -side left -fill x -expand true
|
||
|
pack $fr.$c.edit.add $fr.$c.edit.del -side left
|
||
|
|
||
|
ttk::frame $fr.$c.cmds
|
||
|
listbox $fr.$c.cmds.list -height 5 -width 50 \
|
||
|
-yscrollcommand "$fr.$c.cmds.scroll set" -exportselection 0
|
||
|
bind $fr.$c.cmds.list <<ListboxSelect>> "listboxSelect $fr.$c.cmds.list $fr.$c.edit.cmd"
|
||
|
ttk::scrollbar $fr.$c.cmds.scroll -command "$fr.$c.cmds.list yview"
|
||
|
pack $fr.$c.cmds.list -side left -fill both -expand true
|
||
|
pack $fr.$c.cmds.scroll -side left -fill y
|
||
|
pack $fr.$c.edit $fr.$c.cmds -side top -anchor w -fill x
|
||
|
pack $fr.$c -side top -fill x -expand true
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# color the customize/edit button adjacent to each service checkbutton
|
||
|
#
|
||
|
proc setCustomButtonColor { btn node service needcustom } {
|
||
|
set color lightgray ;# default button background color
|
||
|
|
||
|
# color button yellow if enabled and customization is needed
|
||
|
if { $needcustom } {
|
||
|
# button $wi.vals.$gn.item$n.custom / value $wi.vals.$gn.item$n.entval
|
||
|
set i [string last ".custom" $btn]
|
||
|
set entval [string replace $btn $i end ".entval"]
|
||
|
global $entval
|
||
|
if { [set $entval] } {
|
||
|
set color yellow
|
||
|
}
|
||
|
}
|
||
|
if { [getCustomService $node $service] != "" } {
|
||
|
set color green
|
||
|
}
|
||
|
$btn configure -bg $color
|
||
|
}
|
||
|
|
||
|
proc scaleresolution { res var val } {
|
||
|
set factor [expr {1 / $res}]
|
||
|
set val [expr {int($val * $factor) / $factor}]
|
||
|
global $var
|
||
|
set $var $val
|
||
|
return $val
|
||
|
}
|
||
|
|
||
|
# return a list of services that have been selected (checkbox is checked)
|
||
|
proc getSelectedServices { } {
|
||
|
global g_service_ctls
|
||
|
set selected {}
|
||
|
foreach c $g_service_ctls {
|
||
|
global $c
|
||
|
set service [$c cget -text]
|
||
|
set var [$c cget -variable]
|
||
|
global $var
|
||
|
if { [set $var] == 1 } { lappend selected $service }
|
||
|
}
|
||
|
return $selected
|
||
|
}
|
||
|
|
||
|
# send a config request message with the opaque field set to query for all
|
||
|
# service parameters; the opaque field is "service:s5,s2,s3,s4", where service
|
||
|
# s5 is being configured (parseConfMessage will invoke customizeServiceValues)
|
||
|
proc customizeServiceRefresh { var args } {
|
||
|
set args [lindex $args 0]
|
||
|
set w [lindex $args 0]
|
||
|
set node [lindex $args 1]
|
||
|
set services [lindex $args 2]
|
||
|
|
||
|
# move service to the front of the list of services
|
||
|
set i [lsearch $services $var]
|
||
|
if { $i < 0 } {
|
||
|
puts "error: service $var not found in '$services'"
|
||
|
return
|
||
|
} elseif { $i > 0 } {
|
||
|
set services [lreplace $services $i $i]
|
||
|
set services [linsert $services 0 $var]
|
||
|
}
|
||
|
|
||
|
# request service parameters from daemon
|
||
|
set svcstr [join $services ","]
|
||
|
set sock [lindex [getEmulPlugin $node] 2]
|
||
|
sendConfRequestMessage $sock $node services 0x1 -1 "service:$svcstr"
|
||
|
update
|
||
|
}
|
||
|
|
||
|
# this returns a list of values for the service s on node if a custom service
|
||
|
# configuration exists
|
||
|
proc getCustomService { node s } {
|
||
|
set values [getCustomConfigByID $node "service:$s"]
|
||
|
return $values
|
||
|
}
|
||
|
|
||
|
# this helper is invoked upon receiving the reply to the message sent from
|
||
|
# customizeServiceRefresh; it populates the dialog box fields
|
||
|
proc customizeServiceValues { node values services } {
|
||
|
global plugin_img_folder
|
||
|
|
||
|
set service [lindex $services 0]
|
||
|
|
||
|
set w .popupServicesCustomize
|
||
|
if { ![winfo exists $w] } {
|
||
|
# apply config update without dialog box
|
||
|
# this occurs when loading from XML or reconnecting to a session
|
||
|
setCustomConfig $node "service:$service" $service $values 0
|
||
|
return
|
||
|
}
|
||
|
|
||
|
global g_customize_service_values_orig
|
||
|
set g_customize_service_values_orig $values
|
||
|
|
||
|
# merge any custom values with defaults from message
|
||
|
set custom_values [getCustomService $node $service]
|
||
|
set i 0
|
||
|
set has_keys [hasKeyValues $custom_values]
|
||
|
foreach val $custom_values {
|
||
|
if { $has_keys } {
|
||
|
set kv [splitKeyValue $val]
|
||
|
set key [lindex $kv 0]; set value [lindex $kv 1]
|
||
|
set values [setServiceValuesItem $values $key $value]
|
||
|
} else {
|
||
|
set values [lreplace $values $i $i $val]
|
||
|
}
|
||
|
incr i
|
||
|
}
|
||
|
|
||
|
# populate meta-data
|
||
|
set meta [getServiceValuesItem $values "meta" 6]
|
||
|
$w.top.meta.ent delete 0 end
|
||
|
$w.top.meta.ent insert end $meta
|
||
|
|
||
|
# populate Files tab
|
||
|
set files [tupleStringToList [getServiceValuesItem $values "files" 1]]
|
||
|
set chosenfile [lindex $files 0] ;# auto-display first file from list
|
||
|
$w.note.files.name.combo configure -values $files
|
||
|
$w.note.files.name.combo delete 0 end
|
||
|
if { $chosenfile != "" } {
|
||
|
$w.note.files.name.combo insert 0 $chosenfile
|
||
|
}
|
||
|
global g_service_configs_last
|
||
|
set g_service_configs_last $chosenfile
|
||
|
|
||
|
# file data
|
||
|
foreach f $files {
|
||
|
set filedata [join [getCustomService $node "$service:$f"] "\n"]
|
||
|
if { $filedata != "" } {
|
||
|
# use file contents from existing config
|
||
|
customizeServiceFile $node $f "service:$service" $filedata false
|
||
|
} elseif { $f != "" } {
|
||
|
# request the file contents
|
||
|
set svcstr [join $services ","]
|
||
|
set sock [lindex [getEmulPlugin "*"] 2]
|
||
|
set opaque "service:$svcstr:$f"
|
||
|
# this causes customizeServiceFile to be invoked upon reply
|
||
|
sendConfRequestMessage $sock $node services 0x1 -1 $opaque
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# populate Directories tab
|
||
|
set dirs [tupleStringToList [getServiceValuesItem $values "dirs" 0]]
|
||
|
$w.note.dirs.tree delete root
|
||
|
$w.note.dirs.tree insert {} end -id root -text "/" -open true \
|
||
|
-image $plugin_img_folder
|
||
|
foreach dir $dirs {
|
||
|
set dir [string range $dir 1 end] ;# chop off leading slash
|
||
|
treeviewInsert $w.note.dirs.tree root [split $dir "/"]
|
||
|
}
|
||
|
|
||
|
# populate Startup/shutdown tab
|
||
|
set idx [getServiceValuesItem $values "startidx" 2]
|
||
|
global g_service_startup_index
|
||
|
set g_service_startup_index $idx
|
||
|
|
||
|
set valuesidx 3
|
||
|
foreach c "up down val" {
|
||
|
set fr $w.note.ss
|
||
|
$fr.$c.edit.cmd delete 0 end
|
||
|
$fr.$c.cmds.list delete 0 end
|
||
|
set value [getServiceValuesItem $values "cmd$c" $valuesidx]
|
||
|
foreach cmd [tupleStringToList $value] {
|
||
|
if { $cmd != "" } { $fr.$c.cmds.list insert end $cmd }
|
||
|
}
|
||
|
incr valuesidx
|
||
|
}
|
||
|
|
||
|
set starttime [getServiceValuesItem $values "starttime" 6]
|
||
|
global g_service_startup_time
|
||
|
set g_service_startup_time $starttime
|
||
|
|
||
|
# populate any custom service tab
|
||
|
set service [lindex $services 0]
|
||
|
set custom_vals_callback "popupServiceConfig_${service}_vals"
|
||
|
if { [info commands $custom_vals_callback] == $custom_vals_callback } {
|
||
|
$custom_vals_callback $node $values $services $w
|
||
|
}
|
||
|
|
||
|
$w.btn.apply configure -state disabled
|
||
|
}
|
||
|
|
||
|
# extract items from a list of values
|
||
|
# old-style values has an ordered list of values; idx determines key
|
||
|
# new-style values is a list of key=value pairs
|
||
|
proc getServiceValuesItem { values key idx } {
|
||
|
# determine how to handle values
|
||
|
set has_keys [hasKeyValues $values]
|
||
|
if { $has_keys } {
|
||
|
return [getKeyValue $key $values ""]
|
||
|
} else {
|
||
|
return [lindex $values $idx]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# replace a "key=value" pair in a list, returning the list
|
||
|
proc setServiceValuesItem { values key value } {
|
||
|
set i 0
|
||
|
foreach v $values {
|
||
|
set k [lindex [splitKeyValue $v] 0]
|
||
|
if { $k == $key } { break }
|
||
|
incr i
|
||
|
}
|
||
|
if { $i == [llength $values] } {
|
||
|
puts "key not found '$key' in service values"
|
||
|
return $values
|
||
|
}
|
||
|
return [lreplace $values $i $i "$key=$value"]
|
||
|
}
|
||
|
|
||
|
# this helper is invoked upon receiving a File Message in reply to the Config
|
||
|
# Message sent from customizeServiceRefresh; it populates the config file entry
|
||
|
proc customizeServiceFile { node name type data generated} {
|
||
|
global g_service_configs_tmp g_service_configs_tmp_orig
|
||
|
global g_service_configs_last
|
||
|
|
||
|
set w .popupServicesCustomize
|
||
|
if { ![winfo exists $w] } {
|
||
|
# apply file config update without dialog box
|
||
|
# this occurs when loading from XML or reconnecting to a session
|
||
|
# type should be e.g. "service:zebra"
|
||
|
set data [split $data "\n"]
|
||
|
setCustomConfig $node "$type:$name" $name $data 0
|
||
|
return
|
||
|
}
|
||
|
|
||
|
# store file data in array
|
||
|
array set g_service_configs_tmp [list $name $data]
|
||
|
if { $generated } {
|
||
|
array set g_service_configs_tmp_orig [list $name $data]
|
||
|
} else {
|
||
|
array set g_service_configs_tmp_orig [list $name ""]
|
||
|
}
|
||
|
|
||
|
# display file if currently selected
|
||
|
if { $g_service_configs_last == $name } {
|
||
|
customizeServiceFileDataSet $w $data
|
||
|
}
|
||
|
|
||
|
# invoke any custom service callback
|
||
|
set service [string range $type 8 end] ;# assume already checked "service:"
|
||
|
set custom_file_callback "popupServiceConfig_${service}_file"
|
||
|
if { [info commands $custom_file_callback] == $custom_file_callback } {
|
||
|
$custom_file_callback $node $name $data $w
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# helper to recursively add a directory path to a treeview
|
||
|
proc treeviewInsert { tree parent items } {
|
||
|
# pop first item
|
||
|
set item [lindex $items 0]
|
||
|
set items [lreplace $items 0 0]
|
||
|
set img [$tree item $parent -image] ;# adopt icon from parent
|
||
|
if { ![$tree exists "$parent/$item"] } {
|
||
|
$tree insert $parent end -id "$parent/$item" -text $item -open true \
|
||
|
-image $img
|
||
|
}
|
||
|
|
||
|
if { [llength $items] > 0 } {
|
||
|
treeviewInsert $tree "$parent/$item" $items
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# return all children that are leaf nodes in a tree
|
||
|
proc treeviewLeaves { tree parent } {
|
||
|
set leaves ""
|
||
|
set children [$tree children $parent]
|
||
|
if { [llength $children] == 0 } {
|
||
|
return $parent
|
||
|
}
|
||
|
foreach child $children {
|
||
|
set leaves [concat $leaves [treeviewLeaves $tree $child]]
|
||
|
}
|
||
|
return $leaves
|
||
|
}
|
||
|
|
||
|
# apply button pressed on customizeService dialog
|
||
|
proc customizeServiceApply { w node service } {
|
||
|
global g_customize_service_diff_only
|
||
|
|
||
|
catch { $w.btn.apply configure -state disabled }
|
||
|
|
||
|
set values ""
|
||
|
|
||
|
# Directories
|
||
|
set dirs ""
|
||
|
set dirstmp [treeviewLeaves $w.note.dirs.tree root]
|
||
|
foreach dir $dirstmp {
|
||
|
set dir [string replace $dir 0 3] ;# chop off "root" prefix
|
||
|
if { $dir == "" } { continue }
|
||
|
lappend dirs $dir
|
||
|
}
|
||
|
lappend values "dirs=[listToTupleString $dirs]"
|
||
|
|
||
|
# Files
|
||
|
set files [$w.note.files.name.combo cget -values]
|
||
|
lappend values "files=[listToTupleString $files]"
|
||
|
|
||
|
# Startup index
|
||
|
global g_service_startup_index
|
||
|
lappend values "startidx=$g_service_startup_index"
|
||
|
|
||
|
# Startup/shutdown commands
|
||
|
foreach c "up down val" {
|
||
|
set cmds [$w.note.ss.$c.cmds.list get 0 end]
|
||
|
lappend values "cmd$c=[listToTupleString $cmds]"
|
||
|
}
|
||
|
|
||
|
# meta
|
||
|
lappend values "meta=[$w.top.meta.ent get]"
|
||
|
|
||
|
# start time
|
||
|
global g_service_startup_time
|
||
|
lappend values "starttime=$g_service_startup_time"
|
||
|
|
||
|
# remove any existing config files for this service
|
||
|
# this prevents duplicates when files are renamed/deleted
|
||
|
set cfgs [getCustomConfig $node]
|
||
|
foreach cfg $cfgs {
|
||
|
set cid [lindex [lsearch -inline $cfg "custom-config-id *"] 1]
|
||
|
set len [expr {[string length "service:$service:"] - 1}]
|
||
|
if { [string range $cid 0 $len] == "service:$service:" } {
|
||
|
setCustomConfig $node $cid "" "" 1
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# save config files (that have changed)
|
||
|
set trimmed_files {}
|
||
|
global g_service_configs_tmp g_service_configs_tmp_orig
|
||
|
global g_service_configs_last
|
||
|
set cfg [customizeServiceFileDataGet $w]
|
||
|
array set g_service_configs_tmp [list $g_service_configs_last $cfg]
|
||
|
foreach cfgname $files {
|
||
|
if { ![info exists g_service_configs_tmp($cfgname)] } {
|
||
|
puts "missing config for file '$cfgname'"
|
||
|
continue
|
||
|
}
|
||
|
if { [info exists g_service_configs_tmp_orig($cfgname)] } {
|
||
|
if { $g_service_configs_tmp_orig($cfgname) == \
|
||
|
$g_service_configs_tmp($cfgname) } {
|
||
|
# file has not changed
|
||
|
if { $g_customize_service_diff_only } { continue }
|
||
|
}
|
||
|
}
|
||
|
set cfg [split $g_service_configs_tmp($cfgname) "\n"]
|
||
|
setCustomConfig $node "service:$service:$cfgname" $cfgname $cfg 0
|
||
|
lappend trimmed_files $cfgname
|
||
|
}
|
||
|
|
||
|
|
||
|
# store only values that have changed from the defaults
|
||
|
set trimmed {}
|
||
|
global g_customize_service_values_orig
|
||
|
for {set i 0} {$i < [llength $values]} {incr i} {
|
||
|
set value_orig [lindex $g_customize_service_values_orig $i]
|
||
|
set value_orig [tupleStringToList $value_orig]
|
||
|
set value_new [tupleStringToList [lindex $values $i]]
|
||
|
if { $i == 1 } {
|
||
|
# when a file has changed, store all filenames whether or not
|
||
|
# the name(s) have changed
|
||
|
if { [llength $trimmed_files] > 0 } {
|
||
|
lappend trimmed [lindex $values 1]
|
||
|
}
|
||
|
continue
|
||
|
}
|
||
|
if {$value_orig != $value_new} {
|
||
|
lappend trimmed [lindex $values $i]
|
||
|
}
|
||
|
}
|
||
|
if { $g_customize_service_diff_only } {
|
||
|
set values $trimmed
|
||
|
}
|
||
|
unset g_customize_service_values_orig
|
||
|
|
||
|
# save values without config file
|
||
|
setCustomConfig $node "service:$service" $service $values 0
|
||
|
|
||
|
array unset g_service_configs_tmp
|
||
|
array unset g_service_configs_tmp_orig
|
||
|
unset g_service_configs_last
|
||
|
|
||
|
# may want to apply here, if some config validation is implemented or
|
||
|
# runtime applying of service customization
|
||
|
# otherwise this is not necessary due to config being sent upon startup
|
||
|
# also more logic would be needed for using the reset button
|
||
|
#set sock [lindex [getEmulPlugin $node] 2]
|
||
|
#set types [string repeat "10 " [llength $values]]
|
||
|
#sendConfReplyMessage $sock $node services $types $values "service:$service"
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# reset button is pressed on customizeService dialog
|
||
|
#
|
||
|
proc customizeServiceReset { w node service services } {
|
||
|
set cfgnames [$w.note.files.name.combo cget -values]
|
||
|
setCustomConfig $node "service:$service" "" "" 1
|
||
|
foreach cfgname $cfgnames {
|
||
|
setCustomConfig $node "service:$service:$cfgname" "" "" 1
|
||
|
}
|
||
|
|
||
|
customizeServiceRefresh $service [list $w $node $services]
|
||
|
}
|
||
|
|
||
|
# check for old service configs in all nodes
|
||
|
proc upgradeConfigServices {} {
|
||
|
global node_list
|
||
|
foreach node $node_list {
|
||
|
upgradeNodeConfigService $node
|
||
|
upgradeCustomPostConfigCommands $node
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# provide backwards-compatibility with changes to services fields here
|
||
|
proc upgradeNodeConfigService { node } {
|
||
|
set OLD_NUM_FIELDS 7
|
||
|
set cfgs [getCustomConfig $node]
|
||
|
foreach cfg $cfgs {
|
||
|
set cid [lindex [lsearch -inline $cfg "custom-config-id service:*"] 1]
|
||
|
# skip configs that are not a service definition ("service:name")
|
||
|
if { [llength [split $cid :]] != 2 } { continue }
|
||
|
|
||
|
set values [getConfig $cfg config]
|
||
|
if { [llength $values] != [expr {$OLD_NUM_FIELDS-1}] } { continue }
|
||
|
|
||
|
# update from 6 service fields to 7 when introducing validate commands
|
||
|
set service [lindex [split $cid :] 1]
|
||
|
#puts -nonewline "note: updating service $service on $node with empty "
|
||
|
#puts "validation commands"
|
||
|
set values [linsert $values end-1 {}]
|
||
|
setCustomConfig $node "service:$service" $service $values 0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc upgradeCustomPostConfigCommands { node } {
|
||
|
set cfg [getCustomPostConfigCommands $node]
|
||
|
setCustomPostConfigCommands $node {}
|
||
|
if { $cfg == "" } { return }
|
||
|
set cfgname "custom-post-config-commands.sh"
|
||
|
set values "{files=('$cfgname', )} startidx=35 {cmdup=('sh $cfgname', )}"
|
||
|
setCustomConfig $node "service:UserDefined" "UserDefined" $values 0
|
||
|
setCustomConfig $node "service:UserDefined:$cfgname" $cfgname $cfg 0
|
||
|
set services [getNodeServices $node true]
|
||
|
lappend services "UserDefined"
|
||
|
setNodeServices $node $services
|
||
|
puts "adding user-defined custom-post-config-commands.sh service for $node"
|
||
|
|
||
|
}
|
||
|
|
||
|
# populate services menu when right-clicking on a node at runtime
|
||
|
proc addServicesRightClickMenu { m node } {
|
||
|
$m add cascade -label "Services" -menu $m.services
|
||
|
|
||
|
set i 0
|
||
|
set services [getNodeServices $node true]
|
||
|
foreach s $services {
|
||
|
set childmenu $m.services.s$i
|
||
|
incr i
|
||
|
destroy $childmenu
|
||
|
menu $childmenu -tearoff 0
|
||
|
$m.services add cascade -label $s -menu $childmenu
|
||
|
foreach cmd "start stop restart validate" {
|
||
|
$childmenu add command -label $cmd \
|
||
|
-command "sendServiceCmd $node $s $cmd"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc sendServiceCmd { node service cmd } {
|
||
|
global eventtypes
|
||
|
|
||
|
set plugin [lindex [getEmulPlugin "*"] 0]
|
||
|
set sock [pluginConnect $plugin connect true]
|
||
|
|
||
|
if { $cmd == "validate" } { set cmd "pause" }
|
||
|
set type $eventtypes(event_$cmd)
|
||
|
set nodenum [string range $node 1 end]
|
||
|
set name "service:$service"
|
||
|
set data ""
|
||
|
|
||
|
sendEventMessage $sock $type $nodenum $name $data 0
|
||
|
}
|