分布式 Kerio Connect 服务器之间共享文件夹、联系人、日历的完全同步

下午好,哈布尔!

任务

我的组织使用 Kerio Connect 平台上的邮件服务器;邮件服务器安装在不同的城市来为其用户提供服务。 最初没有分布式结构,因为域在第三级不同,表示站点的城市。 一切顺利,每个人都很高兴。 一个晴朗的日子,管理层设定了一项任务,即所有站点之间的活动共同日历!

史前

最初的想法是建立 Kerio 分布式邮件域,它会自己完成所有事情。 说完就创建了一个分布式域,但事实并非如此,服务器已准备好在位于同一服务器上的域之间同步日历、文件夹、联系人,但根本不会在多个域之间同步数据服务器。

当然,我没想到会出现这样的情况,并且很长一段时间都无法相信我需要的功能缺失了。 后来我找到了这一事实的文献证据。 我对此感到非常困惑和失望。

任务顺利地变成了问题。

有哪些选择?

  • 在不同的服务器上创建两个客户端,与某些第三方软件交换必要的数据。 有必要找到可以实现此功能的第三方软件 - 我不喜欢这样的耙子,但似乎这是唯一的快速解决方案。
  • 编写自己的脚本用于服务器之间的数据同步。 事实上,Kerio 将每个对象存储为单独的文件,因此有必要开发一个用于处理文件的脚本,但鉴于源数量足够,该任务似乎有些复杂,特别是因为需要执行多个检查数据的正确性,以防有人在同一时间段创建任务等。

展望未来,我会说,虽然 Kerio 将对象存储为单独的文件,但它并没有愚蠢到每次访问该对象时都询问文件系统是如何做的。

经过深思熟虑,起草了一堆“夺取敌地”计划的纸片后,我在六点钟做出了两个正确的决定:

  • 第一个决定就是做好自己的事情,不要向外界寻求任何东西。
  • 第二个解决办法是去睡觉。

早上我醒来时就带着一个单一而真实的想法,它被简化为几个字母 - DFS

解决方案本身看起来像这样

  • 将所有参与同步的服务器带到 Windows 操作系统。 (部分是在Linux上。需要将邮件数据迁移到另一个操作系统)
  • 确定将参与同步的目录的结构 - 它们必须相同。
  • 使用单个 DFS 空间定义一个域下的所有邮件服务器。
  • 创建上述分布式 Kerio 域,因为在我的例子中需要数据同步,不仅在服务器之间而且在域之间;第二个可以由 Kerio 服务器独立处理。 (与第一个不同)
  • 将同步目录设置为DFS空间。
  • 拿出某种拐杖(毕竟,没有拐杖你就活不下去)

履行

两个邮件服务器上的示例(也许更多)

1.Kerio分布式域

分布式 Kerio Connect 服务器之间共享文件夹、联系人、日历的完全同步

Master不参与同步,但这不是前提条件。

如何筹集Kerio分布式域名我就不介绍了,没什么复杂的,可以研究一下官方 手动

最终,您应该在管理控制台中看到以下图像:

分布式 Kerio Connect 服务器之间共享文件夹、联系人、日历的完全同步

分布式 Kerio Connect 服务器之间共享文件夹、联系人、日历的完全同步

接下来我对共享文件夹感兴趣;在主服务器上,您可以指定以下选项:

分布式 Kerio Connect 服务器之间共享文件夹、联系人、日历的完全同步

分布式 Kerio Connect 服务器之间共享文件夹、联系人、日历的完全同步

具体针对每个域 - 服务器不会同步域之间的公共文件夹

所有域通用 - 所有服务器将放弃每个域中的现有公用文件夹,并为每个邮件服务器上的所有域创建新的单个文件夹。

警告! 尽管此选项更改了所有服务器上的配置策略,但它与每个服务器分开同步(即没有单个公共空间)

管理员仍然能够在用户之间分配访问权限。
就我而言,它们都是我的,我需要在每台服务器上完全同步(在您的情况下,解决方案可能不同),您需要创建需要同步的相同域集。

2.Kerio数据目录

现在您需要创建需要在每台服务器上同步的相同共享目录。 文件夹、日历、联系人。

建议-用英语创建目录,如果用拉丁语创建目录,该目录的名称将采用一些难以理解的编码,这至少不方便。

现在您需要找到每台服务器上邮件文件夹的物理路径。

所有域通用 ~DataMailmail#publicСинхронизируемый каталог#msgs
具体针对每个域 ~DataMailmail**Domain**#publicСинхронизируемый каталог#msgs

请注意,我们不会同步整个目录,而只会同步包含数据的容器 #消息 — 对象本身存储在这里,每个服务器的所有其他数据必须是单独的。

3.DFS

我也不会详细描述如何配置DFS,关于这个问题已经有足够的信息了。

DFS 是 Windows Server 中的一项角色服务,提供组合位于不同服务器上的共享文件夹的功能
MS DFS 文档链接

在设置DFS之前,您必须停止所有将参与数据同步的邮件服务器。

设置完成后,您应该会收到每个同步文件夹的下图

分布式 Kerio Connect 服务器之间共享文件夹、联系人、日历的完全同步

当然,我们不需要发布复制的文件夹。

分布式 Kerio Connect 服务器之间共享文件夹、联系人、日历的完全同步

复制发生后(并且没有什么特别要复制的地方 - 文件夹是空的),可以启动邮件服务器。

接下来,您可以向其中一台邮件服务器填充数据,并检查数据是否正确复制。

4.拐杖

反射描述

正如您在数据开始同步 (DFS) 后所看到的,如果您在第一台服务器上创建了某些内容,不知何故,第二台服务器上什么也没有出现,或者它出现了,但不知何故并不总是如此。

不要绝望,当然,它迟早会出现,但早点总比晚点好。 因为再过6-12个小时就太晚了。

问题是,一旦您在第一台服务器上创建了某些内容,由于 DFS 系统,在第二台及后续服务器上该文件当然会立即出现,但如果该邮件目录之前已被某人读取过再次请求时,服务器不会重新读取#msgs文件夹,而是从自己的索引中吐出数据,这可能不再符合我们的现实。

Kerio 有一个重新读取索引的机制,但它可以在大约 6 小时内工作,并且在这 XNUMX 小时内,日历中任务的相关性可能会有所丢失。
为了立即测试同步,可以删除对应同步目录下的index.fld文件,重新访问邮件服务器上的文件夹后,如果该文件丢失,Kerio会重新读取该目录和数据会出现。 看起来这就是解决方案,当数据发生变化时删除文件,但这并不是每次都有效,而只是第一次,然后Kerio出于某种原因失去了对index.fld的所有兴趣
它还开始吐出用户无法理解的消息 - 关于某种索引并且它已经在那里做了一些事情。

还有另一种选择,创建一些东西 - 在创建新对象的那一刻,服务器突然意识到它想要分配的文件名已经被占用,但它就像滚雪球一样,这是一个死胡同的选择。

怎么会是这样?

如果我们再次关注我们已经熟悉的画面。

分布式 Kerio Connect 服务器之间共享文件夹、联系人、日历的完全同步

但在另一架飞机上,你可以看到我们现在需要的一个非常有趣的按钮—— 重新索引文件夹

确实如此。 如果我们在不知道同步#msgs 中已发生更改的邮件服务器上单击此按钮,我们将获得稳定、快速的结果。 一切隐藏的事情都会变得清晰。

在日志中,您可以看到此过程需要多长时间;在我的情况下,有数千(15)条记录,大约需要 3-4 分钟。

我们所要做的就是弄清楚如何在需要时实际按下这个按钮。

事实证明 凯里奥 有自己的 API

使用说明
Документация

执行我们任务的函数如下所示:
session = callMethod("Domains.checkPublicFoldersIntegrity",{}, token)

综上所述,我们需要编写一个脚本来监视感兴趣的文件夹的状态,如果发生变化,则执行我们需要的功能。

我想说的是,我编写了几个不同版本的脚本来执行不同的检查,并选择了根据文件数量得出所有结论的版本。

脚本执行

CMD脚本示例及说明

重新索引.bat

@echo off
set dir=%~dp0
%dir:~0,2%
CD "%~dp0"
md "%CD%LOG"
md "%CD%Setup"

ECHO -Start- >> "%CD%LOG%Computername%.log"
ECHO Start -> %Computername% %Date% %Time% >> "%CD%LOG%Computername%.log"

SetLocal EnableDelayedExpansion
for /f "UseBackQ Delims=" %%A IN ("%CD%Setup%Computername%.List") do (
  set /a c+=1
  set "m!c!=%%A"
)

set d=%c%
Echo Folder = %c%
ECHO Folder = %c% >> "%CD%LOG%Computername%.log"
ECHO.
ECHO. >> "%CD%LOG%Computername%.log"

:start
cls
if %c% LSS 1 exit
set /a id=1
set R=0

:Find
REM PF-Start
if "%id%" gtr "%c%" if %R% == 1 Goto Reindex 
if "%id%" gtr "%c%" timeout 60 && Goto start

For /F "tokens=1-3" %%a IN ('Dir "!m%id%!#msgs" /-C/S/A:-D') Do Set 2DirSize!id!=!DS!& Set DS=%%c
if "2DirSize!id!" == "" set 1DirSize!id!=!2DirSize%id%!

echo %id%
ECHO !m%id%!
echo Count        [ !1DirSize%id%! -- !2DirSize%id%! ]

if "!1DirSize%id%!" == "!2DirSize%id%!" ECHO Synk

REM DEL index.fld
if "!1DirSize%id%!" NEQ "!2DirSize%id%!" del /f /q !m%id%!index.fld && del /f /q !m%id%!indexlog.fld && del /f /q !m%id%!search.fld && set R=1 && ECHO RE-index Count && ECHO RE-index Count %Date% %Time% - Delete !m%id%! >> "%CD%LOG%Computername%.log"

set 1DirSize!id!=!2DirSize%id%!

ECHO.
ECHO.

set /a id+=1
goto Find

:Reindex
ECHO. >> "%CD%LOG%Computername%.log"
ECHO --- RE-INDEX - Start - %Date% %Time% --- >> "%CD%LOG%Computername%.log"
ECHO. >> ----------------------------------- >> "%CD%LOG%Computername%.log"
call PublicFolders.py
timeout 60
goto start

exit

该脚本的副本在每个邮件服务器上运行(可以用​​作服务,不需要 Adm 权限)

脚本读取文件 Setup%Computername%.List

其中 %Computername% 是当前服务器的名称(该目录可以同时包含所有服务器的列表。)

文件%Computername%.List – 包含同步目录的完整路径,每个路径都写在新行上,并且不应包含空行。

首次启动后,无论是否有必要,脚本都会执行索引过程,并且脚本还会创建每个同步目录中文件数量的索引。

该脚本的目的是统计指定目录中的所有文件。

在对每个目录进行计数结束时,如果至少有一个目录中的文件当前值与前一个目录不匹配,则脚本会从同步邮件目录的根目录中删除文件: index.fld, indexlog.fld, search.fld 并启动共享邮件文件夹的索引过程。

有关任务执行的信息转储到 LOG 目录中。

索引过程
索引过程归结为执行 Kerio API 函数
Session = callMethod("Domains.checkPublicFoldersIntegrity",{}, 令牌)

python 中给出了一个示例实现
公共文件夹.py

import json
import urllib.request
import http.cookiejar
""" Cookie storage is necessary for session handling """
jar = http.cookiejar.CookieJar()
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar))
urllib.request.install_opener(opener)
""" Hostname or ip address of your Kerio Control instance with protocol, port and credentials """

server = "http://127.0.0.1:4040"
username = "user"
password = "password"

def callMethod(method, params, token = None):
    """
    Remotely calls given method with given params.
    :param: method string with fully qualified method name
    :param: params dict with parameters of remotely called method
    :param: token CSRF token is always required except login method. Use method "Session.login" to obtain this token.
    """
    data =  {"method": method ,"id":1, "jsonrpc":"2.0", "params": params}

    req = urllib.request.Request(url = server + '/admin/api/jsonrpc/')
    req.add_header('Content-Type', 'application/json')
    if (token is not None):
        req.add_header('X-Token', token)    

    httpResponse = urllib.request.urlopen(req, json.dumps(data).encode())

    if (httpResponse.status == 200):
        body = httpResponse.read().decode()
        return json.loads(body)

session = callMethod("Session.login", {"userName":username, "password":password, "application":{"vendor":"Kerio", "name":"Control Api-Local", "version":"Python"}})
token = session["result"]["token"]
print (session)

session = callMethod("Domains.checkPublicFoldersIntegrity",{"domainId": "test2.local"}, token)
print (session)

callMethod("Session.logout",{}, token)

http://127.0.0.1:4040 您可以保持原样,但如果您需要 HTTPS,python 必须信任 Kerio 证书。

另外,在该文件中,您必须指定一个有权执行邮件服务器此功能(Adm - 公共邮件文件夹)的帐户。

我希望我的文章对 Kerio Connect 管理员有用。

来源: habr.com

添加评论