Ahogy belemerültem a 1C termékek linuxos működésébe, egy hiányosságot észleltem — a 1C szerverek klaszterének kezelésére szolgáló kényelmes, grafikus, multiplatform eszköz hiányát. E hiányosság orvoslására úgy döntöttem, hogy GUI-t írok a rac konzolos segédprogramhoz. A fejlesztéshez a tcl/tk nyelvet választottam, mivel szerintem ez a legmegfelelőbb erre a feladatra. Íme, néhány érdekes szempontot szeretnék bemutatni ebben az anyagban.
A munkához szükség van a tcl/tk és a 1C disztribúciókra. Mivel azt terveztem, hogy maximálisan kihasználom a tcl/tk alapcsomagjának lehetőségeit külső csomagok használata nélkül, ezért a 8.6.7 verzióra lesz szükség, amely tartalmazza a ttk-t — egy csomag, amely további grafikus elemeket kínál, amelyek közül számunkra elsősorban a ttk::TreeView szükséges, amely lehetővé teszi az adatok megjelenítését fa struktúrában vagy táblázatos (listás) formában. Ezenkívül az új verzióban átalakításra került a kivételekkel való munka (a try parancs, amely a projektben külső parancsok futtatásakor használatos).
A projekt több fájlból áll (bár semmi nem akadályoz meg abban, hogy mindent egy fájlba tegyünk):
rac_gui.cfg — alapértelmezett konfiguráció
rac_gui.tcl — fő indító script
A lib mappában találhatók azok a fájlok, amelyek automatikusan betöltődnek indításkor:
function.tcl — a programok eljárásai
gui.tcl — fő grafikus felület
images.tcl — képkönyvtár base64 formátumban
A rac_gui.tcl fájl indítja el az interpretert, inicializálja a változókat, betölti a modulokat, konfigurációkat és így tovább. A fájl tartalma megjegyzésekkel:
rac_gui.tcl
#!/bin/sh
exec wish "$0" -- "$@"
# Устанавливаем текущий каталог
set dir(root) [pwd]
# Устанавливаем рабочий каталог, если его нет то создаём
set dir(work) [file join $env(HOME) .rac_gui]
if {[file exists $dir(work)] == 0 } {
file mkdir $dir(work)
}
# каталог с модулями
set dir(lib) "[file join $dir(root) lib]"
# загружаем пользовательский конфиг, если он отсутствует, то копируем дефолтный
if {[file exists [file join $dir(work) rac_gui.cfg]] ==0} {
file copy [file join [pwd] rac_gui.cfg] [file join $dir(work) rac_gui.cfg]
}
source [file join $dir(work) rac_gui.cfg]
# Код проверки наличия rac и правильности указания пути в конфиге
# если программа не найдена то будет выведен диалог для указания корректного пути
# и этот путь будет записан в пользовательский конфиг
if {[file exists $rac_cmd] == 0} {
set rac_cmd [tk_getOpenFile -initialdir $env(HOME) -parent . -title "Укажите путь до rac" -initialfile rac]
file copy [file join $dir(work) rac_gui.cfg] [file join $dir(work) rac_gui.cfg.bak]
set orig_file [open [file join $dir(work) rac_gui.cfg.bak] "r"]
set file [open [file join $dir(work) rac_gui.cfg] "w"]
while {[gets $orig_file line] >=0 } {
if {[string match "set rac_cmd*" $line]} {
puts $file "set rac_cmd $rac_cmd"
} else {
puts $file $line
}
}
close $file
close $orig_file
#return "$host:$port"
file delete [file join $dir(work) 1c_srv.cfg.bak]
} else {
puts "Found $rac_cmd"
}
set cluster_user ""
set cluster_pwd ""
set agent_user ""
set agent_pwd ""
## LOAD FILE ##
# Загружаем модули кроме gui.tcl так как его надо загрузить последним
foreach modFile [lsort [glob -nocomplain [file join $dir(lib) *.tcl]]] {
if {[file tail $modFile] ne "gui.tcl"} {
source $modFile
puts "Loaded module $modFile"
}
}
source [file join $dir(lib) gui.tcl]
source [file join $dir(work) rac_gui.cfg]
# Читаем файл со списком серверов 1С
# и добавляем в дерево
if [file exists [file join $dir(work) 1c_srv.cfg]] {
set f [open [file join $dir(work) 1c_srv.cfg] "RDONLY"]
while {[gets $f line] >=0} {
.frm_tree.tree insert {} end -id "server::$line" -text "$line" -values "$line"
}
}Miután minden szükséges dolgot betöltöttünk és ellenőriztük a rac segédprogram létezését, megnyílik a grafikus ablak. A program felülete három elemből áll:
Eszköztár, fa nézet és lista
A 'fa' tartalmát maximálisan hasonlóvá tettem az 1C beépített Windows-oszközkészletéhez.
A program ezen ablakának fő kódja a következő fájlban található:
lib/gui.tcl
# установка размера и положения основного окна
# можно установить в переменную topLevelGeometry в конфиг программы
if {[info exists topLevelGeometry]} {
wm geometry . $topLevelGeometry
} else {
wm geometry . 1024x768
}
# Заголовок окна
wm title . "1C Rac GUI"
wm iconname . "1C Rac Gui"
# иконка окна (берется из файла lib/imges.tcl)
wm iconphoto . tcl
wm protocol . WM_DELETE_WINDOW Quit
wm overrideredirect . 0
wm positionfrom . user
ttk::style theme use clam
# Панель инсрументов
set frm_tool [frame .frm_tool]
pack $frm_tool -side left -fill y
ttk::panedwindow .panel -orient horizontal -style TPanedwindow
pack .panel -expand true -fill both
pack propagate .panel false
ttk::button $frm_tool.btn_add -command Add -image add_grey_32
ttk::button $frm_tool.btn_del -command Del -image del_grey_32
ttk::button $frm_tool.btn_edit -command Edit -image edit_grey_32
ttk::button $frm_tool.btn_quit -command Quit -image quit_grey_32
pack $frm_tool.btn_add $frm_tool.btn_del $frm_tool.btn_edit -side top -padx 5 -pady 5
pack $frm_tool.btn_quit -side bottom -padx 5 -pady 5
# Дерево с полосами прокрутки
set frm_tree [frame .frm_tree]
ttk::scrollbar $frm_tree.hsb1 -orient horizontal -command [list $frm_tree.tree xview]
ttk::scrollbar $frm_tree.vsb1 -orient vertical -command [list $frm_tree.tree yview]
set tree [ttk::treeview $frm_tree.tree -show tree
-xscrollcommand [list $frm_tree.hsb1 set] -yscrollcommand [list $frm_tree.vsb1 set]]
grid $tree -row 0 -column 0 -sticky nsew
grid $frm_tree.vsb1 -row 0 -column 1 -sticky nsew
grid $frm_tree.hsb1 -row 1 -column 0 -sticky nsew
grid columnconfigure $frm_tree 0 -weight 1
grid rowconfigure $frm_tree 0 -weight 1
# назначение обработчика нажатия кнопкой мыши
bind $frm_tree.tree <ButtonRelease> "TreePress $frm_tree.tree"
# Список для данных (таблица)
set frm_work [frame .frm_work]
ttk::scrollbar $frm_work.hsb -orient horizontal -command [list $frm_work.tree_work xview]
ttk::scrollbar $frm_work.vsb -orient vertical -command [list $frm_work.tree_work yview]
set tree_work [
ttk::treeview $frm_work.tree_work
-show headings -columns "par val" -displaycolumns "par val"
-xscrollcommand [list $frm_work.hsb set]
-yscrollcommand [list $frm_work.vsb set]
]
# Установка цветов для чередования в таблице
$tree_work tag configure dark -background $color(dark_table_bg)
$tree_work tag configure light -background $color(light_table_bg)
# Размещение элементов на форме
grid $tree_work -row 0 -column 0 -sticky nsew
grid $frm_work.vsb -row 0 -column 1 -sticky nsew
grid $frm_work.hsb -row 1 -column 0 -sticky nsew
grid columnconfigure $frm_work 0 -weight 1
grid rowconfigure $frm_work 0 -weight 1
pack $frm_tree $frm_work -side left -expand true -fill both
#.panel add $frm_tool -weight 1
.panel add $frm_tree -weight 1
.panel add $frm_work -weight 1
A program használatának algoritmusa a következő:
1. Először is, hozzá kell adni a klaszter fő szerverét (azaz a klaszter vezérlő szerverét (Linux alatt a vezérlés indítása a ' /opt/1C/v8.3/x86_64/ras cluster —daemon' parancs segítségével történik)).
Ehhez kattintson a '+' gombra, majd a megnyíló ablakban adja meg a szerver címét és a portot:

Ezután a fában megjelenik a szerver, amelyre kattintva a klaszterek listája nyílik meg, vagy kapcsolat hibaüzenet jelenik meg.
2. A klaszterre kattintva megnyílik a hozzá tartozó funkciók listája.
3.…
Tehát, ha új klasztert szeretnénk hozzáadni, válasszunk ki bármelyik elérhető elemet a listában, majd kattintsunk a "+" gombra az eszköztáron, és megjelenik az új klaszter hozzáadásának párbeszédpanele:
Az eszköztáron található gombok a kontextustól függően hajtanak végre funkciókat, tehát attól függően, hogy melyik fa- vagy listaelemet választottuk ki, az adott eljárás fog végrehajtódni.
Vegyük például a hozzáadás gombot ("+"):
A gombkód:
ttk::button $frm_tool.btn_add -command Add -image add_grey_32Itt látható, hogy a gomb megnyomásakor végrehajtódik az "Add" eljárás, amelynek kódja:
proc Add {} {
global active_cluster host
# Meghatározzuk a kiválasztott elem azonosítóját
set id [.frm_tree.tree selection]
# Meghatározzuk ennek az elemnek az értékét
set values [.frm_tree.tree item [.frm_tree.tree selection] -values]
set key [lindex [split $id "::"] 0]
# Attól függően, hogy mit választottunk, a megfelelő eljárás fog elindulni
if {$key eq "" || $key eq "server"} {
set host [ Add::server ]
return
}
Add::$key .frm_tree.tree $host $values
}Itt máris látható az egyik előnye a ticknek — a procedúra nevébe átadhatjuk a változó értékét:
Add::$key .frm_tree.tree $host $valuesTehát például, ha a fő szerverre kattintunk és rákattintunk a "+" gombra, akkor az Add::server eljárás indul el, ha a klaszterre kattintunk — Add::cluster és így tovább (arról, hogy honnan származnak a szükséges "kulcsok", kicsit később írok), a felsorolt eljárások a kontextusnak megfelelő grafikai elemeket rajzolják ki.
Ahogy már észlelhették, az űrlapok stílusa hasonló — nem meglepő, hiszen egy eljárás generálja őket, pontosabban az űrlap alapvázát (ablak, gombok, kép, címke), a procedúra neve: AddTopLevel
proc AddToplevel {lbl img {win_name .add}} {
set cmd "destroy $win_name"
if [winfo exists $win_name] {destroy $win_name}
toplevel $win_name
wm title $win_name $lbl
wm iconphoto $win_name tcl
# címke ikonképpel
ttk::label $win_name.lbl -image $img
# keret a beviteli mezőkkel
set frm [ttk::labelframe $win_name.frm -text $lbl -labelanchor nw]
grid columnconfigure $frm 0 -weight 1
grid rowconfigure $frm 0 -weight 1
# keret és gombok
set frm_btn [frame $win_name.frm_btn -border 0]
ttk::button $frm_btn.btn_ok -image ok_grey_24 -command { }
ttk::button $frm_btn.btn_cancel -command $cmd -image quit_grey_24
grid $win_name.lbl -row 0 -column 0 -sticky nw -padx 5 -pady 10
grid $frm -row 0 -column 1 -sticky nw -padx 5 -pady 5
grid $frm_btn -row 1 -column 1 -sticky se -padx 5 -pady 5
pack $frm_btn.btn_cancel -side right
pack $frm_btn.btn_ok -side right -padx 10
return $frm
}
A hívási paraméterek: cím, ikon kép neve a könyvtárból (lib/images.tcl) és egy opcionális ablaknév paraméter (alapértelmezés szerint .add). Így, ha a fenti példákat vesszük a fő kiszolgáló és a klaszter hozzáadásához, akkor a hívás ennek megfelelően lesz:
AddToplevel "A fő kiszolgáló hozzáadása" server_grey_64vagy
AddToplevel "Klaszter hozzáadása" cluster_grey_64Azzal a példával folytatva bemutatom azokat a procedúrákat, amelyek a kiszolgáló vagy klaszter hozzáadási párbeszédablakait jelenítik meg.
Add::server
proc Add::server {} {
global default
# megjelenítjük a fő űrlapot
set frm [AddToplevel "A fő kiszolgáló hozzáadása" server_grey_64]
# hozzájárulások és beviteli mezők hozzáadása ehhez az űrlaphoz
label $frm.lbl_host -text "A kiszolgáló címe"
entry $frm.ent_host
label $frm.lbl_port -text "Port"
entry $frm.ent_port
$frm.ent_port insert end $default(port)
grid $frm.lbl_host -row 0 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.ent_host -row 0 -column 1 -sticky nsew -padx 5 -pady 5
grid $frm.lbl_port -row 1 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.ent_port -row 1 -column 1 -sticky nsew -padx 5 -pady 5
grid columnconfigure $frm 0 -weight 1
grid rowconfigure $frm 0 -weight 1
#set frm_btn [frame .add.frm_btn -border 0]
# átkonfiguráljuk a gombnyomás kezelőt
.add.frm_btn.btn_ok configure -command {
set host [SaveMainServer [.add.frm.ent_host get] [.add.frm.ent.port get]]
.frm_tree.tree insert {} end -id "server::$host" -text "$host" -values "$host"
destroy .add
return $host
}
return $frm
}Add::cluster
proc Add::cluster {tree host values} {
global default lifetime_limit expiration_timeout session_fault_tolerance_level
global max_memory_size max_memory_time_limit errors_count_threshold security_level
global load_balancing_mode kill_problem_processes
agent_user agent_pwd cluster_user cluster_pwd auth_agent
if {$agent_user ne "" && $agent_pwd ne ""} {
set auth_agent "--agent-user=$agent_user --agent-pwd=$agent_pwd"
} else {
set auth_agent ""
}
# beállítjuk a globális változókat ()
set lifetime_limit $default(lifetime_limit)
set expiration_timeout $default(expiration_timeout)
set session_fault_tolerance_level $default(session_fault_tolerance_level)
set max_memory_size $default(max_memory_size)
set max_memory_time_limit $default(max_memory_time_limit)
set errors_count_threshold $default(errors_count_threshold)
set security_level [lindex $default(security_level) 0]
set load_balancing_mode [lindex $default(load_balancing_mode) 0]
set frm [AddToplevel "Kluster hozzáadása" cluster_grey_64]
label $frm.lbl_host -text "Fő szerver címe"
entry $frm.ent_host
label $frm.lbl_port -text "Port"
entry $frm.ent_port
$frm.ent_port insert end $default(port)
label $frm.lbl_name -text "Kluster neve"
entry $frm.ent_name
label $frm.lbl_secure_connect -text "Biztonságos kapcsolat"
ttk::combobox $frm.cb_security_level -textvariable security_level -values $default(security_level)
label $frm.lbl_expiration_timeout -text "Kikapcsolt folyamatok leállítása után:"
entry $frm.ent_expiration_timeout -textvariable expiration_timeout
label $frm.lbl_session_fault_tolerance_level -text "Hibatűrés"
entry $frm.ent_session_fault_tolerance_level -textvariable session_fault_tolerance_level
label $frm.lbl_load_balancing_mode -text "Terheléselosztási mód"
ttk::combobox $frm.cb_load_balancing_mode -textvariable load_balancing_mode
-values $default(load_balancing_mode)
label $frm.lbl_errors_count_threshold -text "Megengedett hibaszám eltérés, %"
entry $frm.ent_errors_count_threshold -textvariable errors_count_threshold
label $frm.lbl_processes -text "Munka folyamatok:"
label $frm.lbl_lifetime_limit -text "Újraindítási időszak, mp."
entry $frm.ent_lifetime_limit -textvariable lifetime_limit
label $frm.lbl_max_memory_size -text "Megengedett memória mennyisége, KB"
entry $frm.ent_max_memory_size -textvariable max_memory_size
label $frm.lbl_max_memory_time_limit -text "Maximális memória túllépés időtartama, mp."
entry $frm.ent_max_memory_time_limit -textvariable max_memory_time_limit
label $frm.lbl_kill_problem_processes -justify left -anchor nw -text "Problémás folyamatok erőltetett leállítása"
checkbutton $frm.check_kill_problem_processes -variable kill_problem_processes -onvalue yes -offvalue no
grid $frm.lbl_host -row 0 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.ent_host -row 0 -column 1 -sticky nsew -padx 5 -pady 5
grid $frm.lbl_port -row 1 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.ent_port -row 1 -column 1 -sticky nsew -padx 5 -pady 5
grid $frm.lbl_name -row 2 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.ent_name -row 2 -column 1 -sticky nsew -padx 5 -pady 5
grid $frm.lbl_secure_connect -row 3 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.cb_security_level -row 3 -column 1 -sticky nsew -padx 5 -pady 5
grid $frm.lbl_expiration_timeout -row 4 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.ent_expiration_timeout -row 4 -column 1 -sticky nsew -padx 5 -pady 5
grid $frm.lbl_session_fault_tolerance_level -row 5 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.ent_session_fault_tolerance_level -row 5 -column 1 -sticky nsew -padx 5 -pady 5
grid $frm.lbl_load_balancing_mode -row 6 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.cb_load_balancing_mode -row 6 -column 1 -sticky nsew -padx 5 -pady 5
grid $frm.lbl_errors_count_threshold -row 7 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.ent_errors_count_threshold -row 7 -column 1 -sticky nsew -padx 5 -pady 5
grid $frm.lbl_processes -row 8 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.lbl_lifetime_limit -row 9 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.ent_lifetime_limit -row 9 -column 1 -sticky nsew -padx 5 -pady 5
grid $frm.lbl_max_memory_size -row 10 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.ent_max_memory_size -row 10 -column 1 -sticky nsew -padx 5 -pady 5
grid $frm.lbl_max_memory_time_limit -row 11 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.ent_max_memory_time_limit -row 11 -column 1 -sticky nsew -padx 5 -pady 5
grid $frm.lbl_kill_problem_processes -row 12 -column 0 -sticky nw -padx 5 -pady 5
grid $frm.check_kill_problem_processes -row 12 -column 1 -sticky nw -padx 5 -pady 5
# átírjuk a kezelőt
.add.frm_btn.btn_ok configure -command {
RunCommand "" "cluster insert
--host=[.add.frm.ent_host get]
--port=[.add.frm.ent_port get]
--name=[.add.frm.ent_name get]
--expiration-timeout=$expiration_timeout
--lifetime-limit=$lifetime_limit
--max-memory-size=$max_memory_size
--max-memory-time-limit=$max_memory_time_limit
--security-level=$security_level
--session-fault-tolerance-level=$session_fault_tolerance_level
--load-balancing-mode=$load_balancing_mode
--errors-count-threshold=$errors_count_threshold
--kill-problem-processes=$kill_problem_processes
$auth_agent $host"
Run::server $tree $host ""
destroy .add
}
return $frm
}A kódok összehasonlításakor ezeknek az eljárásoknak a között szabad szemmel is látható a különbség, itt a ‘Ok’ gomb kezelőjére szeretnék figyelmet irányítani. A Tk-ban a grafikus elemek tulajdonságait a program futási ideje alatt újra lehet definiálni egy opció segítségével. configurePéldául, a gomb kiírásának eredeti parancsa:
ttk::button $frm_btn.btn_ok -image ok_grey_24 -command { }De a mi formáinkban a parancs az elvárt funkcionális igényektől függ:
.add.frm_btn.btn_ok configure -command {
RunCommand "" "cluster insert
--host=[.add.frm.ent_host get]
--port=[.add.frm.ent_port get]
--name=[.add.frm.ent_name get]
--expiration-timeout=$expiration_timeout
--lifetime-limit=$lifetime_limit
--max-memory-size=$max_memory_size
--max-memory-time-limit=$max_memory_time_limit
--security-level=$security_level
--session-fault-tolerance-level=$session_fault_tolerance_level
--load-balancing-mode=$load_balancing_mode
--errors-count-threshold=$errors_count_threshold
--kill-problem-processes=$kill_problem_processes
$auth_agent $host"
Run::server $tree $host ""
destroy .add
}
Az előző példában a ‘gomb’ elindítja a klaszter hozzáadó eljárást.
Itt érdemes egy kis kitérőt tenni a Tk grafikus elemeivel való munkával kapcsolatban – különböző adatbeviteli elemek (entry, combobox, checkbutton, stb.) esetében bevezetésre került egy olyan paraméter, mint a szöveges változó (textvariable):
entry $frm.ent_lifetime_limit -textvariable lifetime_limitEz a változó a globális névtérben van definiálva, és tartalmazza az aktuálisan beírt értéket. Vagyis a beírt szöveg megszerzéséhez egyszerűen letartóztatni kell a megfelelő változó értékét (természetesen feltéve, hogy az létre lett hozva az elem létrehozásakor).
A második módszer a beírt szöveg (entry típusú elemeknél) megszerzésére a get parancs használata:
.add.frm.ent_name getMindkét módszert láthatjuk a fentebb említett kódban.
Ez a gomb, jelen esetben, elindítja a RunCommand eljárást a klaszter hozzáadásához szükséges parancssorral a rac terminológiában:
/opt/1C/v8.3/x86_64/rac cluster insert --host=localhost --port=1540 --name=dsdsds --expiration-timeout=0 --lifetime-limit=0 --max-memory-size=0 --max-memory-time-limit=0 --security-level=0 --session-fault-tolerance-level=0 --load-balancing-mode=performance --errors-count-threshold=0 --kill-problem-processes=no localhost:1545Most pedig elérkeztünk a fő parancshoz, amely a rac megfelelő paraméterekkel való elindítását irányítja, valamint a parancsok kimenetét listákra bontja, és visszaadja, ha szükséges:
RunCommand
proc RunCommand {root par} {
global dir rac_cmd cluster work_list_row_count agent_user agent_pwd cluster_user cluster_pwd
puts "$rac_cmd $par"
set work_list_row_count 0
# csatorna megnyitása nem blokkoló módban
# $rac - teljes útvonalú parancs
# $par - generált indító kulcsok és opciók
set pipe [open "|$rac_cmd $par" "r"]
try {
set lst ""
set l ""
# a parancs kimenetét egy listába adjuk hozzá
while {[gets $pipe line] >= 0} {
#puts $line
if {$line eq ""} {
lappend l $lst
set lst ""
} else {
lappend lst [string trim $line]
}
}
close $pipe
return $l
} on error {result options} {
# Hibakezelő eljárás indítása
ErrorParcing $result $options
return ""
}
}A fő szerver adatainak megadása után az hozzáadódik a fába, amelyért a fent említett Add:server eljárás következő kódja felelős:
.frm_tree.tree insert {} end -id "server::$host" -text "$host" -values "$host"Most, ha rákattintunk a fa szerver nevére, megkapjuk azokat a klasztereket, amelyek az adott szerver által kezelt nagy szerverek, míg a klaszterre kattintva megkapjuk a klaszter elemeinek listáját (szerverek, információs bázisok stb.). Ez a TreePress eljárásban van megvalósítva (fájl lib/function.tcl):
proc TreePress {tree} {
global host server active_cluster infobase
# a kijelölt elem meghatározása
set id [$tree selection]
# a szükséges globális változók beállítása
SetGlobalVarFromTreeItems $tree $id
# A kulcs és az érték, azaz a kiválasztott elem típusa
set values [$tree item $id -values]
set key [lindex [split $id "::"] 0]
# és attól függően, hogy mit választottunk, a megfelelő eljárás indul el
# a Run névtartományban
Run::$key $tree $host $values
}Ezért a fő szerver esetén a Run::server fog elindulni (a klaszterről — Run::cluster, a munkaszerverről — Run::work_server stb.). Tehát a $key változó értéke a fa elem nevének egy része, amelyet az opció határoz meg -id.
Irányítsuk figyelmünket az eljárásra
Run::server
proc Run::server {tree host values} {
# a szerverhez szükséges klaszterek listájának lekérése
set lst [RunCommand server::$host "cluster list $host"]
if {$lst eq ""} {return}
set l [lindex $lst 0]
#puts $lst
# felesleges elemek eltávolítása a listából
.frm_work.tree_work delete [ .frm_work.tree_work children {}]
# a lista beolvasása
foreach cluster_list $lst {
# Az értékek kitöltése a listába
InsertItemsWorkList $cluster_list
# a kimenet (lista) feldolgozása az adatok fának való hozzáadásához
foreach i $cluster_list {
#puts $i
set cluster_list [split $i ":"]
if {[string trim [lindex $cluster_list 0]] eq "cluster"} {
set cluster_id [string trim [lindex $cluster_list 1]]
lappend cluster($cluster_id) $cluster_id
}
if {[string trim [lindex $cluster_list 0]] eq "name"} {
lappend cluster($cluster_id) [string trim [lindex $cluster_list 1]]
}
}
}
# a klaszterek hozzáadása a fához
foreach x [array names cluster] {
set id [lindex $cluster($x) 0]
if { [$tree exists "cluster::$id"] == 0 } {
$tree insert "server::$host" end -id "cluster::$id" -text "[lindex $cluster($x) 1]" -values "$id"
# elemek hozzáadása a klaszterhez
InsertClusterItems $tree $id
}
}
if { [$tree exists "agent_admins::$id"] == 0 } {
$tree insert "server::$host" end -id "agent_admins::$id" -text "Adminisztrátorok" -values "$id"
#InsertClusterItems $tree $id
}
}Ez a folyamat feldolgozza, amit a szervertől kaptunk a RunCommand parancs útján, és különféle elemeket ad hozzá a fához — klaszterek, különböző gyökérelemek (adatbázisok, munkaszerverek, munkamenetek stb.). Ha alaposan megnézzük, észrevehetjük az InsertItemsWorkList eljárás hívását. Ez az eljárás a grafikus listába való elemek hozzáadására szolgál, feldolgozva a rac konzolsegéd kimenetét, amely korábban listaként került tárolásra a $lst változóban. Ez egy listákból álló lista, amely elempárokat tartalmaz, kettősponttal elválasztva.
Például a klaszter kapcsolatok listája:
svk@svk ~]$ /opt/1C/v8.3/x86_64/rac connection list --cluster=783d2170-56c3-11e8-c586-fc75165efbb2 localhost:1545
connection : dcf5991c-7d24-11e8-1690-fc75165efbb2
conn-id : 0
host : svk.home
process : 79de2e16-56c3-11e8-c586-fc75165efbb2
infobase : 00000000-0000-0000-0000-000000000000
application : "JobScheduler"
connected-at : 2018-07-01T14:49:51
session-number : 0
blocked-by-ls : 0
connection : b993293a-7d24-11e8-1690-fc75165efbb2
conn-id : 0
host : svk.home
process : 79de2e16-56c3-11e8-c586-fc75165efbb2
infobase : 00000000-0000-0000-0000-000000000000
application : "JobScheduler"
connected-at : 2018-07-01T14:48:52
session-number : 0
blocked-by-ls : 0
Grafikus formában így fog kinézni:
A fent említett eljárás kiemeli az elemek nevének címét és az adatokhoz szükséges információkat a táblázat kitöltéséhez:
InsertItemsWorkList
proc InsertItemsWorkList {lst} {
global work_list_row_count
# színek váltakozásának beállítása a sorhoz
if [expr $work_list_row_count % 2] {
set tag dark
} else {
set tag light
}
# sorok kulcs - érték párokra bontása
foreach i $lst {
if [regexp -nocase -all -- {(D+)(s*?|)(:)(s*?|)(.*)} $i match param v2 v3 v4 value] {
lappend column_list [string trim $param]
lappend value_list [string trim $value]
}
}
# táblázat kitöltése
.frm_work.tree_work configure -columns $column_list -displaycolumns $column_list
.frm_work.tree_work insert {} end -values $value_list -tags $tag
.frm_work.tree_work column #0 -stretch
# fejléc beállítása
foreach j $column_list {
.frm_work.tree_work heading $j -text $j
}
incr work_list_row_count
}Itt a sima parancs [split $str «:»] helyett, amely a kettőspont által elválasztott elemekre bontja a sztringet és visszaad egy listát, reguláris kifejezést használtunk, mivel néhány elem szintén kettőspontot tartalmaz.
Az InsertClusterItems eljárás (az ilyen eljárások egyike) egyszerűen hozzáadja a fát a megfelelő cluster elemeihez egy lista gyermek elemmel a megfelelő azonosítókkal.
InsertClusterItems
proc InsertClusterItems {tree id} {
set parent "cluster::$id"
$tree insert $parent end -id "infobases::$id" -text "Információs alapok" -values "$id"
$tree insert $parent end -id "servers::$id" -text "Munkaszerverek" -values "$id"
$tree insert $parent end -id "admins::$id" -text "Adminisztrátorok" -values "$id"
$tree insert $parent end -id "managers::$id" -text "Cluster menedzserek" -values $id
$tree insert $parent end -id "processes::$id" -text "Munkafolyamatok" -values "workprocess-all"
$tree insert $parent end -id "sessions::$id" -text "Munkamenetek" -values "sessions-all"
$tree insert $parent end -id "locks::$id" -text "Zárolások" -values "blocks-all"
$tree insert $parent end -id "connections::$id" -text "Kapcsolatok" -values "connections-all"
$tree insert $parent end -id "profiles::$id" -text "Biztonsági profilok" -values $id
}
Két további megvalósítási változat is bemutatható, ahol világosan látható, hogyan lehet optimalizálni és megszabadulni a megismétlődő parancsoktól:
Ebben az eljárásban a hozzáadás és a ellenőrzés nyíltan van megoldva:
InsertBaseItems
proc InsertBaseItems {tree id} {
set parent "infobase::$id"
if { [$tree exists "sessions::$id"] == 0 } {
$tree insert $parent end -id "sessions::$id" -text "Munkamenetek" -values "$id"
}
if { [$tree exists "locks::$id"] == 0 } {
$tree insert $parent end -id "locks::$id" -text "Zárolások" -values "$id"
}
if { [$tree exists "connections::$id"] == 0 } {
$tree insert $parent end -id "connections::$id" -text "Kapcsolatok" -values "$id"
}
}
Itt a megközelítés helyesebb:
InsertProfileItems
proc InsertProfileItems {tree id} {
set parent "profile::$id"
set lst {
{dir "Virtuális mappák"}
{com "Engedélyezett COM-osztályok"}
{addin "Külső komponensek"}
{module "Külső jelentések és feldolgozások"}
{app "Engedélyezett alkalmazások"}
{inet "Internet erőforrások"}
}
foreach i $lst {
append item [lindex $i 0] "::$id"
if { [$tree exists $item] == 0 } {
$tree insert $parent end -id $item -text [lindex $i 1] -values "$id"
}
unset item
}
}A ciklus alkalmazásában rejlik a különbség, amelyben a visszatérő parancsok (parancs) végrehajtását biztosítja. Melyik megközelítést alkalmazzuk, az már a fejlesztő döntése.
Az elemek hozzáadása és az adatok megszerzése után itt az ideje a szerkesztésre összpontosítani. Mivel a szerkesztéshez és a hozzáadáshoz alapvetően ugyanazokat a paramétereket használjuk (kivételt képez az információs adatbázis), így a párbeszédformák is azonosak. A hozzáadási eljárások hívásának algoritmusa a következő:
Add::$key->AddToplevel
Szerkesztéshez pedig így néz ki:
Edit::$key->Add::$key->AddTopLevel
Példaként vegyük a klaszter szerkesztését, azaz a fában a klaszter nevére kattintva megnyomjuk a szerkesztés gombot az eszköztáron (ceruza ikon), és a megfelelő űrlap jelenik meg a képernyőn:

Edit::cluster
proc Edit::cluster {tree host values} {
global default lifetime_limit expiration_timeout session_fault_tolerance_level
global max_memory_size max_memory_time_limit errors_count_threshold security_level
global load_balancing_mode kill_problem_processes active_cluster
agent_user agent_pwd cluster_user cluster_pwd auth
if {$cluster_user ne "" && $cluster_pwd ne ""} {
set auth "--cluster-user=$cluster_user --cluster-pwd=$cluster_pwd"
} else {
set auth ""
}
# Klauster formájának megrajzolása
set frm [Add::cluster $tree $host $values]
# Szöveg megváltoztatása a cimkén
$frm configure -text "Klauster szerkesztése"
set active_cluster $values
# Adatok lekérése a kiválasztott klastról
set lst [RunCommand cluster::$values "cluster info --cluster=$active_cluster $host"]
# Mezők kitöltése
FormFieldsDataInsert $frm $lst
# A nem szerkeszthető mezők letiltása
$frm.ent_host configure -state disable
$frm.ent_port configure -state disable
# Kezelő újracsoportosítása
.add.frm_btn.btn_ok configure -command {
RunCommand "" "cluster update
--cluster=$active_cluster $auth
--name=[.add.frm.ent_name get]
--expiration-timeout=$expiration_timeout
--lifetime-limit=$lifetime_limit
--max-memory-size=$max_memory_size
--max-memory-time-limit=$max_memory_time_limit
--security-level=$security_level
--session-fault-tolerance-level=$session_fault_tolerance_level
--load-balancing-mode=$load_balancing_mode
--errors-count-threshold=$errors_count_threshold
--kill-problem-processes=$kill_problem_processes
$auth $host"
$tree delete "cluster::$active_cluster"
Run::server $tree $host ""
destroy .add
}
}A kódban található megjegyzések alapján lényegében minden világos, kivéve, hogy a gombkezelő kódja át lett definiálva, és van egy FormFieldsDataInsert eljárás, amely feltölti a mezőket adatokkal és inicializálja a változókat:
FormFieldsDataInsert
proc FormFieldsDataInsert {frm lst} {
foreach i [lindex $lst 0] {
# Paraméterek és értékek listájának lekérése
if [regexp -nocase -all -- {(D+)(s*?|)(:)(s*?|)(.*)} $i match param v2 v3 v4 value] {
# Karakterek megváltoztatása
regsub -all -- "-" [string trim $param] "_" entry_name
# Adatok feltöltése
if [winfo exists $frm.ent_$entry_name] {
$frm.ent_$entry_name delete 0 end
$frm.ent_$entry_name insert end [string trim $value """]
}
if [winfo exists $frm.cb_$entry_name] {
global $entry_name
set $entry_name [string trim $value """]
}
# A jelölőnégyzetekhez az értékek megváltoztatása
if [winfo exists $frm.check_$entry_name] {
global $entry_name
if {$value eq "0"} {
set $entry_name no
} elseif {$value eq "1"} {
set $entry_name yes
} else {
set $entry_name $value
}
}
}
}
}
Ezekben a folyamatokban felmerült egy másik előny, hogy a tcl esetében a változók neve helyett más változók értékei kerülnek behelyettesítésre. Azaz a űrlapok automatikus kitöltésére és a változók inicializálására vonatkozóan a mezők és változók elnevezései megfelelnek a rac parancssori eszköz kulcsainak és a parancsok kimeneti paramétereinek neveinek, néhány kivétellel — a kötőjelet aláhúzásra cserélve. Például scheduled-jobs-deny megfelel a mezőnek ent_scheduled_jobs_deny és a változónak scheduled_jobs_deny.
A hozzáadási és szerkesztési űrlapok mezőinek összetétele eltérő lehet, például az információs bázissal való munka során:
IB hozzáadása

IB szerkesztése

A Edit::infobase szerkesztési eljárás során a szükséges mezők hozzáadódnak az űrlaphoz, mivel a kód kiterjedt, ezt itt nem hozom.
Hasonlóképpen alakították ki a többi elem hozzáadását, szerkesztését és törlését végző eljárásokat is.
Mivel az eszköz működése korlátlan számú szervert, klasztert, információs bázist stb. feltételez, ezért néhány globális változót vezettek be, melyek értékeit minden egyes faelemre való kattintáskor beállítják. Azaz a folyamat rekurzívan átfut az összes szülőelemhez, és beállítja a változókat:
SetGlobalVarFromTreeItems
proc SetGlobalVarFromTreeItems {tree id} {
global host server active_cluster infobase
set parent [$tree parent $id]
set values [$tree item $id -values]
set key [lindex [split $id "::"] 0]
switch -- $key {
server {set host $values}
work_server {set server $values}
cluster {set active_cluster $values}
infobase {set infobase $values}
}
if {$parent eq ""} {
return
} else {
SetGlobalVarFromTreeItems $tree $parent
}
}
A 1C klaszter lehetővé teszi a munkát hitelesítéssel és anélkül. Kétféle adminisztrátor létezik — a klaszter ügynök adminisztrátora és a klaszter adminisztrátora. Ennek megfelelően a helyes működés érdekében további 4 globális változó került bevezetésre, amelyek az adminisztrátor felhasználónevének és jelszavának tárolására szolgálnak. Azaz, ha a klaszterben található egy adminisztrátor felhasználói fiók, megjelenik egy párbeszédablak a felhasználónév és jelszó megadására, az adatok a memóriába kerülnek, és minden parancsban behelyettesítik az adott klaszterre.
Ezt a hibakezelő eljárás kezeli
ErrorParcing
proc ErrorParcing {err opt} {
global cluster_user cluster_pwd agent_user agent_pwd
switch -regexp -- $err {
"A klaszter adminisztrátor nem hitelesített" {
AuthorisationDialog "Klaszter adminisztrátor"
.auth_win.frm_btn.btn_ok configure -command {
set cluster_user [.auth_win.frm.ent_name get]
set cluster_pwd [.auth_win.frm.ent_pwd get]
destroy .auth_win
}
#RunCommand $root $par
}
"A központi szerver adminisztrátor nem hitelesített" {
AuthorisationDialog "A klaszter ügynök adminisztrátor"
.auth_win.frm_btn.btn_ok configure -command {
set agent_user [.auth_win.frm.ent_name get]
set agent_pwd [.auth_win.frm.ent_pwd get]
destroy .auth_win
}
}
"A klaszter adminisztrátor nem hitelesített" {
AuthorisationDialog "Klaszter adminisztrátor"
.auth_win.frm_btn.btn_ok configure -command {
set cluster_user [.auth_win.frm.ent_name get]
set cluster_pwd [.auth_win.frm.ent_pwd get]
destroy .auth_win
}
#RunCommand $root $par
}
"A központi szerver adminisztrátor nem hitelesített" {
AuthorisationDialog "A klaszter ügynök adminisztrátor"
.auth_win.frm_btn.btn_ok configure -command {
set agent_user [.auth_win.frm.ent_name get]
set agent_pwd [.auth_win.frm.ent_pwd get]
destroy .auth_win
}
}
(.+) {
tk_messageBox -type ok -icon error -message "$err"
}
}
}Tehát a visszaadott parancs függvényében lesz a reakció is.
Jelenleg a funkciók 95%-ban megvalósultak, már csak a biztonsági profilok kezelését kell befejezni, na meg tesztelni =). Ennyi lenne. Elnézést a tömör történetért.
A kód, a hagyományokhoz híven, elérhető .
Frissítés: Befejeztem a biztonsági profilokkal való munkát. Most a funkciók 100%-ban megvalósultak.
Frissítés 2: Angol és orosz lokalizálás hozzáadva, teszteltem Win7 alatt.

Forrás: habr.com
