GUI-t írunk az 1C RAC-hoz, vagy ismét a Tcl / Tk-hoz

Ahogy belemerültünk abba a témába, hogy az 1C termékek hogyan működnek a Linux környezetben, egy hátrányt fedeztek fel - a kényelmes grafikus többplatformos eszköz hiányát az 1C szerverek fürtjének kezelésére. És úgy döntöttek, hogy ezt a hátrányt orvosolják egy grafikus felhasználói felület megírásával a rac konzol segédprogramhoz. Fejlesztési nyelvnek a Tcl/tk-t választottam, mivel véleményem szerint erre a feladatra a legalkalmasabb. Ezért szeretném bemutatni a megoldás néhány érdekes aspektusát ebben az anyagban.

A működéshez tcl/tk és 1C disztribúciókra lesz szükség. És mivel úgy döntöttem, hogy harmadik féltől származó csomagok használata nélkül használom ki a legtöbbet az alap tcl/tk kézbesítés képességeiből, szükségem lesz a 8.6.7-es verzióra, amely tartalmazza a ttk-t - egy olyan csomagot további grafikai elemekkel, amelyek közül elsősorban a ttk-re van szükségünk. ::TreeView, lehetővé teszi az adatok megjelenítését mind fastruktúra, mind táblázat (lista) formájában. Szintén az új verzióban a kivételekkel végzett munka átdolgozásra került (a try parancs, amelyet a projektben külső parancsok futtatásakor használunk).

A projekt több fájlból áll (bár semmi sem akadályozza meg, hogy mindent egyben végezzen):

rac_gui.cfg - alapértelmezett konfiguráció
rac_gui.tcl – fő indítószkript
A lib könyvtár olyan fájlokat tartalmaz, amelyek az indításkor automatikusan betöltődnek:
function.tcl - eljárásokat tartalmazó fájl
gui.tcl - fő grafikus felület
images.tcl - base64 képtár

A rac_gui.tcl fájl valójában elindítja az értelmezőt, 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 letöltött mindent, ami szükséges, és ellenőrizte a rac segédprogram meglétét, egy grafikus ablak jelenik meg. A program felülete három elemből áll:

Eszköztár, fa és lista

A „fa” tartalmát a lehető leghasonlatosabbá tettem az 1C szabványos Windows berendezéséhez.

GUI-t írunk az 1C RAC-hoz, vagy ismét a Tcl / Tk-hoz

Az ablakot alkotó fő kód a 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 programmal való munka algoritmusa a következő:

1. Először is hozzá kell adnia a fő fürtkiszolgálót (azaz a fürtkezelő kiszolgálót (Linux esetén a felügyelet a „/opt/1C/v8.3/x86_64/ras cluster —daemon”) paranccsal indul).

Ehhez kattintson a „+” gombra, és a megnyíló ablakban adja meg a szerver címét és portját:

GUI-t írunk az 1C RAC-hoz, vagy ismét a Tcl / Tk-hoz

Utána rákattintva megjelenik a szerverünk a fában, megnyílik a klaszterek listája, vagy egy kapcsolati hiba jelenik meg.

2. A fürt nevére kattintva megnyílik a számára elérhető funkciók listája.

3. ...

És így tovább, pl. új fürt hozzáadásához válasszon ki egyet a listából, és nyomja meg a „+” gombot az eszköztáron, és megjelenik az új hozzáadása párbeszédpanel:

GUI-t írunk az 1C RAC-hoz, vagy ismét a Tcl / Tk-hoz

Az eszköztár gombjai a kontextustól függő funkciókat látnak el, pl. Attól függően, hogy a fa vagy a lista melyik eleme van kiválasztva, egy vagy másik eljárás kerül végrehajtásra.

Nézzük a hozzáadás gomb („+”) példáját:

Gombgenerációs kód:

ttk::button $frm_tool.btn_add  -command Add  -image add_grey_32

Itt látjuk, hogy a gomb megnyomásakor az „Add” eljárás lefut, ennek kódja:

proc Add {} {
    global active_cluster host
    # Определяем идентификатор выделенного элемента
    set id  [.frm_tree.tree selection] 
    # Определяем значение этого элемента
    set values [.frm_tree.tree item [.frm_tree.tree selection] -values]
    set key [lindex [split $id "::"] 0]
    # в зависимости от того что выделили будет запущена нужная процедура
    if {$key eq "" || $key eq "server"} {
        set host [ Add::server ]
        return
    }
    Add::$key .frm_tree.tree $host $values
}

Itt van a csiklandozás egyik előnye: egy változó értékét átadhatja eljárásnévként:

Add::$key .frm_tree.tree $host $values

Vagyis ha például a főszerverre mutatunk és megnyomjuk a „+” gombot, akkor elindul az Add::server eljárás, ha a fürtnél - Add::cluster és így tovább (írom, hogy hol a a szükséges „kulcsok” kicsit alulról származnak), a felsorolt ​​eljárások a kontextusnak megfelelő grafikai elemeket rajzolnak.

Amint azt már észrevette, az űrlapok stílusában hasonlóak - ez nem meglepő, mert egy eljárással jelennek meg, pontosabban az űrlap fő kerete (ablak, gombok, kép, címke), az eljárás 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
    # метка с иконкой
    ttk::label $win_name.lbl -image $img
    # фрейм с полями ввода
    set frm [ttk::labelframe $win_name.frm -text $lbl -labelanchor nw]
    
    grid columnconfigure $frm 0 -weight 1
    grid rowconfigure $frm 0 -weight 1
    # фрейм и кнопки
    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
}

Hívásparaméterek: cím, képnév az ikonhoz a könyvtárból (lib/images.tcl) és egy opcionális ablaknév-paraméter (alapértelmezett .add). Így, ha a fenti példákat vesszük a fő szerver és a fürt hozzáadására, a hívás ennek megfelelően lesz:

AddToplevel "Добавление основного сервера" server_grey_64

vagy

AddToplevel "Добавление кластера" cluster_grey_64

Nos, folytatva ezekkel a példákkal, bemutatom azokat az eljárásokat, amelyek egy kiszolgálóhoz vagy fürthöz hozzáadási párbeszédpaneleket jelenítenek meg.

Szerver hozzáadása

proc Add::server {} {
    global default
    # выводим основную форму
    set frm [AddToplevel "Добавление основного сервера" server_grey_64]
    # добавляем етки и поля ввода на эту форму
    label $frm.lbl_host -text "Адрес сервера"
    entry  $frm.ent_host
    label $frm.lbl_port -text "Порт"
    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]
    # переопределяем обработчик нажатия кнопки
    .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 ""
    }
    # устанавливаем глобальные переменные ()
    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 "Добавление кластера" cluster_grey_64]
    
    label $frm.lbl_host -text "Адрес основного сервера"
    entry  $frm.ent_host
    label $frm.lbl_port -text "Порт"
    entry $frm.ent_port 
    $frm.ent_port  insert end $default(port)
    label $frm.lbl_name -text "Название кластера"
    entry  $frm.ent_name
    label $frm.lbl_secure_connect -text "Защищённое соединение"
    ttk::combobox $frm.cb_security_level -textvariable security_level -values $default(security_level)
    label $frm.lbl_expiration_timeout -text "Останавливать выключенные процессы через:"
    entry  $frm.ent_expiration_timeout -textvariable expiration_timeout
    label $frm.lbl_session_fault_tolerance_level -text "Уровень отказоустойчивости"
    entry  $frm.ent_session_fault_tolerance_level -textvariable session_fault_tolerance_level
    label $frm.lbl_load_balancing_mode -text "Режим распределения нагрузки"
    ttk::combobox $frm.cb_load_balancing_mode -textvariable load_balancing_mode 
    -values $default(load_balancing_mode)
    label $frm.lbl_errors_count_threshold -text "Допустимое отклонение количества ошибок сервера, %"
    entry  $frm.ent_errors_count_threshold -textvariable errors_count_threshold
    label $frm.lbl_processes -text "Рабочие процессы:"
    label $frm.lbl_lifetime_limit -text "Период перезапуска, сек."
    entry  $frm.ent_lifetime_limit -textvariable lifetime_limit
    label $frm.lbl_max_memory_size -text "Допустимый объём памяти, КБ"
    entry  $frm.ent_max_memory_size -textvariable max_memory_size
    label $frm.lbl_max_memory_time_limit -text "Интервал превышения допустимого объёма памяти, сек."
    entry  $frm.ent_max_memory_time_limit -textvariable max_memory_time_limit
    label $frm.lbl_kill_problem_processes -justify left -anchor nw -text "Принудительно завершать проблемные процессы"
    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
    # переопределяем обработчик
    .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
}

Ezen eljárások kódjainak összehasonlításakor szabad szemmel is látható a különbség, az „Ok” gombkezelőre fogok koncentrálni. A Tk-ban a grafikus elemek tulajdonságai a program végrehajtása során felülírhatók az opció segítségével configure. Például a gomb megjelenítésének kezdeti parancsa:

ttk::button $frm_btn.btn_ok -image ok_grey_24 -command { }

De űrlapjainkban a parancs a szükséges funkcióktó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
    }

A fenti példában az „eltömődött” gomb elindítja a fürt hozzáadásának folyamatát.

Itt érdemes kitérni a Tk grafikus elemeivel való munkavégzés irányába - a különféle adatbeviteli elemekhez (bejegyzés, kombinált doboz, ellenőrző gomb stb.) szöveges változóként egy paraméter került be:

entry  $frm.ent_lifetime_limit -textvariable lifetime_limit

Ez a változó a globális névtérben van definiálva, és az aktuálisan beírt értéket tartalmazza. Azok. ahhoz, hogy a beírt szöveget a mezőből megkaphassuk, csak a változónak megfelelő értéket kell kiolvasni (persze, ha az elem létrehozásakor definiálva van).

A második módszer a beírt szöveg lekérésére (bejegyzés típusú elemek esetén) a get parancs használata:

.add.frm.ent_name get

Mindkét módszer látható a fenti kódban.

Ha erre a gombra kattint, ebben az esetben elindítja a RunCommand eljárást a generált parancssorral a rac szempontjából fürt hozzáadásához:

/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:1545

Most elérkeztünk a fő parancshoz, amely a rac indítását vezérli a szükséges paraméterekkel, a parancsok kimenetét is listákba elemezi, és szükség esetén visszaadja:

Futtassa a parancsot

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
    # открываем канал в неблокирующем режиме
    # $rac - команда с полным путём
    # $par - сформированные ключи запуска и опции    
    set pipe [open "|$rac_cmd $par" "r"]
    try {
        set lst ""
        set l ""
        # вывод команды добавляем в список списков
        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} {
        # Запуск обработчика ошибок
        ErrorParcing $result $options
        return ""
    }
}

A fő szerver adatok megadása után felkerül a fára, ehhez a fenti Add:server eljárásban a következő kód a felelős:

.frm_tree.tree insert {} end -id "server::$host" -text "$host" -values "$host"

Most a fában a kiszolgáló nevére kattintva egy listát kapunk az adott szerver által kezelt klaszterekről, egy fürtre kattintva pedig a fürtelemek listáját (szerverek, infobázisok stb.). Ez a TreePress eljárásban valósul meg (lib/function.tcl fájl):

proc TreePress {tree} {
   global host server active_cluster infobase
   # определяем выделенный элемент
    set id  [$tree selection]
   # устанавливаем нужные глобальные переменные
    SetGlobalVarFromTreeItems $tree $id
   # Определяем ключ и значение, т.е. именно тип выбранного элемента
    set values [$tree item $id -values]
    set key [lindex [split $id "::"] 0]
   # и в зависимости от того что выбрали будет запущена соответствующая процедура 
   # в пространстве имён Run
    Run::$key $tree $host $values
}

Ennek megfelelően a Run::server elindul a fő szerveren (fürtnél - Run::cluster, működő szervernél - Run::work_server stb.). Azok. a $key változó értéke része az opció által megadott faelem nevének id.

Figyeljünk az eljárásra

Futtassa::server

proc Run::server {tree host values} {
    # получаем список кластеров требуемого сервера
    set lst [RunCommand server::$host "cluster list $host"]
    if {$lst eq ""} {return}
    set l [lindex $lst 0]
    #puts $lst
    # удаляем лишнее из списка
    .frm_work.tree_work delete  [ .frm_work.tree_work children {}]
    # читаем список
    foreach cluster_list $lst {
        # Заполняем список полученными значениями
        InsertItemsWorkList $cluster_list
        # обрабатываем вывод (список) для добавления данных в дерево
        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]]
            }
        }
    }
    # добавляем кластеры в дерево
    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"
            # добавляем элементы в кластер
            InsertClusterItems $tree $id
        }
    }
    if { [$tree exists "agent_admins::$id"] == 0 } {
        $tree insert "server::$host" end -id "agent_admins::$id" -text "Администраторы" -values "$id"
        #InsertClusterItems $tree $id
    }
}

Ez az eljárás feldolgozza a kiszolgálótól a RunCommand paranccsal kapottakat, és mindenféle dolgot hozzáad a fához - fürtöket, különféle gyökérelemeket (bázisokat, működő szervereket, munkameneteket stb.). Ha alaposan megnézi, észreveszi az InsertItemsWorkList eljárás hívását. Elemek hozzáadására szolgál egy grafikus listához a rac konzol segédprogram kimenetének feldolgozásával, amely korábban listaként került vissza a $lst változóba. Ez egy olyan lista, amely kettősponttal elválasztott elempárokat tartalmaz.

Például a fürtkapcsolatok 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 valahogy így fog kinézni:

GUI-t írunk az 1C RAC-hoz, vagy ismét a Tcl / Tk-hoz

A fenti eljárás kiválasztja az elemek nevét a fejléchez és az adatokhoz a táblázat kitöltéséhez:

InsertItemsWorkList

proc InsertItemsWorkList {lst} {
    global work_list_row_count
    # установка чередования цвета для строки
    if [expr $work_list_row_count % 2] {
        set tag dark
    } else {
        set tag light
    }
    # разбор строк на пары ключ - значение
    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]
        }
    }
     # заполнение таблицы
    .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
    # установка заголовков
    foreach j $column_list {
        .frm_work.tree_work heading $j -text $j
    }
    incr work_list_row_count
}

Itt egy egyszerű [split $str ":"] parancs helyett, amely a karakterláncot ":"-vel elválasztott elemekre bontja, és egy listát ad vissza, reguláris kifejezést használunk, mivel egyes elemek kettőspontot is tartalmaznak.

Az InsertClusterItems eljárás (egy a több hasonló közül) egyszerűen hozzáadja a gyermekelemek listáját a megfelelő azonosítókkal a kívánt fürtelem fájához.
InsertClusterItems

proc InsertClusterItems {tree id} {
    set parent "cluster::$id"
    $tree insert $parent end -id "infobases::$id" -text "Информационные базы" -values "$id"
    $tree insert $parent end -id "servers::$id" -text "Рабочие серверы" -values "$id"
    $tree insert $parent end -id "admins::$id" -text "Администраторы" -values "$id"
    $tree insert $parent end -id "managers::$id" -text "Менеджеры кластера" -values $id
    $tree insert $parent end -id "processes::$id" -text "Рабочие процессы" -values "workprocess-all"
    $tree insert $parent end -id "sessions::$id" -text "Сеансы" -values "sessions-all"
    $tree insert $parent end -id "locks::$id" -text "Блокировки" -values "blocks-all"
    $tree insert $parent end -id "connections::$id" -text "Соединения" -values "connections-all"
    $tree insert $parent end -id "profiles::$id" -text "Профили безопасности" -values $id
}

Megfontolhat még két lehetőséget egy hasonló eljárás megvalósítására, ahol jól látható lesz, hogyan optimalizálhatja az ismétlődő parancsokat és megszabadulhat azoktól:

Ebben az eljárásban a hozzáadás és az ellenőrzés közvetlenül megoldódik:

InsertBaseItems

proc InsertBaseItems {tree id} {
    set parent "infobase::$id"
    if { [$tree exists "sessions::$id"] == 0 } {
        $tree insert $parent end -id "sessions::$id" -text "Сеансы" -values "$id"
    }
    if { [$tree exists "locks::$id"] == 0 } {
        $tree insert $parent end -id "locks::$id" -text "Блокировки" -values "$id"
    }
    if { [$tree exists "connections::$id"] == 0 } {
        $tree insert $parent end -id "connections::$id" -text "Соединения" -values "$id"
    }
}

Íme egy korrektebb megközelítés:

InsertProfileItems

proc InsertProfileItems {tree id} {
    set parent "profile::$id"
    set lst {
        {dir "Виртуальные каталоги"}
        {com "Разрешённые COM-классы"}
        {addin "Внешние компоненты"}
        {module "Внешние отчёты и обработки"}
        {app "Разрешённые приложения"}
        {inet "Ресурсы интернет"}
    }
    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 különbség köztük egy ciklus használata, amelyben az ismétlődő parancs(ok) végrehajtásra kerülnek. A fejlesztő döntésétől függ, hogy melyik megközelítést alkalmazzuk.

Az elemek hozzáadásával és az adatok lekérésével foglalkoztunk, most itt az ideje, hogy a szerkesztésre összpontosítson. Mivel alapvetően ugyanazokat a paramétereket használjuk a szerkesztéshez és a hozzáadáshoz (kivéve az információs bázist), ezért ugyanazok a párbeszédablakok. A hozzáadási eljárások meghívásának algoritmusa így néz ki:

Add::$key->AddToplevel

És az ilyen szerkesztéshez:

Edit::$key->Add::$key->AddTopLevel

Vegyük például egy fürt szerkesztését, pl. Miután a fában a fürt nevére kattintott, nyomja meg a szerkesztés gombot az eszköztáron (ceruza), és a megfelelő űrlap jelenik meg a képernyőn:

GUI-t írunk az 1C RAC-hoz, vagy ismét a Tcl / Tk-hoz
Szerkesztés::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 ""
    }
    # рисуем форму для кластера
    set frm [Add::cluster $tree $host $values]
    # меняем текст на метке
    $frm configure -text "Редактирование кластера"
    
    set active_cluster $values
    # получаем данные по выделенному кластеру
    set lst [RunCommand cluster::$values "cluster info --cluster=$active_cluster $host"]
    # заполняем поля
    FormFieldsDataInsert $frm $lst
    # выключаем поля, редактирование которых запрещено
    $frm.ent_host configure -state disable
    $frm.ent_port configure -state disable
    # переназначаем обработчик
    .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 elvileg minden világos, kivéve, hogy a gombkezelő kód felül van írva, és van egy FormFieldsDataInsert eljárás, ami kitölti a mezőket és inicializálja a változókat:

FormFieldsDataInsert

proc FormFieldsDataInsert {frm lst} {
    foreach i [lindex $lst 0] {
        # получаем список параметров и значений
        if [regexp -nocase -all -- {(D+)(s*?|)(:)(s*?|)(.*)} $i match param v2 v3 v4 value] {
            # меняем символы
            regsub -all -- "-" [string trim $param] "_" entry_name
            # заполняем данными
            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 """]
            }
            # для чекбоксов меняем значения
            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
                }
            }
        }
    }
}

Ebben az eljárásban a tcl másik előnye is felszínre került - más változók értékeit változónévként helyettesítik. Azok. Az űrlapok kitöltésének és a változók inicializálásának automatizálása érdekében a mezők és a változók nevei megfelelnek a rac segédprogram parancssori kapcsolóinak és a parancskimeneti paraméterek nevének néhány kivétellel - a kötőjelet aláhúzás helyettesíti. Például ütemezett-munka-megtagadás megfelel a mezőnynek ent_scheduled_jobs_deny és változó ütemezett_jobs_deny.

A hozzáadáshoz és szerkesztéshez szükséges űrlapok eltérhetnek a mezők összetételétől, például egy információs bázissal való munka során:

Információbiztonság hozzáadása

GUI-t írunk az 1C RAC-hoz, vagy ismét a Tcl / Tk-hoz

Az információbiztonság szerkesztése

GUI-t írunk az 1C RAC-hoz, vagy ismét a Tcl / Tk-hoz

Az Edit::infobase szerkesztési eljárásban a kötelező mezők bekerülnek az űrlapba, a kód terjedelmes, ezért itt nem mutatom be.

Hasonló módon a hozzáadási, szerkesztési és törlési eljárásokat más elemekre is végrehajtják.

Mivel a segédprogram működése korlátlan számú szervert, klasztert, információs bázist stb. foglal magában, így annak meghatározásához, hogy melyik fürt melyik szerverhez vagy információbiztonsági rendszerhez tartozik, több globális változót vezettek be, amelyek értékeit minden esetben beállítják. amikor rákattint a fa elemeire. Azok. az eljárás rekurzívan végigfut az összes szülőelemen, é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
    }
}

Az 1C fürt lehetővé teszi, hogy jogosultsággal vagy anélkül dolgozzon. Kétféle adminisztrátor létezik – fürtügynök-adminisztrátor és fürt-adminisztrátor. Ennek megfelelően a helyes működés érdekében további 4 globális változót vezettünk be, amelyek az adminisztrátori bejelentkezési nevet és jelszót tartalmazzák. Azok. Ha a fürtben van rendszergazdai fiók, megjelenik egy párbeszédpanel a bejelentkezési nevének és jelszavának megadásához, az adatok a memóriába kerülnek, és beilleszthetők a megfelelő fürt egyes parancsaiba.

Ez a hibakezelési eljárás felelőssége.

ErrorParcing

proc ErrorParcing {err opt} {
    global cluster_user cluster_pwd agent_user agent_pwd
        switch -regexp -- $err {
        "Cluster administrator is not authenticated" {
            AuthorisationDialog "Администратор кластера"
            .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
        }
        "Central server administrator is not authenticated" {
            AuthorisationDialog "Администратор агента кластера"
            .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
            }
        }
        "Администратор кластера не аутентифицирован" {
            AuthorisationDialog "Администратор кластера"
            .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
        }
        "Администратор центрального сервера не аутентифицирован" {
            AuthorisationDialog "Администратор агента кластера"
            .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"
        }
    }
}

Azok. attól függően, hogy mit ad vissza a parancs, a reakció ennek megfelelően történik.

Jelenleg a funkcionalitás mintegy 95 százaléka megvalósult, már csak a biztonsági profilokkal való munka megvalósítása és tesztelése van hátra =). Ez minden. Elnézést kérek a gyűrött történetért.

A kód hagyományosan elérhető itt.

Frissítés: Befejeztem a biztonsági profilokkal való munkát. Most a funkcionalitás 100%-ban megvalósult.

2. frissítés: angol és orosz honosítás került hozzáadásra, a win7-es működés tesztelve
GUI-t írunk az 1C RAC-hoz, vagy ismét a Tcl / Tk-hoz

Forrás: will.com

Hozzászólás