Синхронизация локального репозитория с серверным

Дата публикации: 08.03.2011
Дата изменения: не изменялась
Состояние: завершена

На сервере находится репозиторий, структура и состав данных которого такова, чтобы можно было в автоматическом режиме выполнять синхронизацию с его локальной версией (структура которой значительно отличается от серверной). 

Серверный репозиторий разделяет информацию на ту, что зависит от версии AutoCAD и на ту, что является общей для всех версий AutoCAD. Помимо этого, программное обеспечение разделяется по версии целевой платформы (x86 или x64). В серверном репозитории так же хранятся исходные коды программ, обучающие видеофильмы и т.п.

Поскольку локальный репозиторий размещается на машине, имеющей чётко обозначенную версию платформы, а так же определённый набор установленных AutoCAD, то структура этого репозитория не разделяет версии программного обеспечения по целевым платформам, а автоматически берёт при синхронизации только те, которые подходят под платформу текущего компьютера. Т.е. на компьютер с архитектурой x64 в локальный репозиторий не будут копироваться те программы и библиотеки, которые на сервере лежат в каталоге, ориентированном на x86. Кроме того - в локальный репозиторий будет поступать только та информация, которая относится к тем версиям AutoCAD, которые установлены на локальной машине и разрешены к использованию в составе AdminCAD (это разрешение указывается в общем конфигурационном файле уровня домена в блоке AllowedAutoCADs). Если пользователь установит новую версию AutoCAD или удалит существующую - состав локального репозитория будет изменён при следующей синхронизации с серверным репозиторием.

Одной из основных задач  AdminCAD является снятие с администратора CAD заботы о том, что нужно как-то запустить процесс синхронизации на локальных компьютерах, после того, как в серверном репозитории будут произведены изменения (что-то добавлено/удалено/обновлено). Администратор CAD изменяет данные в серверном репозитории, а AdminCAD автоматически выполняет все нужные действия для того, чтобы можно было выполнить синхронизацию локальных машин, сообщая пользователям о том, что на сервере имеются доступные обновления и предоставляя выбор - выполнить синхронизацию сейчас или позже. В процессе синхронизации, AdminCAD исключает из процесса те файлы и подкаталоги, которые не следует копировать на локальную машину (этими правилами управляет администратор CAD).

Далее привожу пример кода, в котором продемонстрировано то, как используя AdminCAD API можно выполнить синхронизацию локального репозитория с серверным сразу для всех разрешённых к использованию версий AutoCAD (из тех, что установлены на локальной машине).

Код C# 3.5:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using Microsoft.Win32;
   6:  using System.Security.Cryptography;
   7:  using System.Xml.Linq;
   8:  using System.Text.RegularExpressions;
   9:  using System.IO;
  10:  using AdminCAD.Configuration;
  11:  using AdminCAD.Net.Mail;
  12:  using AdminCAD.DirectoryServices;
  13:  using AdminCAD.Autodesk.AutoCAD;
  14:  using AdminCAD.Security.Cryptography;
  15:  using AdminCAD.Configuration.Journaling;
  16:   
  17:  namespace AdminCAD.AcadLibsTest
  18:  {
  19:      class Program
  20:      {
  21:          static void Main(string[] args)
  22:          {
  23:              //Вектор инициализации
  24:              byte[] iv = new byte[] { 100, 149, 158, 255, 68, 25, 176, 203, 162, 203, 171, 160, 19, 74, 119, 34 };
  25:              //Ключ шифрования/дешифровки
  26:              byte[] key = XSecurity.GetBytesFromFile(@"D:\SERVER\AdminCAD\Settings\Key.key");
  27:              //Кодировка символов
  28:              Encoding encoding = Encoding.UTF8;
  29:              //Имя файла общих конфигурационных настроек уровня домена
  30:              string comSetFileName = @"D:\SERVER\AdminCAD\Settings\CommonSettings.set";
  31:              //Получаем объект общих настроек уровня домена
  32:              CommonSettings comSet = CommonSettings.Load(comSetFileName, key, iv, encoding);
  33:              //Версия AutoCAD, для которой хотим получить настройки (в дальнейшем мы можем указывать другую версию через свойство AcadVersion)
  34:              AcadVersion av = new AcadVersion("18.1", Platform.x86);
  35:              //Создаём объект, который будет предоставлять нам информацию для той версии AutoCAD, которую мы ему указываем
  36:              CommonSettingsWrapper csw = new CommonSettingsWrapper(av, comSet, key, iv, encoding);
  37:              //Если в процессе работы программы возникнет ошибка - информируем об этом администратора CAD
  38:              try
  39:              {
  40:                  //Сгенерируем полный набор серверных журналов, а так же набор журналов и сценариев обновления для всех тех установленных на локальной машине версий AutoCAD, с которыми разрешено работать AdminCAD:
  41:   
  42:                  //Можно получить общий массив задач синхронизации, которые должны быть выполнены для указанной нами версии AutoCAD:
  43:                  //Task[] allTasks = csw.GetAllTasks();
  44:                  //Этод метод реализован, однако, лучше получать два массива задач: зависящих от версии AutoCAD и не зависящих он неё. Такое разделение полезно тогда, когда на компьютере установлено несколько версий AutoCAD. Если бы мы выбирали все задачи, и запускали бы их ДЛЯ ВСЕХ УСТАНОВЛЕННЫХ НА ЛОКАЛЬНОЙ МАШИНЕ версий AutoCAD, то те задачи, которые не зависят от версии AutoCAD у нас генерировались бы столько раз, сколько версий AutoCAD у нас установлено, а это нецелесообразно. Поэтому лучше сначала для всех установленных версий AutoCAD выполнять зависимые от них задачи, а затем, один раз выполнить те, что не зависят от версии AutoCAD - это снижает время, затрачиваемое на работу программы. Ниже показано такое разделение.
  45:   
  46:                  //Получаем словарь переменных
  47:                  Dictionary<string, string> dict = csw.GetVariables();
  48:                  //Удаляем все старые СЕРВЕРНЫЕ журналы прежде чем сгенерируем новые
  49:                  DirectoryInfo dir = new DirectoryInfo(dict["XmlPath.ServerDirectoryContents"]);
  50:                  dir.GetFiles(csw.CommonSettings.GetJournalFilesFilter(), SearchOption.TopDirectoryOnly).All(n => { n.Delete(); return true; });
  51:                  //Удаляем все старые ЛОКАЛЬНЫЕ журналы прежде чем сгенерируем новые
  52:                  dir = new DirectoryInfo(dict["XmlPath.LocalDirectoryContents"]);
  53:                  dir.GetFiles(csw.CommonSettings.GetJournalFilesFilter(), SearchOption.TopDirectoryOnly).All(n => { n.Delete(); return true; });
  54:                  //Удаляем все старые СЦЕНАРИИ ОБНОВЛЕНИЙ прежде чем сгенерируем новые
  55:                  dir = new DirectoryInfo(dict["XmlPath.LocalUpdateTasks"]);
  56:                  dir.GetFiles(csw.CommonSettings.GetUpdatingScriptFilesFilter(), SearchOption.TopDirectoryOnly).All(n => { n.Delete(); return true; });
  57:   
  58:                  //Обновляем ВСЕ СЕРВЕРНЫЕ ЖУРНАЛЫ (операция нарочно вынесена в отдельное действие, чтобы наглядно показать, как можно отделить генерацию серверных журналов от локальных). Это не зависит от того, какие версии AutoCAD установлены на локальной машине
  59:                  foreach (AcadVersion item in csw.CommonSettings.AllowedAutoCADs)
  60:                  {
  61:                      csw.AcadVersion = item;
  62:                      //Получаем перечень задач синхронизации каталогов, содержимое которых зависит от версии AutoCAD
  63:                      Task[] depTasks = csw.GetDependentTasks();
  64:                      foreach (Task item2 in depTasks)
  65:                      {
  66:                          //Если на сервере отсутствует каталог, указанный в задаче - сообщаем об этом администратору CAD, чтобы он мог внести правку в задачу, или добавил этот каталог
  67:                          if (!Directory.Exists(item2.SourceDirectory))
  68:                          {
  69:                              MailManager mng = new MailManager(csw);
  70:                              mng.SendMail(EmailTarget.ErrorReportAddress, "На сервере отсутствует указанный в задаче каталог", string.Format("Имя файла, который должен был быть сгенерирован задачей (атрибут FileName): '{0}'. Отсутствующий каталог: '{1}'", item2.FileName, item2.SourceDirectory), true);
  71:                              //Пропускаем эту задачу и переходим к следующей
  72:                              continue;
  73:                          }
  74:                          //Создаём журналы и сценарии с последующим их сохранением в файлы
  75:                          item2.WriteFiles(csw, true, false, false);//Второй, третий и четвертый параметры указывают, следует ли создавать файлы серверных журналов, локальных журналов и сценариев обновлений
  76:                      }
  77:                      Task[] undepTasks = csw.GetUndependentTasks();
  78:                      foreach (Task item2 in undepTasks)
  79:                      {
  80:                          //Если на сервере отсутствует каталог, указанный в задаче - сообщаем об этом администратору CAD, чтобы он мог внести правку в задачу, или добавил этот каталог
  81:                          if (!Directory.Exists(item2.SourceDirectory))
  82:                          {
  83:                              MailManager mng = new MailManager(csw);
  84:                              mng.SendMail(EmailTarget.ErrorReportAddress, "На сервере отсутствует указанный в задаче каталог", string.Format("Имя файла, который должен был быть сгенерирован задачей (атрибут FileName): '{0}'. Отсутствующий каталог: '{1}'", item2.FileName, item2.SourceDirectory), true);
  85:                              //Пропускаем эту задачу и переходим к следующей
  86:                              continue;
  87:                          }
  88:                          //Создаём журналы и сценарии с последующим их сохранением в файлы
  89:                          item2.WriteFiles(csw, true, false, false);//Второй, третий и четвертый параметры указывают, следует ли создавать файлы серверных журналов, локальных журналов и сценариев обновлений
  90:                      }
  91:                  }
  92:   
  93:                  //В цикле генерируем ЛОКАЛЬНЫЕ журналы и сценарии обновлений для всех установленных на локальной машине версий AutoCAD
  94:                  foreach (AcadVersion item in AutoCAD.GetCurrentMachineAllowedCadVersions(csw.CommonSettings))
  95:                  {
  96:                      csw.AcadVersion = item;
  97:   
  98:                      //Получаем перечень задач синхронизации каталогов, содержимое которых зависит от версии AutoCAD
  99:                      Task[] depTasks = csw.GetDependentTasks();
 100:                      //Генерируем новые файлы
 101:                      foreach (Task item3 in depTasks)
 102:                      {
 103:                          //Если на сервере отсутствует каталог, указанный в задаче - сообщаем об этом администратору CAD, чтобы он мог внести правку в задачу, или добавил этот каталог
 104:                          if (!Directory.Exists(item3.SourceDirectory))
 105:                          {
 106:                              MailManager mng = new MailManager(csw);
 107:                              mng.SendMail(EmailTarget.ErrorReportAddress, "На сервере отсутствует указанный в задаче каталог", string.Format("Имя файла, который должен был быть сгенерирован задачей (атрибут FileName): '{0}'. Отсутствующий каталог: '{1}'", item3.FileName, item3.SourceDirectory), true);
 108:                              //Пропускаем эту задачу и переходим к следующей
 109:                              continue;
 110:                          }
 111:                          //Если на локальной машине отсутствует каталог, соответствующий серверному, указанному в задаче - создаём этот каталог
 112:                          if (!Directory.Exists(item3.TargetDirectory))
 113:                              Directory.CreateDirectory(item3.TargetDirectory);
 114:                          //Создаём журналы и сценарии с последующим их сохранением в файлы
 115:                          item3.WriteFiles(csw, false, true, true);//Второй, третий и четвертый параметры указывают, следует ли создавать файлы серверных журналов, локальных журналов и сценариев обновлений
 116:                      }
 117:                  }
 118:                  //Получаем перечень задач синхронизации каталогов, содержимое которых не зависит от версии AutoCAD
 119:                  Task[] _undepTasks = csw.GetUndependentTasks();
 120:                  //Генерируем новые файлы
 121:                  foreach (Task item4 in _undepTasks)
 122:                  {
 123:                      //Если на сервере отсутствует каталог, указанный в задаче - сообщаем об этом администратору CAD, чтобы он мог внести правку в задачу, или добавил этот каталог
 124:                      if (!Directory.Exists(item4.SourceDirectory))
 125:                      {
 126:                          MailManager mng = new MailManager(csw);
 127:                          mng.SendMail(EmailTarget.ErrorReportAddress, "На сервере отсутствует указанный в задаче каталог", string.Format("Имя файла, который должен был быть сгенерирован задачей (атрибут FileName): '{0}'. Отсутствующий каталог: '{1}'", item4.FileName, item4.SourceDirectory), true);
 128:                          //Пропускаем эту задачу и переходим к следующей
 129:                          continue;
 130:                      }
 131:                      //Если на локальной машине отсутствует каталог, соответствующий серверному, указанному в задаче - создаём этот каталог
 132:                      if (!Directory.Exists(item4.TargetDirectory))
 133:                          Directory.CreateDirectory(item4.TargetDirectory);
 134:   
 135:                      //Создаём журналы и сценарии с последующим их сохранением в файлы
 136:                      item4.WriteFiles(csw, false, true, true);//Второй, третий и четвертый параметры указывают, следует ли создавать файлы серверных журналов, локальных журналов и сценариев обновлений
 137:                  }
 138:                  //Теперь можно запустить сценарии обновлений на исполнение, произведя тем самым полную синхронизацию локального репозитория с серверным для всех версий AutoCAD, установленных на локальной машине:
 139:   
 140:                  //1.Получаем все имеющиеся на локальной машине файлы сценарииев
 141:                  FileInfo[] files = new DirectoryInfo(dict["XmlPath.LocalUpdateTasks"]).GetFiles(csw.CommonSettings.GetUpdatingScriptFilesFilter(), SearchOption.TopDirectoryOnly);
 142:                  //2. Запускаем на выполнение каждый сценарий
 143:                  foreach (FileInfo item in files)
 144:                  {
 145:                      UpdatingScript us = UpdatingScript.Load(item.FullName, csw.Key, csw.IV, csw.Encoding);
 146:                      us.Run();
 147:                  }
 148:              }
 149:              catch (Exception ex)
 150:              {
 151:                  //Отправляем администратору CAD информацию о произошедшей ошибке, дополнительно собрав информацию о всех установленных на локальной машине версиях AutoCAD, их обновлениях, а так же информация об операционной системе, о пользователе и т.п.
 152:                  MailManager mng = new MailManager(csw);
 153:                  mng.SendMail(EmailTarget.ErrorReportAddress, "Ошибка синхронизациии", ex.Message, true);
 154:                  //Генерируем исключение
 155:                  throw ex;
 156:              }            
 157:          }
 158:      }
 159:  }

Код подробно снабжён комментариями, так что в процессе его чтения особых вопросов возникнуть не должно. Выше продемонстрированный код выполнял следующий набор задач по синхронизации, прописанных в общем конфигурационном файле уровня домена:

   1:   <!--Задачи по синхронизации каталогов-->
   2:    <Tasks>
   3:      <!--Каталог, в котором находятся плагины (кроме тех, что написаны на AutoLisp/VisualLisp), специфичные для конкретной версии AutoCAD (с учётом разрядности операционной системы)-->
   4:      <Task FileName="Plugins_Arx_Net  %Xml.AcadVersion%%Xml.Platform%" SourceDirectory="%XmlPath.ServerDir%\Dependent\%Xml.AcadVersion%\bin\%Xml.Platform%" TargetDirectory="%XmlPath.LocalCommonDir%\Dependent\%Xml.AcadVersion%\bin" DirectoryExcludeRule="excludeFolders-1" FileExcludeRule="excludeFiles-1" />
   5:      <!--Каталог, в котором находятся различного рода данные, специфичные для конкретной версии AutoCAD-->
   6:      <Task FileName="DependentData %Xml.AcadVersion%%Xml.Platform%" SourceDirectory="%XmlPath.ServerDir%\Dependent\%Xml.AcadVersion%\etc" TargetDirectory="%XmlPath.LocalCommonDir%\Dependent\%Xml.AcadVersion%\etc" DirectoryExcludeRule="empty" FileExcludeRule="empty" />
   7:      <!--Каталог, в котором находятся плагины, написанные на AutoLisp/VisualLisp и VBA-->
   8:      <Task FileName="Plugins_Lisp_Vba" SourceDirectory="%XmlPath.ServerDir%\Undependent" TargetDirectory="%XmlPath.LocalCommonDir%\Undependent" DirectoryExcludeRule="excludeFolders-2" FileExcludeRule="empty" />
   9:      <!--Каталог, в котором содержатся данные, используемые в любой версии AutoCAD-->
  10:      <Task FileName="UndependentData" SourceDirectory="%XmlPath.ServerDir%\Common" TargetDirectory="%XmlPath.LocalCommonDir%\Common" DirectoryExcludeRule="empty" FileExcludeRule="empty" />
  11:      <!--Каталог, в котором содержатся настройки уровня доменных групп-->
  12:      <Task FileName="GroupSettings" SourceDirectory="%XmlPath.ServerDir%\Settings\GroupSettings" TargetDirectory="%XmlPath.LocalCommonDir%\Settings\GroupSettings" DirectoryExcludeRule="empty" FileExcludeRule="empty" />
  13:      <!--Каталог, в котором содержатся общие приложения Системы-->
  14:      <Task FileName="CommonTools %Xml.Platform%" SourceDirectory="%XmlPath.ServerDir%\Tools\bin\%Xml.Platform%" TargetDirectory="%XmlPath.LocalCommonDir%\Tools" DirectoryExcludeRule="excludeFolders-1" FileExcludeRule="excludeFiles-1" />
  15:    </Tasks>

В результате работы кода будут созданы следующие файлы (щёлкните по изображению, чтобы увеличить его):


Приведённый в начале статьи код написан более развёрнуто для того, чтобы дать представление об алгоритме, согласно которому происходит синхронизация локального репозитория с серверным. На самом деле код можно было бы записать более кратко:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using Microsoft.Win32;
   6:  using System.Security.Cryptography;
   7:  using System.Xml.Linq;
   8:  using System.Text.RegularExpressions;
   9:  using System.IO;
  10:  using AdminCAD.Configuration;
  11:  using AdminCAD.Net.Mail;
  12:  using AdminCAD.DirectoryServices;
  13:  using AdminCAD.Autodesk.AutoCAD;
  14:  using AdminCAD.Security.Cryptography;
  15:  using AdminCAD.Configuration.Journaling;
  16:   
  17:  namespace AdminCAD.AcadLibsTest
  18:  {
  19:      class Program
  20:      {
  21:          static void Main(string[] args)
  22:          {
  23:              //Вектор инициализации
  24:              byte[] iv = new byte[] { 100, 149, 158, 255, 68, 25, 176, 203, 162, 203, 171, 160, 19, 74, 119, 34 };
  25:              //Ключ шифрования/дешифровки
  26:              byte[] key = XSecurity.GetBytesFromFile(@"D:\SERVER\AdminCAD\Settings\Key.key");
  27:              //Кодировка символов
  28:              Encoding encoding = Encoding.UTF8;
  29:              //Имя файла общих конфигурационных настроек уровня домена
  30:              string comSetFileName = @"D:\SERVER\AdminCAD\Settings\CommonSettings.set";
  31:              //Получаем объект общих настроек уровня домена
  32:              CommonSettings comSet = CommonSettings.Load(comSetFileName, key, iv, encoding);
  33:              //Версия AutoCAD, для которой хотим получить настройки (в дальнейшем мы можем указывать другую версию через свойство AcadVersion)
  34:              AcadVersion av = new AcadVersion("17.2");
  35:              //Создаём объект, который будет предоставлять нам информацию для той версии AutoCAD, которую мы ему указываем
  36:              CommonSettingsWrapper csw = new CommonSettingsWrapper(av, comSet, key, iv, encoding);
  37:              //Если в процессе работы программы возникнет ошибка - информируем об этом администратора CAD
  38:              try
  39:              {
  40:                  //Обновляем на сервере журналы тех каталогов, содержимое которых зависит от версии AutoCAD
  41:                  csw.UpdateServerDependentJournals();
  42:                  //Обновляем на сервере журналы тех каталогов, содержимое которых не зависит от версии AutoCAD
  43:                  csw.UpdateServerUndependentJournals();
  44:                  //Обновляем на локальной машине журналы и сценарии тех каталогов, содержимое которых зависит от версии AutoCAD
  45:                  csw.UpdateLocalDependentJournals();
  46:                  //Обновляем на локальной машине журналы и сценарии тех каталогов, содержимое которых не зависит от версии AutoCAD
  47:                  csw.UpdateLocalUndependentJournals();
  48:                  //Выполняем все сценарии синхронизации
  49:                  csw.RunAllLocalScenaries();
  50:              }
  51:              catch (Exception ex)
  52:              {
  53:                  //Отправляем администратору CAD информацию о произошедшей ошибке, дополнительно собрав информацию о всех установленных на локальной машине версиях AutoCAD, их обновлениях, а так же информация об операционной системе, о пользователе и т.п.
  54:                  MailManager mng = new MailManager(csw);
  55:                  mng.SendMail(EmailTarget.ErrorReportAddress, "Ошибка синхронизациии", ex.Message, true);
  56:                  //Генерируем исключение
  57:                  throw ex;
  58:              }            
  59:          }
  60:      }
  61:  }

Результат работы кода будет таким же, каким был для первого варианта кода.

Для большей наглядности, далее представлено видео, демонстрирующее синхронизацию локального репозитория с серверным. Наглядно показано, как происходит процесс генерации журналов и сценариев с последующим запуском этих сценариев на исполнение. После того, как сценарий будет успешно выполнен - файл этого сценария удаляется. Если в процессе формирования журнала или сценария происходит ошибка - администратору CAD отправляется письмо с информацией на эту тему.

Видео YouTube


Comments