RFC 8347 A YANG Data Model for the Virtual Router Redundancy Protocol (VRRP)

Internet Engineering Task Force (IETF)                       X. Liu, Ed.
Request for Comments: 8347                                   A. Kyparlis
Category: Standards Track                                          Jabil
ISSN: 2070-1721                                                R. Parikh
                                                                  VMware
                                                               A. Lindem
                                                           Cisco Systems
                                                                M. Zhang
                                                     Huawei Technologies
                                                              March 2018

A YANG Data Model for the Virtual Router Redundancy Protocol (VRRP)

Модель данных YANG для протокола резервирования виртуальных маршрутизаторов (VRRP)

PDF

Аннотация

Этот документ описывает модель данных для для протокола резервирования виртуальных маршрутизаторов (VRRP) версий 2 и 3.

Статус документа

Документ относится к категории Internet Standards Track.

Документ является результатом работы IETF1 и представляет согласованный взгляд сообщества IETF. Документ прошёл открытое обсуждение и был одобрен для публикации IESG2. Дополнительную информацию о стандартах Internet можно найти в разделе 2 в RFC 7841.

Информацию о текущем статусе документа, ошибках и способах обратной связи можно найти по ссылке https://www.rfc-editor.org/info/rfc8347.

Авторские права

Copyright (c) 2018. Авторские права принадлежат IETF Trust и лицам, указанным в качестве авторов документа. Все права защищены.

К документу применимы права и ограничения, указанные в BCP 78 и IETF Trust Legal Provisions и относящиеся к документам IETF (http://trustee.ietf.org/license-info), на момент публикации данного документа. Прочтите упомянутые документы внимательно. Фрагменты программного кода, включённые в этот документ, распространяются в соответствии с упрощённой лицензией BSD, как указано в параграфе 4.e документа IETF Trust Legal Provisions, без каких-либо гарантий (как указано в Simplified BSD License).

1. Введение

Этот документ добавляет модель данных YANG [RFC6020] [RFC7950] для протокола VRRP [RFC3768] [RFC5798], обеспечивающего отказоустойчивость за счёт указания протокола выбора, который динамически назначает ответственность за виртуальный маршрутизатор одному из маршрутизаторов VRRP в ЛВС.

Заданный здесь модуль YANG поддерживает версии 2 и 3 протокола VRRP. Версия VRRP 2 (задана в [RFC3768]) поддерживает IPv4, вресия 3 (задана в [RFC5798]) — IPv4 и IPv6.

1.1. Терминология

Ключевые слова необходимо (MUST), недопустимо (MUST NOT), требуется (REQUIRED), нужно (SHALL), не следует (SHALL NOT), следует (SHOULD), не нужно (SHOULD NOT), рекомендуется (RECOMMENDED), не рекомендуется (NOT RECOMMENDED), возможно (MAY), необязательно (OPTIONAL) в данном документе интерпретируются в соответствии с BCP 14 [RFC2119] [RFC8174] тогда и только тогда, когда они выделены шрифтом, как показано здесь.

Ниже указаны термины, определённые в [RFC7950] и не переопределяемые здесь

  • augment — дополнение;

  • data model — модель данных;

  • data node — узел данных.

1.2. Диаграммы деревьев

В документе применяется графическое представление моделей данных, описанное в [RFC8340].

1.3. Префиксы в именах узлов данных

В этом документе имена узлов данных, действий и других объектов модели часто указываются без префиксов, если из контекста понятно, в каком модуле YANG они определены. В остальных случаях применяется префикс соответствующего модуля YANG, как показано в таблице 1.

Таблица 1. Префиксы и соответствующие модули YANG.

Префикс

Модуль YANG

Документ

yang

ietf-yang-types

[RFC6991]

inet

ietf-inet-types

[RFC6991]

if

ietf-interfaces

[RFC8343]

ip

ietf-ip

[RFC8344]

 

2. Устройство модели данных

2.1. Область действия модели

Модель охватывает VRRP версии 2 [RFC3768] и VRRP версии 3 [RFC5798]. Модель предназначена для реализации на устройствах, где реализован протокол VRRP версии 2 или 3. С подходящим протоколом управления модель может служить:

  • для настройки VRRP версии 2 или 3;

  • для управления поведением операций протоколов;

  • для определения рабочего состояния протокола;

  • для получения протокольных уведомлений.

2.2. Связи с моделями интерфейса и IP

Эта модель дополняет модели данных интерфейса ietf-interfaces [RFC8343] и управления IP ietf-ip [RFC8344]. Связи и дополнения показаны ниже.

   module: ietf-interfaces
      +--rw interfaces
         +--rw interface* [name]
               ...
            +--rw ip:ipv4!
            |  +--rw ip:address* [ip]
                     ...
            |  +--rw vrrp:vrrp
            |     +--rw vrrp:vrrp-instance* [vrid]
            |        +--rw vrrp:vrid                            uint8
            |        +--rw vrrp:virtual-ipv4-addresses
                           ...
            +--rw ip:ipv6!
               +--rw ip:address* [ip]
                     ...
               +--rw vrrp:vrrp
                  +--rw vrrp:vrrp-instance* [vrid]
                     +--rw vrrp:vrid                            uint8
                     +--rw vrrp:virtual-ipv6-addresses
                           ...

В приведённом выше дереве узел без префикса взят из модели ietf-interfaces, узел с префиксом ip: — из модели ietf-ip, узел с префиксом vrrp: — из описанной здесь модели VRRP.

Контейнер vrrp содержит список узлов vrrp-instance, которые создаются в ветви интерфейса для заданного семейства адресов (IPv4 или IPv6). Каждый узел vrrp-instance представляет состояние конечного автомата маршрутизатора VRRP, как описано в параграфе 6.4 [RFC5798], обеспечивая сведения о конфигурации и состоянии для процесса выбора виртуального маршрутизатора. Адреса IP добавленного интерфейса являются реальными адресами, через которые работает маршрутизатор VRRP. Адрес IPv4 или IPv6 или адреса, связанные с виртуальным маршрутизатором (раздел 1 в [RFC5798]) моделируются как список адресов IPv4 или IPv6 в ветви vrrp-instance.

2.3. Конфигурация протокола

Структура модели для настройки протокола показана ниже.

     augment /if:interfaces/if:interface/ip:ipv4:
       +--rw vrrp
          +--rw vrrp-instance* [vrid]
             +--rw vrid                            uint8
             |     ...
             +--rw track
             |  +--rw interfaces
             |  |  +--rw interface* [interface]
             |  |     +--rw interface             if:interface-ref
             |  |           ...
             |  +--rw networks
             |     +--rw network* [prefix]
             |        +--rw prefix                inet:ipv4-prefix
             |              ...
             +--rw virtual-ipv4-addresses
                +--rw virtual-ipv4-address* [ipv4-address]
                   +--rw ipv4-address    inet:ipv4-address

     augment /if:interfaces/if:interface/ip:ipv6:
       +--rw vrrp
          +--rw vrrp-instance* [vrid]
             +--rw vrid                            uint8
             |     ...
             +--rw track
             |  +--rw interfaces
             |  |  +--rw interface* [interface]
             |  |     +--rw interface             if:interface-ref
             |  |           ...
             |  +--rw networks
             |     +--rw network* [prefix]
             |        +--rw prefix                inet:ipv6-prefix
             |              ...
             +--rw virtual-ipv6-addresses
                +--rw virtual-ipv6-address* [ipv6-address]
                   +--rw ipv6-address    inet:ipv6-address

Модель позволяет настраивать:

  • экземпляр VRRP (версии 2 или 3), представляющий маршрутизатор VRRP;

  • виртуальный адрес IPv4 или IPv6, связанный с виртуальным маршрутизатором;

  • интерфейс отслеживания для обнаружения отказов связности интерфейса;

  • сеть отслеживания для обнаружения отказов связности с сетью.

2.4. Состояния протокола

Ниже показана структура модели для состояний протокола.

   module: ietf-vrrp
       +--ro vrrp
          |     // global operational states
          +--ro virtual-routers?   uint32
          +--ro interfaces?        uint32
          +--ro statistics                 // global statistics
             +--ro discontinuity-datetime?   yang:date-and-time
             +--ro checksum-errors?          yang:counter64
             +--ro version-errors?           yang:counter64
             +--ro vrid-errors?              yang:counter64
             +--ro ip-ttl-errors?            yang:counter64

     augment /if:interfaces/if:interface/ip:ipv4:
       +--rw vrrp
          +--rw vrrp-instance* [vrid]
             +--rw vrid                            uint8
             |     ...
             +--rw track
             |  +--rw interfaces
             |  |  +--rw interface* [interface]
             |  |     +--rw interface             if:interface-ref
             |  |           ...
             |  +--rw networks
             |     +--rw network* [prefix]
             |        +--rw prefix                inet:ipv4-prefix
             |              ...
             +--rw virtual-ipv4-addresses
             |  +--rw virtual-ipv4-address* [ipv4-address]
             |     +--rw ipv4-address    inet:ipv4-address
             |
             |     // per-instance operational states
             +--ro state?                         identityref
             +--ro is-owner?                      boolean
             +--ro last-adv-source?               inet:ip-address
             +--ro up-datetime?                   yang:date-and-time
             +--ro master-down-interval?          uint32
             +--ro skew-time?                     uint32
             +--ro last-event?                    identityref
             +--ro new-master-reason?             new-master-reason-type
             +--ro statistics                // per-instance statistics
                +--ro discontinuity-datetime?    yang:date-and-time
                +--ro master-transitions?        yang:counter32
                +--ro advertisement-rcvd?        yang:counter64
                +--ro advertisement-sent?        yang:counter64
                +--ro interval-errors?           yang:counter64
                |       {validate-interval-errors}?
                +--ro priority-zero-pkts-rcvd?   yang:counter64
                +--ro priority-zero-pkts-sent?   yang:counter64
                +--ro invalid-type-pkts-rcvd?    yang:counter64
                +--ro address-list-errors?       yang:counter64
                |       {validate-address-list-errors}?
                +--ro packet-length-errors?      yang:counter64

     augment /if:interfaces/if:interface/ip:ipv6:
       +--rw vrrp
          +--rw vrrp-instance* [vrid]
             +--rw vrid                            uint8
             +     ...
             +--rw track
             |  +--rw interfaces
             |  |  +--rw interface* [interface]
             |  |     +--rw interface             if:interface-ref
             |  |           ...
             |  +--rw networks
             |     +--rw network* [prefix]
             |        +--rw prefix                inet:ipv6-prefix
             |              ...
             +--rw virtual-ipv6-addresses
             |  +--rw virtual-ipv6-address* [ipv6-address]
             |     +--rw ipv6-address    inet:ipv6-address
             |
             |     // per-instance operational states
             +--ro state?                         identityref
             +--ro is-owner?                      boolean
             +--ro last-adv-source?               inet:ip-address
             +--ro up-datetime?                   yang:date-and-time
             +--ro master-down-interval?          uint32
             +--ro skew-time?                     uint32
             +--ro last-event?                    identityref
             +--ro new-master-reason?             new-master-reason-type
             +--ro statistics                // per-instance statistics
                +--ro discontinuity-datetime?    yang:date-and-time
                +--ro master-transitions?        yang:counter32
                +--ro advertisement-rcvd?        yang:counter64
                +--ro advertisement-sent?        yang:counter64
                +--ro interval-errors?           yang:counter64
                |       {validate-interval-errors}?
                +--ro priority-zero-pkts-rcvd?   yang:counter64
                +--ro priority-zero-pkts-sent?   yang:counter64
                +--ro invalid-type-pkts-rcvd?    yang:counter64
                +--ro address-list-errors?       yang:counter64
                |       {validate-address-list-errors}?
                +--ro packet-length-errors?      yang:counter64

Эта модель соответствует архитектуре хранилищ данных управления сетью (Network Management Datastore Architecture или NMDA) [RFC8342]. Данные рабочего состояния объединяются с соответствующими данными конфигурации в одной иерархии [YANG-Guidelines]. Когда состояния протокола извлекаются из рабочего хранилища NMDA, охватываются все узлы config true (rw) и config false (ro), определённые в схеме.

Модель позволяет извлекать состояния протокола на указанных ниже уровнях.

  • Экземпляр VRRP (версии 2 или 3), представляющий маршрутизатор VRRP.

  • Виртуальный адрес IPv4 или IPv6, связанный с виртуальным маршрутизатором.

  • Интерфейс отслеживания для обнаружения отказов связности интерфейса.

  • Сеть отслеживания для обнаружения отказов связности с сетью.

  • Глобальные состояния и статистика со сводкой для всех экзепляров.

2.5. Уведомления

Эта модель определяет указанные ниже уведомления, связанные с VRRP.

   notifications:
      +---n vrrp-new-master-event
      |  +--ro master-ip-address    inet:ip-address
      |  +--ro new-master-reason    new-master-reason-type
      +---n vrrp-protocol-error-event
      |  +--ro protocol-error-reason    identityref
      +---n vrrp-virtual-router-error-event
         +--ro interface                      if:interface-ref
         +--ro (ip-version)
         |  +--:(ipv4)
         |  |  +--ro ipv4
         |  |     +--ro vrid    leafref
         |  +--:(ipv6)
         |     +--ro ipv6
         |        +--ro vrid    leafref
         +--ro virtual-router-error-reason    identityref

Каждый тип уведомления служит для указания типа смены состояния VRRP или возникновения ошибки.

vrrp-new-master-event

Выбран новый ведущий VRRP.

vrrp-protocol-error-event

Ошибка протокола VRRP для сообщения, которое не может достичь экземпляра VRRP для своей обработки.

vrrp-virtual-router-error-event

Ошибка виртуального маршрутизатора VRRP для сообщения, обрабатываемого экземпляром VRRP.

В дополнение к указанным уведомлениям могут применяться механизмы, определённые в [Subscribed-Notifications] и [YANG-Push], которые позволяют пользователю:

  • подписаться на уведомления на уровне клиента;

  • задать фильтры ветвей XML Path Language (XPath) для отправки лишь интересующего содержимого;

  • задать переиодические уведомления или уведомления по запросу.

3. Структура дерева

Дерево модели данных YANG для VRRP, заданной этим этим документом, показано ниже.

   module: ietf-vrrp
       +--ro vrrp
          +--ro virtual-routers?   uint32
          +--ro interfaces?        uint32
          +--ro statistics
             +--ro discontinuity-datetime?   yang:date-and-time
             +--ro checksum-errors?          yang:counter64
             +--ro version-errors?           yang:counter64
             +--ro vrid-errors?              yang:counter64
             +--ro ip-ttl-errors?            yang:counter64
     augment /if:interfaces/if:interface/ip:ipv4:
       +--rw vrrp
          +--rw vrrp-instance* [vrid]
             +--rw vrid                            uint8
             +--rw version                         identityref
             +--rw log-state-change?               boolean
             +--rw preempt
             |  +--rw enabled?     boolean
             |  +--rw hold-time?   uint16
             +--rw priority?                       uint8
             +--rw accept-mode?                    boolean
             +--rw (advertise-interval-choice)?
             |  +--:(v2)
             |  |  +--rw advertise-interval-sec?         uint8
             |  +--:(v3)
             |     +--rw advertise-interval-centi-sec?   uint16
             +--rw track
             |  +--rw interfaces
             |  |  +--rw interface* [interface]
             |  |     +--rw interface             if:interface-ref
             |  |     +--rw priority-decrement?   uint8
             |  +--rw networks
             |     +--rw network* [prefix]
             |        +--rw prefix                inet:ipv4-prefix
             |        +--rw priority-decrement?   uint8
             +--rw virtual-ipv4-addresses
             |  +--rw virtual-ipv4-address* [ipv4-address]
             |     +--rw ipv4-address    inet:ipv4-address
             +--ro state?                          identityref
             +--ro is-owner?                       boolean
             +--ro last-adv-source?                inet:ip-address
             +--ro up-datetime?                    yang:date-and-time
             +--ro master-down-interval?           uint32
             +--ro skew-time?                      uint32
             +--ro last-event?                     identityref
             +--ro new-master-reason?
    new-master-reason-type
             +--ro statistics
                +--ro discontinuity-datetime?    yang:date-and-time
                +--ro master-transitions?        yang:counter32
                +--ro advertisement-rcvd?        yang:counter64
                +--ro advertisement-sent?        yang:counter64
                +--ro interval-errors?           yang:counter64
                |       {validate-interval-errors}?
                +--ro priority-zero-pkts-rcvd?   yang:counter64
                +--ro priority-zero-pkts-sent?   yang:counter64
                +--ro invalid-type-pkts-rcvd?    yang:counter64
                +--ro address-list-errors?       yang:counter64
                |       {validate-address-list-errors}?
                +--ro packet-length-errors?      yang:counter64
     augment /if:interfaces/if:interface/ip:ipv6:
       +--rw vrrp
          +--rw vrrp-instance* [vrid]
             +--rw vrid                            uint8
             +--rw version                         identityref
             +--rw log-state-change?               boolean
             +--rw preempt
             |  +--rw enabled?     boolean
             |  +--rw hold-time?   uint16
             +--rw priority?                       uint8
             +--rw accept-mode?                    boolean
             +--rw advertise-interval-centi-sec?   uint16
             +--rw track
             |  +--rw interfaces
             |  |  +--rw interface* [interface]
             |  |     +--rw interface             if:interface-ref
             |  |     +--rw priority-decrement?   uint8
             |  +--rw networks
             |     +--rw network* [prefix]
             |        +--rw prefix                inet:ipv6-prefix
             |        +--rw priority-decrement?   uint8
             +--rw virtual-ipv6-addresses
             |  +--rw virtual-ipv6-address* [ipv6-address]
             |     +--rw ipv6-address    inet:ipv6-address
             +--ro state?                          identityref
             +--ro is-owner?                       boolean
             +--ro last-adv-source?                inet:ip-address
             +--ro up-datetime?                    yang:date-and-time
             +--ro master-down-interval?           uint32
             +--ro skew-time?                      uint32
             +--ro last-event?                     identityref
             +--ro new-master-reason?
    new-master-reason-type
             +--ro statistics
                +--ro discontinuity-datetime?    yang:date-and-time
                +--ro master-transitions?        yang:counter32
                +--ro advertisement-rcvd?        yang:counter64
                +--ro advertisement-sent?        yang:counter64
                +--ro interval-errors?           yang:counter64
                |       {validate-interval-errors}?
                +--ro priority-zero-pkts-rcvd?   yang:counter64
                +--ro priority-zero-pkts-sent?   yang:counter64
                +--ro invalid-type-pkts-rcvd?    yang:counter64
                +--ro address-list-errors?       yang:counter64
                |       {validate-address-list-errors}?
                +--ro packet-length-errors?      yang:counter64

     notifications:
       +---n vrrp-new-master-event
       |  +--ro master-ip-address    inet:ip-address
       |  +--ro new-master-reason    new-master-reason-type
       +---n vrrp-protocol-error-event
       |  +--ro protocol-error-reason    identityref
       +---n vrrp-virtual-router-error-event
          +--ro interface                      if:interface-ref
          +--ro (ip-version)
          |  +--:(ipv4)
          |  |  +--ro ipv4
          |  |     +--ro vrid    leafref
          |  +--:(ipv6)
          |     +--ro ipv6
          |        +--ro vrid    leafref
          +--ro virtual-router-error-reason    identityref

4. Модуль YANG

Этот модуль ссылается на [RFC2787], [RFC3768], [RFC5798], [RFC6527].

   <CODE BEGINS> file "ietf-vrrp@2018-03-13.yang"

   module ietf-vrrp {
     yang-version 1.1;
     namespace "urn:ietf:params:xml:ns:yang:ietf-vrrp";
     prefix "vrrp";

     import ietf-inet-types {
       prefix "inet";
     }

     import ietf-yang-types {
       prefix "yang";
     }

     import ietf-interfaces {
       prefix "if";
     }

     import ietf-ip {
       prefix "ip";
     }

     organization
       "IETF Routing Area Working Group (RTGWG)";
     contact
       "WG Web:   <https://datatracker.ietf.org/wg/rtgwg/> 
        WG List:  <mailto:rtgwg@ietf.org> 

        Editor:   Xufeng Liu
                  <mailto:xufeng.liu.ietf@gmail.com> 

        Editor:   Athanasios Kyparlis
                  <mailto:Athanasios_Kyparlis@jabil.com> 
        Editor:   Ravi Parikh
                  <mailto:parikhr@vmware.com> 

        Editor:   Acee Lindem
                  <mailto:acee@cisco.com> 

        Editor:   Mingui Zhang
                  <mailto:zhangmingui@huawei.com>"; 

     description
       "Этот модуль YANG задаёт модель для управления протоколом VRRP
        версий 2 и 3.

        Авторские права (Copyright (c) 2018) принадлежат IETF Trust
        и лицам, указанным в качестве авторов кода. Все права защищены.

        Распространение и использование в исходной или двоичной форме с
        изменениями или без таковых разрешено в соответствии с лицензией
        Simplified BSD, изложенной в разделе 4  IETF Trust's Legal
        Provisions применительно к документам IETF
        (http://trustee.ietf.org/license-info). 
 
        Эта версия данного модуля YANG является частью RFC 8347, где
        правовые вопросы рассмотрены более полно.";

     revision 2018-03-13 {
       description
         "Исходный выпуск.";
       reference
         "RFC 8347: A YANG Data Model for the Virtual Router Redundancy
                    Protocol (VRRP)
          RFC 2787: Definitions of Managed Objects for the Virtual
                    Router Redundancy Protocol
          RFC 3768: Virtual Router Redundancy Protocol (VRRP)
          RFC 5798: Virtual Router Redundancy Protocol (VRRP)
                    Version 3 for IPv4 and IPv6
          RFC 6527: Definitions of Managed Objects for the Virtual
                    Router Redundancy Protocol Version 3 (VRRPv3)";
     }

     /*
      * Свойства (функции)
      */

     feature validate-interval-errors {
       description
         "Указывает, что система проверяет, соответствует ли интервал
          анонсов в полученных пакетах интервалу, заданному для 
          локального маршрутизатора VRRP.";
     }

     feature validate-address-list-errors {
       description
         "Указывает, что система проверяет соответствие списка адресов в
          полученных пакетах настроенному локально списку для 
          маршрутизатора VRRP.";
     }

     /*
      * Определения типов
      */
     typedef new-master-reason-type {
       type enumeration {
         enum not-master {
           description
             "Виртульный маршрутизатор никогда не был ведущим.";
         }
         enum priority {
           description
             "Приоритет был выше.";
         }
         enum preempted {
           description
             "Ведущий (master) был вытеснен (сменён).";
         }
         enum no-response {
           description
             "Прежний ведущий не отвечает.";
         }
       }
       description
         "Указывает переход виртуального маршрутизатора в
          состояние master.";
     } // new-master-reason-type

     /*
      * Отождествления (идентификаторы)
      */

     /* vrrp-event-type и производные от него. */
     identity vrrp-event-type {
       description
         "Тип события протокола VRRP.";
     }
     identity vrrp-event-none {
       base vrrp-event-type;
       description
         "Незначимое событие.";
     }
     identity vrrp-event-startup {
       base vrrp-event-type;
       description
         "Экземпляр маршрутизатора VRRP создан протоколом.";
     }
     identity vrrp-event-shutdown {
       base vrrp-event-type;
       description
         "Маршрутизатор VRRP закрыт (down) протоколом.";
     }
     identity vrrp-event-higher-priority-backup {
       base vrrp-event-type;
       description
         "Резервный (backup) маршрутизатор имеет приоритет вше, чем
          имеющийся ведущий (master).";
     }
     identity vrrp-event-master-timeout {
       base vrrp-event-type;
       description
         "Текущий master не передал анонса в интервале
          master-down-interval.";
     }
     identity vrrp-event-interface-up {
       base vrrp-event-type;
       description
         "Интерфейс со включенным VRRP перешёл в состояние
          operational up.";
     }
     identity vrrp-event-interface-down {
       base vrrp-event-type;
       description
         "Интерфейс со включенным VRRP перешёл в состояние
          operational down.";
     }
     identity vrrp-event-no-primary-ip-address {
       base vrrp-event-type;
       description
         "Основной адрес IP на интерфейсе с VRRP утратил доступность.";
     }
     identity vrrp-event-primary-ip-address {
       base vrrp-event-type;
       description
         "Основной адрес IP на интерфейсе с VRRP стал доступным.";
     }
     identity vrrp-event-no-virtual-ip-addresses {
       base vrrp-event-type;
       description
         "На виртуальном маршрутизаторе нет виртуального адреса IP.";
     }
     identity vrrp-event-virtual-ip-addresses {
       base vrrp-event-type;
       description
         "На виртуальном маршрутизаторе есть виртуальные адреса IP.";
     }
     identity vrrp-event-preempt-hold-timeout {
       base vrrp-event-type;
       description
         "Указывает, что заданное время удержания вытеснения прошло.";
     }
     identity vrrp-event-lower-priority-master {
       base vrrp-event-type;
       description
         "Указывает наличие VRRP master с меньшим приоритетом.";
     }
     identity vrrp-event-owner-preempt {
       base vrrp-event-type;
       description
         "Владелец сделал ведущим (master) другой маршрутизатор.";
     }

     /* vrrp-error-global и производные от него. */
     identity vrrp-error-global {
       description
         "Тип ошибки VRRP, возникшей для пакета до попадания
          на маршрутизатор VRRP.";
     }
     identity checksum-error {
       base vrrp-error-global;
       description
         "Получен пакет с ошибкой контрольной суммы VRRP.";
     }
     identity ip-ttl-error {
       base vrrp-error-global;
       description
         "Получен пакет с IP TTL (Time-To-Live), отличным от 255.";
     }
     identity version-error {
       base vrrp-error-global;
       description
         "Получен пакет неизвестной или неподдерживаемой версии.";
     }
     identity vrid-error {
       base vrrp-error-global;
       description
         "Получен пакет с идентификатором виртуального маршрутизатора
          (VRID), не действительным на этом маршрутизаторе.";
     }

     /* vrrp-error-virtual-router и производные от него. */
     identity vrrp-error-virtual-router {
       description
         "Ошибка VRRP, возникшая после попадания пакета на
          маршрутизатор VRRP.";
     }
     identity address-list-error {
       base vrrp-error-virtual-router;
       description
         "Получен пакет со списком адресов, не соответствующим
          заданному локально списку для виртуального маршрутизатора.";
     }
     identity interval-error {
       base vrrp-error-virtual-router;
       description
         "Получен пакет с интервалом анонсирования, отличным от
          заданного для локального виртуального маршрутизатора.";
     }
     identity packet-length-error {
       base vrrp-error-virtual-router;
       description
         "Получен пакет с размером меньше размера заголовка VRRP.";
     }

     /* vrrp-state-type и производные от него. */
     identity vrrp-state-type {
       description
         "Состояние виртуального маршрутизатора.";
     }
     identity initialize {
       base vrrp-state-type;
       description
         "Виртуальный маршрутизатор ждёт стартового события.";
     }
     identity backup {
       base vrrp-state-type;
       description
         "Виртуальный маршрутизатор отслеживает доступность ведущего.";
     }
     identity master {
       base vrrp-state-type;
       description
         "Указывает, что виртуальный маршрутизатор пересылает пакеты для
          адресов IP, связанных с этим виртуальным маршрутизатором.";
     }

     /* vrrp-version и производные от него. */
     identity vrrp-version {
       description
         "Версия VRRP.";
     }
     identity vrrp-v2 {
       base vrrp-version;
       description
         "VRRP версии 2.";
     }
     identity vrrp-v3 {
       base vrrp-version;
       description
         "VRRP версии 3.";
     }

     /*
      * Группировки
      */

     grouping vrrp-common-attributes {
       description
         "Группа общих атрибутов VRRP для версий 2 и 3.";

       leaf vrid {
         type uint8 {
           range "1..255";
         }
         description
           "Идентификатор виртуального маршрутизатора (VRID).";
       }

       leaf version {
         type identityref {
           base vrrp:vrrp-version;
         }
         mandatory true;
         description
           "VRRP версии 2 или 3.";
       }

       leaf log-state-change {
         type boolean;
         default "false";
         description
           "Задаёт генерацию сообщения о смене состояния VRRP при каждом
            изменении статуса экземпляра VRRP (up в down или обратно).";
       }

       container preempt {
         description
           "Позволяет резервному маршрутизатору VRRP с более высоким
            приоритетом вытеснить VRRP master с меньшим приоритетом.";
         leaf enabled {
           type boolean;
           default "true";
           description
             "Значение true разрешает вытеснение.";
         }
         leaf hold-time {
           type uint16;
           units seconds;
           default 0;
           description
             "Время (в секундах), в течение которого backup-маршрутизатор
              VRRP с более высоким приоритетом должен ждать перед 
              вытеснением VRRP master.";
         }
       }

       leaf priority {
         type uint8 {
           range "1..254";
         }
         default 100;
         description
           "Приоритет выбора VRRP для резервного виртуального 
            маршрутизатора.";
       }

       leaf accept-mode {
         when "derived-from-or-self(current()/../version, 'vrrp-v3')" {
           description
             "Применимо лишь для версии 3.";
         }
         type boolean;
         default "false";
         description
           "Задаёт, будет ли виртуальный маршрутизатор master
            воспринимать пакеты, направленные владельцу адреса IPvX, как 
            свои, если адрес IPvX не принадлежит ему. По умолчанию 
            задано false. Развёртывания, полагающиеся, например, на
            ping по адресу владельца IPvX могут захотеть установки
            accept-mode true.

            Примечание. Сообщения IPv6 Neighbor Solicitations и Neighbor
            Advertisements НЕДОПУСТИМО отбрасывать при accept-mode
            false.";
       }
     } // vrrp-common-attributes

     grouping vrrp-ipv4-attributes {
       description
         "Группа атрибутов VRRP для IPv4.";

       uses vrrp-common-attributes;

       choice advertise-interval-choice {
         description
           "Опции для интервала, с которым анонсы VRRPv2 или VRRPv3
            передаются с указанного интерфейса.";

         case v2 {
           when "derived-from-or-self(version, 'vrrp-v2')" {
             description
               "Применимо лишь для версии 2.";
           }
           leaf advertise-interval-sec {
             type uint8 {
               range "1..254";
             }
             units seconds;
             default 1;
             description
               "Интервал, с которым анонсы VRRPv2 передаются с
                указанного интерфейса.";
           }
         }

         case v3 {
           when "derived-from-or-self(version, 'vrrp-v3')" {
             description
               "Применимо лишь для версии 3.";
           }
           leaf advertise-interval-centi-sec {
             type uint16 {
               range "1..4095";
             }
             units centiseconds;
             default 100;
             description
               "Интервал, с которым анонсы VRRPv3 передаются с
                указанного интерфейса.";
           }
         }
       } // advertise-interval-choice

       container track {
         description
           "Разрешает указанному экземпляру VRRP отслеживать
            интерфейсы или сети.";
         container interfaces {
           description
             "Разрешает заданному экземпляру VRRPv2 или VRRPv3 
              отслеживать интерфейсы. Это предотвращает потери трафика
              за счёт обнаружения доступности интерфейсов. Рабочие 
              состояния других интерфейсов связаны с приоритетом
              маршрутизатора VRRP. Когда отслеживаемый интерфейс
              становится недоступным (или operational down), приоритет
              маршрутизатора VRRP декрементируется. Когда доступность
              интерфейса восстанавливается, приоритет маршрутизатора
              VRRP инкрементируется на то же значение.";

           list interface {
             key "interface";
             description
               "Интерфейс для отслеживания.";
             leaf interface {
               type if:interface-ref;
               must "/if:interfaces/if:interface[if:name=current()]/"
                  + "ip:ipv4" {
                 description
                   "Интерфейс IPv4.";
               }
               description
                 "Интерфейс для отслеживания.";
             }

             leaf priority-decrement {
               type uint8 {
                 range "1..254";
               }
               default 10;
               description
                 "Значение декремента приоритета экземпляра VRRP при
                  отключении (down) интерфейса.";
             }
           } // interface
         } // interfaces

         container networks {
           description
             "Позволяет экземпляру маршрутизатора VRRPv2 или VRRPv3 
              отслеживать сети по заданному префиксу IPv4. Отслеживание
              сетей предотвращает потери трафика за счёт обнаружения
              отказов связности. Состояния связности с некоторыми сетями
              привязываются к приоритету маршрутизатора VRRP. Когда
              связность с сетью, представленной префиксом, теряется, 
              приоритет маршрутизатора VRRP декрементируется. При
              восстановлении связности приоритет инкрементируется на
              то же значение.";
           list network {
             key "prefix";
             description
               "Позволяет экземпляру маршрутизатора VRRPv2 или VRRPv3 
                отслеживать сеть IPv4 путём задания её префикса IPv4.";

             leaf prefix {
               type inet:ipv4-prefix;
               description
                 "Префикс IPv4 для отслеживаемой сети.";
             }

             leaf priority-decrement {
               type uint8 {
                 range "1..254";
               }
               default 10;
               description
                 "Значение декремента приоритета экземпляра VRRP при
                  отказе сети IPv4.";
             }
           } // network
         } // networks
       } // track

       container virtual-ipv4-addresses {
         description
           "Задаёт виртуальный адрес IPv4 для интерфейса VRRP.";

         list virtual-ipv4-address {
           key "ipv4-address";
           max-elements 16;
           description
             "Виртуальный адрес IPv4 для одного экземпляра VRRP. Для
              владеющего VRRP маршрутизатора виртуальный адрес должен
              совпадать с одним из адресов IPv4 на интерфейсе, 
              соответствующем виртуальному маршрутизатору.";

           leaf ipv4-address {
             type inet:ipv4-address;
             description
               "Адрес IPv4, связанный с виртуальным маршрутизатором.";
             reference
               "RFC 5798: Virtual Router Redundancy Protocol (VRRP)
                          Version 3 for IPv4 and IPv6.  Section 1.2";
           }
         } // virtual-ipv4-address
       } // virtual-ipv4-addresses
     } // vrrp-ipv4-attributes

     grouping vrrp-ipv6-attributes {
       description
         "Группа атрибутов VRRP для IPv6.";

       uses vrrp-common-attributes;

       leaf advertise-interval-centi-sec {
         type uint16 {
           range "1..4095";
         }
         units centiseconds;
         default 100;
         description
           "Интервал, с которым анонсы VRRPv3 передаются с
            указанного интерфейса.";
       }

       container track {
         description
           "Разрешает указанному экземпляру VRRP отслеживать
            интерфейсы или сети.";
         container interfaces {
           description
             "Разрешает заданному экземпляру VRRPv2 или VRRPv3 
              отслеживать интерфейсы. Это предотвращает потери трафика
              за счёт обнаружения доступности интерфейсов. Рабочие 
              состояния других интерфейсов связаны с приоритетом
              маршрутизатора VRRP. Когда отслеживаемый интерфейс
              становится недоступным (или operational down), приоритет
              маршрутизатора VRRP декрементируется. Когда доступность
              интерфейса восстанавливается, приоритет маршрутизатора
              VRRP инкрементируется на то же значение.";
           list interface {
             key "interface";
             description
               "Интерфейс для отслеживания.";

             leaf interface {
               type if:interface-ref;
               must "/if:interfaces/if:interface[if:name=current()]/"
                  + "ip:ipv6" {
                 description
                   "Интерфейс IPv6.";
               }
               description
                 "Интерфейс для отслеживания.";
             }

             leaf priority-decrement {
               type uint8 {
                 range "1..254";
               }
               default 10;
               description
                 "Значение декремента приоритета экземпляра VRRP при
                  отключении (down) интерфейса.";
             }
           } // interface
         } // interfaces

         container networks {
           description
             "Позволяет экземпляру маршрутизатора VRRPv2 или VRRPv3 
              отслеживать сети по заданному префиксу IPv6. Отслеживание
              сетей предотвращает потери трафика за счёт обнаружения
              отказов связности. Состояния связности с некоторыми сетями
              привязываются к приоритету маршрутизатора VRRP. Когда
              связность с сетью, представленной префиксом, теряется, 
              приоритет маршрутизатора VRRP декрементируется. При
              восстановлении связности приоритет инкрементируется на
              то же значение.";
           list network {
             key "prefix";
             description
               "Позволяет экземпляру маршрутизатора VRRPv2 или VRRPv3 
                отслеживать сеть IPv6 путём задания её префикса IPv6.";

             leaf prefix {
               type inet:ipv6-prefix;
               description
                 "Префикс IPv6 для отслеживаемой сети.";
             }

             leaf priority-decrement {
               type uint8 {
                 range "1..254";
               }
               default 10;
               description
                 "Значение декремента приоритета экземпляра VRRP при
                  отказе сети IPv6.";
             }
           } // network
         } // networks
       } // track

       container virtual-ipv6-addresses {
         description
           "Задаёт виртуальный адрес IPv6 для интерфейса VRRP.";
         list virtual-ipv6-address {
           key "ipv6-address";
           max-elements 2;
           description
             "Разрешено два адреса IPv6, первый из которых должен быть
              link-local, а второй может быть глобальным или
              link-local.";

           leaf ipv6-address {
             type inet:ipv6-address;
             description
               "Адрес IPv6, связанный с виртуальным маршрутизатором.";
             reference
               "RFC 5798: Virtual Router Redundancy Protocol (VRRP)
                          Version 3 for IPv4 and IPv6.  Section 1.3";
           }
         } // virtual-ipv6-address
       } // virtual-ipv6-addresses
     } // vrrp-ipv6-attributes

     grouping vrrp-state-attributes {
       description
         "Группа атрибутов состояния VRRP.";

       leaf state {
         type identityref {
           base vrrp:vrrp-state-type;
         }
         config false;
         description
           "Рабочее состояние.";
       }

       leaf is-owner {
         type boolean;
         config false;
         description
           "Значение true, если этот виртуальный маршрутизатор
            является владельцем.";
       }

       leaf last-adv-source {
         type inet:ip-address;
         config false;
         description
           "Анонсированный последним адрес источника IPv4/IPv6.";
       }

       leaf up-datetime {
         type yang:date-and-time;
         config false;
         description
           "Дата и время выхода виртуального маршрутизатора
            из состояния init.";
       }

       leaf master-down-interval {
         type uint32;
         units centiseconds;
         config false;
         description
           "Интервал для резервного маршрутизатора при 
            декларировании master down.";
       }

       leaf skew-time {
         type uint32;
         units microseconds;
         config false;
         description
           "Рассчитывается на основе параметров приоритета и интервала
            анонсирования, заданных командой. См. RFC 3768.";
       }

       leaf last-event {
         type identityref {
           base vrrp:vrrp-event-type;
         }
         config false;
         description
           "Сообщенное последним событие.";
       }

       leaf new-master-reason {
         type new-master-reason-type;
         config false;
         description
           "Причина перехода виртуального маршрутизатора
            в состояние master.";
       }

       container statistics {
         config false;
         description
           "Статистика VRRP.";

         leaf discontinuity-datetime {
           type yang:date-and-time;
           description
             "Время последнего разрыва одного или нескольких счетчиков
              статистики VRRP. Если разрывов не было с момента
              реинициализации локальной подсистемы управления, этот узел
              содержит время реинициализации этой подсистемы.";
         }

         leaf master-transitions {
           type yang:counter32;
           description
             "Общее число переходов этого виртуального маршрутизатора 
              в состояние master.";
         }

         leaf advertisement-rcvd {
           type yang:counter64;
           description
             "Общее число анонсов VRRP, полученных этим виртуальным
              маршрутизатором.";
         }

         leaf advertisement-sent {
           type yang:counter64;
           description
             "Общее число анонсов VRRP, переданных этим виртуальным
              маршрутизатором.";
         }

         leaf interval-errors {
           if-feature validate-interval-errors;
           type yang:counter64;
           description
             "Общее число пакетов с анонсами VRRP, полученных с
              интервалом, отличным от заданного для локального
              виртуального маршрутизатора.";
         }

         leaf priority-zero-pkts-rcvd {
           type yang:counter64;
           description
             "Общее число пакетов VRRP, полученных виртуальным
              маршрутизатором с приоритетом 0.";
         }

         leaf priority-zero-pkts-sent {
           type yang:counter64;
           description
             "Общее число пакетов VRRP, переданных виртуальным
              маршрутизатором с приоритетом 0.";
         }

         leaf invalid-type-pkts-rcvd {
           type yang:counter64;
           description
             "Общее число пакетов VRRP, полученных виртуальным
              маршрутизатором с непригодным значением поля type.";
         }

         leaf address-list-errors {
           if-feature validate-address-list-errors;
           type yang:counter64;
           description
             "Общее число пакетов VRRP, полученных со списком адресов,
              не совпадающим с локально настроенным списком адресов
              для виртуального маршрутизатора.";
         }

         leaf packet-length-errors {
           type yang:counter64;
           description
             "Общее число пакетов VRRP, полученных с размером меньше
              размера заголовка VRRP.";
         }
       } // statistics
     } // vrrp-state-attributes

     grouping vrrp-global-state-attributes {
       description
         "Группа атрибутов глобального состояния VRRP.";

       leaf virtual-routers {
         type uint32;
         description
           "Число настроенных виртуальных маршрутизаторов.";
       }

       leaf interfaces {
         type uint32;
         description
           "Число интерфейсов с настроенным VRRP.";
       }

       container statistics {
         description
           "Глобальная статистика VRRP.";

         leaf discontinuity-datetime {
           type yang:date-and-time;
           description
             "Время последнего разрыва одного или нескольких значений 
              checksum-errors, version-errors, vrid-errors, 
              ip-ttl-errors. Если разрывов не было с момента
              реинициализации локальной подсистемы управления, этот узел
              содержит время реинициализации этой подсистемы.
         }

         leaf checksum-errors {
           type yang:counter64;
           description
             "Общее число пакетов VRRP, полученных с ошибкой 
              контрольной суммы VRRP.";
           reference
             "RFC 5798: Virtual Router Redundancy Protocol (VRRP)
                        Version 3 for IPv4 and IPv6.  Section 5.2.8";
         }

         leaf version-errors {
           type yang:counter64;
           description
             "Общее число пакетов VRRP, полученных с неизвестным 
              или неподдерживаемым номером версии.";
           reference
             "RFC 5798: Virtual Router Redundancy Protocol (VRRP)
                        Version 3 for IPv4 and IPv6.  Section 5.2.1";
         }

         leaf vrid-errors {
           type yang:counter64;
           description
             "Общее число пакетов VRRP, полученных с VRID, не 
              подходящим ни одному виртуальному маршрутизатору
              на этом маршрутизаторе.";
           reference
             "RFC 5798: Virtual Router Redundancy Protocol (VRRP)
                        Version 3 for IPv4 and IPv6.  Section 5.2.3";
         }

         leaf ip-ttl-errors {
           type yang:counter64;
           description
             "Общее число пакетов VRRP, полученных виртуальным
              маршрутизатором с IP TTL (IPv4) или Hop Limit (IPv6),
              отличным от 255.";
           reference
             "RFC 5798: Virtual Router Redundancy Protocol (VRRP)
                        Version 3 for IPv4 and IPv6.
                        Sections 5.1.1.3 and 5.1.2.3";
         }
       } // statistics
     } // vrrp-global-state-attributes

     /*
      * Узлы данных конфигурации и рабочего состояния
      */

     augment "/if:interfaces/if:interface/ip:ipv4" {
       description
         "Дополняет интерфейс IPv4.";

       container vrrp {
         description
           "Настраивает VRRP версии 2 или 3 для IPv4.";

         list vrrp-instance {
           key "vrid";
           description
             "Задаёт виртуальный маршрутизатор, идентифицируемый
              VRID, в адресном пространстве IPv4.";

           uses vrrp-ipv4-attributes;
           uses vrrp-state-attributes;
         }
       }
     } // augments ipv4

     augment "/if:interfaces/if:interface/ip:ipv6" {
       description
         "Дополняет интерфейс IPv6.";

       container vrrp {
         description
           "Настраивает VRRP верси 3 для IPv6.";

         list vrrp-instance {
           must "derived-from-or-self(version, 'vrrp-v3')" {
             description
               "IPv6 поддерживается только версией 3.";
           }
           key "vrid";
           description
             "Задаёт виртуальный маршрутизатор, идентифицируемый
              VRID, в адресном пространстве IPv6.";

           uses vrrp-ipv6-attributes;
           uses vrrp-state-attributes;
         }
       }
     } // augments ipv6

     container vrrp {
       config false;
       description
         "Данные VRRP на глобальном уровне.";

       uses vrrp-global-state-attributes;
     }

     /*
      * Уведомления
      */

     notification vrrp-new-master-event {
       description
         "Уведомление для выбора нового VRRP master.";
       leaf master-ip-address {
         type inet:ip-address;
         mandatory true;
         description
           "Адрес IPv4 IPv6 нового ведущего (master).";
       }
       leaf new-master-reason {
         type new-master-reason-type;
         mandatory true;
         description
           "Причина перехода в состояние master.";
       }
     }

     notification vrrp-protocol-error-event {
       description
         "Уведомление для ошибки протокола VRRP.";
       leaf protocol-error-reason {
         type identityref {
           base vrrp:vrrp-error-global;
         }
         mandatory true;
         description
           "Причина протокольной ошибки.";
       }
     }

     notification vrrp-virtual-router-error-event {
       description
         "Уведомление для ошибки в виртуальном маршрутизаторе.";
       leaf interface {
         type if:interface-ref;
         mandatory true;
         description
           "Интерфейс, на котором произошло событие.";
       }

       choice ip-version {
         mandatory true;
         description
           "Ошибка может возникнуть на виртуальном маршрутизаторе IPv4
            или IPv6. Сведения о версии IP предоставляют указанные ниже
            варианты.";
         case ipv4 {
           description
             "IPv4.";
           container ipv4 {
             description
               "Сведения об ошибке для IPv4.";
             leaf vrid {
               type leafref {
                 path "/if:interfaces/if:interface"
                   + "[if:name = current()/../../vrrp:interface]/"
                   + "ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:vrid";
               }
               mandatory true;
               description
                 "Виртуальный маршрутизатор, где произошло событие.";
             }
           }
         }
         case ipv6 {
           description
             "IPv6.";
           container ipv6 {
             description
               "Сведения об ошибке для IPv6.";
             leaf vrid {
               type leafref {
                 path "/if:interfaces/if:interface"
                   + "[if:name = current()/../../vrrp:interface]/"
                   + "ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:vrid";
               }
               mandatory true;
               description
                 "Виртуальный маршрутизатор, где произошло событие.";
             }
           }
         }
       }

       leaf virtual-router-error-reason {
         type identityref {
           base vrrp:vrrp-error-virtual-router;
         }
         mandatory true;
         description
           "Причина ошибки вируального маршрутизатора.";
       }
     }
   }
   <CODE ENDS>

5. Взаимодействие с IANA

Этот документ регистрирует URI пространства имён в реестре IETF XML Registry [RFC3688].

   URI: urn:ietf:params:xml:ns:yang:ietf-vrrp
   Registrant Contact: The IESG.
   XML: N/A; регистрируемый URI является пространством имён XML.

Документ регистрирует модуль YANG в реестре YANG Module Names [RFC7950].

   name:         ietf-vrrp
   namespace:    urn:ietf:params:xml:ns:yang:ietf-vrrp
   prefix:       vrrp
   reference:    RFC 8347

6. Вопросы безопасности

Заданные в этом документе модули YANG определяют схемы для данных, которые разработаны для доступа через протоколы управления сетью, такие как NETCONF [RFC6241] и RESTCONF [RFC8040]. Нижним уровнем NETCONF является защищённый транспорт с обязательной реализацией Secure Shell (SSH) [RFC6242]. Нижним уровнем RESTCONF служит HTTPS с обязательной реализацией защищённого транспорта TLS [RFC5246].

Модель управления доступом NETCONF [RFC8341] обеспечивает средства, позволяющие предоставить доступ лишь конкретным пользователям NETCONF и RESTCONF к предопределённому подмножеству доступных в NETCONF или RESTCONF протокольных операций и содержимого.

В этом модуле YANG имеется множество узлов данных, доступных для записи, создания, удаления (т. е. с принятым по умолчанию config true). Эти узлы могут быть чувствительными или уязвимыми в некоторых сетевых средах. Операции записи (например, edit-config) в такие узлы без подобающей защиты могут оказывать негативное влияние на работу сети. Ниже указаны ветви и узлы данных и их уязвимости.

/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance

/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance

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

Некоторые из доступных для чтения узлов этого модуля YANG могут быть конфиденциальными или уязвимыми в некоторых сетевых средах. Важно контролировать доступ к считыванию таких узлов данных (например, через операции get, get-config, notification). Ниже указаны ветви и узлы данных конфиденциальные или уязвимые в плане их чтения.

/ietf-vrrp:vrrp

/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance

/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance

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

7. Литература

7.1. Нормативные документы

[RFC2119] Bradner, S., «Key words for use in RFCs to Indicate Requirement Levels», BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.

[RFC2787] Jewell, B. and D. Chuang, «Definitions of Managed Objects for the Virtual Router Redundancy Protocol», RFC 2787, DOI 10.17487/RFC2787, March 2000, <https://www.rfc-editor.org/info/rfc2787>.

[RFC3688] Mealling, M., «The IETF XML Registry», BCP 81, RFC 3688, DOI 10.17487/RFC3688, January 2004, <https://www.rfc-editor.org/info/rfc3688>.

[RFC5246] Dierks, T. and E. Rescorla, «The Transport Layer Security (TLS) Protocol Version 1.2», RFC 5246, DOI 10.17487/RFC5246, August 2008, <https://www.rfc-editor.org/info/rfc5246>.

[RFC5798] Nadas, S., Ed., «Virtual Router Redundancy Protocol (VRRP) Version 3 for IPv4 and IPv6», RFC 5798, DOI 10.17487/RFC5798, March 2010, <https://www.rfc-editor.org/info/rfc5798>.

[RFC6020] Bjorklund, M., Ed., «YANG — A Data Modeling Language for the Network Configuration Protocol (NETCONF)», RFC 6020, DOI 10.17487/RFC6020, October 2010, <https://www.rfc-editor.org/info/rfc6020>.

[RFC6241] Enns, R., Ed., Bjorklund, M., Ed., Schoenwaelder, J., Ed., and A. Bierman, Ed., «Network Configuration Protocol (NETCONF)», RFC 6241, DOI 10.17487/RFC6241, June 2011, <https://www.rfc-editor.org/info/rfc6241>.

[RFC6242] Wasserman, M., «Using the NETCONF Protocol over Secure Shell (SSH)», RFC 6242, DOI 10.17487/RFC6242, June 2011, <https://www.rfc-editor.org/info/rfc6242>.

[RFC6527] Tata, K., «Definitions of Managed Objects for Virtual Router Redundancy Protocol Version 3 (VRRPv3)», RFC 6527, DOI 10.17487/RFC6527, March 2012, <https://www.rfc-editor.org/info/rfc6527>.

[RFC6991] Schoenwaelder, J., Ed., «Common YANG Data Types», RFC 6991, DOI 10.17487/RFC6991, July 2013, <https://www.rfc-editor.org/info/rfc6991>.

[RFC7950] Bjorklund, M., Ed., «The YANG 1.1 Data Modeling Language», RFC 7950, DOI 10.17487/RFC7950, August 2016, <https://www.rfc-editor.org/info/rfc7950>.

[RFC8040] Bierman, A., Bjorklund, M., and K. Watsen, «RESTCONF Protocol», RFC 8040, DOI 10.17487/RFC8040, January 2017, <https://www.rfc-editor.org/info/rfc8040>.

[RFC8174] Leiba, B., «Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words», BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.

[RFC8341] Bierman, A. and M. Bjorklund, «Network Configuration Access Control Model», STD 91, RFC 8341, DOI 10.17487/RFC8341, March 2018, <https://www.rfc-editor.org/info/rfc8341>.

[RFC8342] Bjorklund, M., Schoenwaelder, J., Shafer, P., Watsen, K., and R. Wilton, «Network Management Datastore Architecture (NMDA)», RFC 8342, DOI 10.17487/RFC8342, March 2018, <https://www.rfc-editor.org/info/rfc8342>.

[RFC8343] Bjorklund, M., «A YANG Data Model for Interface Management», RFC 8343, DOI 10.17487/RFC8343, March 2018, <https://www.rfc-editor.org/info/rfc8343>.

[RFC8344] Bjorklund, M., «A YANG Data Model for IP Management», RFC 8344, DOI 10.17487/RFC8344, March 2018, <https://www.rfc-editor.org/info/rfc8344>.

7.2. Дополнительная литература

[RFC3768] Hinden, R., Ed., «Virtual Router Redundancy Protocol (VRRP)», RFC 3768, DOI 10.17487/RFC3768, April 2004, <https://www.rfc-editor.org/info/rfc3768>.

[RFC7224] Bjorklund, M., «IANA Interface Type YANG Module», RFC 7224, DOI 10.17487/RFC7224, May 2014, <https://www.rfc-editor.org/info/rfc7224>.

[RFC7951] Lhotka, L., «JSON Encoding of Data Modeled with YANG», RFC 7951, DOI 10.17487/RFC7951, August 2016, <https://www.rfc-editor.org/info/rfc7951>.

[RFC8340] Bjorklund, M. and L. Berger, Ed., «YANG Tree Diagrams», BCP 215, RFC 8340, DOI 10.17487/RFC8340, March 2018, <https://www.rfc-editor.org/info/rfc8340>.

[Subscribed-Notifications] Voit, E., Clemm, A., Gonzalez Prieto, A., Nilsen-Nygaard, E., and A. Tripathy, «Custom Subscription to Event Streams», Work in Progress, draft-ietf-netconf-subscribed-notifications-103, February 2018.

[YANG-Push] Clemm, A., Voit, E., Gonzalez Prieto, A., Tripathy, A., Nilsen-Nygaard, E., Bierman, A., and B. Lengyel, «YANG Datastore Subscription», Work in Progress, draft-ietf-netconf-yang-push-154, February 2018.

[YANG-Guidelines] Bierman, A., «Guidelines for Authors and Reviewers of YANG Data Model Documents», Work in Progress, draft-ietf-netmod-rfc6087bis-205, March 2018.

Приложение A. Пример дерева данных

В этом приложении представлен пример дерева данных экземпляра в кодировке JSON [RFC7951] для конфигурации и состояния (пример включает iana-if-type из [RFC7224]).

               IP-адрес виртуального маршрутизатора fe80::1
              +-----------------+        +-----------------+
              |                 |        |                 |
              | Маршрутизатор 1 |        | Маршрутизатор 2 |
              |                 |        |                 |
              +--------+--------+        +--------+--------+
                       |eth1                      |eth1
                       |fe80::11                  |fe80::12
                -------+--------------------------+-------
                       |                          |
                       |fe80::51                  |fe80::52
              +--------+--------+        +--------+--------+
              |     Хост 1      |        |     Хост 2      |
              |Шлюз по умолчанию|        |Шлюз по умолчанию|
              |     fe80::1     |        |     fe80::1     |
              +-----------------+        +-----------------+

Данные экземпляра конфигурации Маршрутизатора 1 с приведённого выше рисунка показаны ниже.

   {
     "ietf-interfaces:interfaces": {
       "interface": [
         {
           "name": "eth1",
           "description": "An interface with VRRP enabled.",
           "type": "iana-if-type:ethernetCsmacd",
           "ietf-ip:ipv6": {
             "address": [
               {
                 "ip": "2001:db8:0:1::1",
                 "prefix-length": 64
               },
               {
                 "ip": "fe80::11",
                 "prefix-length": 64
               }
             ],
             "forwarding": true,
             "ietf-vrrp:vrrp": {
               "vrrp-instance": [
                 {
                   "vrid": 1,
                   "version": "vrrp-v3",
                   "priority": 200,
                   "advertise-interval-centi-sec": 50,
                   "virtual-ipv6-addresses": {
                     "virtual-ipv6-address": [
                       "ipv6-address": "fe80::1"
                     ]
                   }
                 }
               ]
             }
           }
         }
       ]
     }
   }

Соответствующие данные рабочего состояния Маршрутизатора 1 показаны ниже.

   {
     "ietf-interfaces:interfaces": {
       "interface": [
         {
           "name": "eth1",
           "description": "An interface with VRRP enabled.",
           "type": "iana-if-type:ethernetCsmacd",
           "phys-address": "00:00:5e:00:53:01",
           "oper-status": "up",
           "statistics": {
             "discontinuity-time": "2016-10-24T17:11:27+02:00"
           },
           "ietf-ip:ipv6": {
             "forwarding": true,
             "mtu": 1500,
             "address": [
               {
                 "ip": "2001:db8:0:1::1",
                 "prefix-length": 64,
                 "origin": "static",
                 "status": "preferred"
               },
               {
                 "ip": "fe80::11",
                 "prefix-length": 64,
                 "origin": "static",
                 "status": "preferred"
               }
             ]

             "ietf-vrrp:vrrp": {
               "vrrp-instance": [
                 {
                   "vrid": 1,
                   "version": "vrrp-v3",
                   "log-state-change": false,
                   "preempt": {
                     "enabled": true,
                     "hold-time": 0
                   }
                   "priority": 200,
                   "accept-mode": false,
                   "advertise-interval-centi-sec": 50,
                   "virtual-ipv6-addresses": {
                     "virtual-ipv6-address": [
                       "ipv6-address": "fe80::1"
                     ]
                   },
                   "state": "master",
                   "is-owner": false,
                   "last-adv-source": "fe80::11",
                   "up-datetime": "2016-10-24T17:11:27+02:00",
                   "master-down-interval": 161,
                   "skew-time": 11,
                   "last-event": "vrrp-event-interface-up",
                   "new-master-reason": "priority",
                   "statistics": {
                     "discontinuity-datetime":
                       "2016-10-24T17:11:27+02:00",
                     "master-transitions": 2,
                     "advertisement-rcvd": 20,
                     "advertisement-sent": 12,
                     "interval-errors": 0,
                     "priority-zero-pkts-rcvd": 0,
                     "priority-zero-pkts-sent": 0,
                     "invalid-type-pkts-rcvd": 0,
                     "address-list-errors": 0,
                     "packet-length-errors": 1
                   }
                 }
               ]
             }
           }
         }
       ]
     }
   }
   {
     "ietf-vrrp:vrrp": {
       "virtual-routers": 3,
       "interfaces": 2,
       "statistics": {
         "discontinuity-datetime": "2016-10-24T17:11:27+02:00",
         "checksum-errors": 2,
         "version-errors": 0,
         "vrid-errors": 0,
         "ip-ttl-errors": 1
       }
     }
   }

Адреса авторов

Xufeng Liu (editor)
Jabil
8281 Greensboro Drive, Suite 200
McLean, VA 22102
United States of America
Email: xufeng.liu.ietf@gmail.com
 
Athanasios Kyparlis
Jabil
8281 Greensboro Drive, Suite 200
McLean, VA 22102
United States of America
Email: Athanasios_Kyparlis@jabil.com
 
Ravi Parikh
VMware
3425 Hillview Avenue
Palo Alto, CA 94304
United States of America
Email: parikhr@vmware.com
 
Acee Lindem
Cisco Systems
301 Midenhall Way
Cary, NC 27513
United States of America
Email: acee@cisco.com
 
Mingui Zhang
Huawei Technologies
No. 156 Beiqing Rd. Haidian District
Beijing 100095
China
Email: zhangmingui@huawei.com

Перевод на русский язык

Николай Малых

nmalykh@protokols.ru

1Internet Engineering Task Force — комиссия по решению инженерных задач Internet.

2Internet Engineering Steering Group — комиссия по инженерным разработкам Internet.

3Опубликовано в RFC 8639. Прим. перев.

4Опубликовано в RFC 8641. Прим. перев.

5Опубликовано в RFC 8407. Прим. перев.

Рубрика: RFC | Оставить комментарий

RFC 8348 A YANG Data Model for Hardware Management

Internet Engineering Task Force (IETF)                        A. Bierman
Request for Comments: 8348                                     YumaWorks
Category: Standards Track                                   M. Bjorklund
ISSN: 2070-1721                                           Tail-f Systems
                                                                 J. Dong
                                                     Huawei Technologies
                                                            D. Romascanu
                                                              March 2018

A YANG Data Model for Hardware Management

Модель данных YANG для управления оборудованием

PDF

Аннотация

Этот документ определяет модель данных YANG для управления оборудованием на одном сервере.

Статус документа

Документ относится к категории Internet Standards Track.

Документ является результатом работы IETF1 и представляет согласованный взгляд сообщества IETF. Документ прошёл открытое обсуждение и был одобрен для публикации IESG2. Дополнительную информацию о стандартах Internet можно найти в разделе 2 RFC 7841.

Информацию о текущем статусе документа, ошибках и способах обратной связи можно найти по ссылке https://www.rfc-editor.org/info/rfc8348.

Авторские права

Copyright (c) 2018. Авторские права принадлежат IETF Trust и лицам, указанным в качестве авторов документа. Все права защищены.

К документу применимы права и ограничения, указанные в BCP 78 и IETF Trust Legal Provisions и относящиеся к документам IETF (http://trustee.ietf.org/license-info), на момент публикации данного документа. Прочтите упомянутые документы внимательно. Фрагменты программного кода, включённые в этот документ, распространяются в соответствии с упрощённой лицензией BSD, как указано в параграфе 4.e документа IETF Trust Legal Provisions, без каких-либо гарантий (как указано в Simplified BSD License).

1. Введение

Этот документ определяет модель данных YANG [RFC7950] для управления оборудованием на одном сервере.

Модель данных включает конфигурацию и состояние системы (сведения о статусе и счётчики статистики).

Модель YANG в этом документе соответствует архитектуре хранилищ сетевой информации NMDA, определённой в [RFC8342]. Для реализаций, ещё не поддерживающих NMDA, в Приложении A задан временный модуль, включающий лишь данные состояния системы.

1.1. Терминология

Ключевые слова необходимо (MUST), недопустимо (MUST NOT), требуется (REQUIRED), нужно (SHALL), не следует (SHALL NOT), следует (SHOULD), не нужно (SHOULD NOT), рекомендуется (RECOMMENDED), не рекомендуется (NOT RECOMMENDED), возможно (MAY), необязательно (OPTIONAL) в данном документе интерпретируются в соответствии с BCP 14 [RFC2119] [RFC8174] тогда и только тогда, когда они выделены шрифтом, как показано здесь.

Ниже указаны термины, определённые в [RFC8342] и не переопределяемые здесь

  • client — клиент;

  • server — сервер;

  • configuration — конфигурация;

  • system state — состояние системы;

  • operational state — рабочее состояние;

  • intended configuration — предполагаемая (предусмотренная) конфигурация.

1.2. Диаграммы деревьев

В документе применяется графическое представление моделей данных, описанное в [RFC8340].

2. Цели

В этом разделе указаны некоторые цели проектирования модели данных для оборудования.

  • Модель должна поддерживать многие общие свойства, служащие для идентификации аппаратных компонентов.

  • Важные сведения и состояния для компонентов оборудования требуется собирать с устройств, поддерживающих модель данных оборудования.

  • Модель данных следует подходить в неизменном виде для новых реализаций.

  • Заданную в этом документе модель данных можно реализовать на системах, которые реализуют ENTITY-MIB, поэтому нужно чёткое сопоставления между моделью данных оборудования и ENTITY-MIB.

  • Модели данных следует поддерживать предварительную подготоку аппаратных компонентов.

3. Модель данных оборудования

Этот документ определяет модуль YANG ietf-hardware, структура которого показана ниже.

   module: ietf-hardware
     +--rw hardware
        +--ro last-change?   yang:date-and-time
        +--rw component* [name]
           +--rw name              string
           +--rw class             identityref
           +--ro physical-index?   int32 {entity-mib}?
           +--ro description?      string
           +--rw parent?           -> ../../component/name
           +--rw parent-rel-pos?   int32
           +--ro contains-child*   -> ../../component/name
           +--ro hardware-rev?     string
           +--ro firmware-rev?     string
           +--ro software-rev?     string
           +--ro serial-num?       string
           +--ro mfg-name?         string
           +--ro model-name?       string
           +--rw alias?            string
           +--rw asset-id?         string
           +--ro is-fru?           boolean
           +--ro mfg-date?         yang:date-and-time
           +--rw uri*              inet:uri
           +--ro uuid?             yang:uuid
           +--rw state {hardware-state}?
           |  +--ro state-last-changed?   yang:date-and-time
           |  +--rw admin-state?          admin-state
           |  +--ro oper-state?           oper-state
           |  +--ro usage-state?          usage-state
           |  +--ro alarm-state?          alarm-state
           |  +--ro standby-state?        standby-state
           +--ro sensor-data {hardware-sensor}?
              +--ro value?               sensor-value
              +--ro value-type?          sensor-value-type
              +--ro value-scale?         sensor-value-scale
              +--ro value-precision?     sensor-value-precision
              +--ro oper-status?         sensor-status
              +--ro units-display?       string
              +--ro value-timestamp?     yang:date-and-time
              +--ro value-update-rate?   uint32

     notifications:
       +---n hardware-state-change
       +---n hardware-state-oper-enabled {hardware-state}?
       |  +--ro name?          -> /hardware/component/name
       |  +--ro admin-state?   -> /hardware/component/state/admin-state
       |  +--ro alarm-state?   -> /hardware/component/state/alarm-state
       +---n hardware-state-oper-disabled {hardware-state}?
          +--ro name?          -> /hardware/component/name
          +--ro admin-state?   -> /hardware/component/state/admin-state
          +--ro alarm-state?   -> /hardware/component/state/alarm-state

3.1. Списки компонентов

Представленная в документе модель данных для оборудования использует плоский список компонентов, каждый из которых указывается именем и имеет обязательный лист class.

Модуль iana-hardware определяет идентификаторы YANG для типов оборудования из поддерживаемого IANA реестра IANA-ENTITY-MIB.

Лист class служит идентификатором YANG, описывающим тип оборудования. Производителям рекомендуется напрямую использовать заданные IANA идентификаторы или выводить из нихе более конкретные обозначения.

4. Связь с ENTITY-MIB

Если устройство реализует ENTITY-MIB [RFC6933], каждая запись в списке /hardware/component рабочего состояния отображается на EntPhysicalEntry. Объекты, доступные для записи в MIB отображаются в узлы config true в списке /hardware/component, за исключением entPhysicalSerialNum (открыт для записи в MIB) имеющего config false в модуле YANG.

Лист physical-index должен содержать значение, соответствующее entPhysicalIndex в entPhysicalEntry.

Лист class сопоставляется с entPhysicalClass и entPhysicalVendorType. Если значением листа class является идентификатор, выведенный из одного из идентификаторов в iana-hardware, либо просто идентификатор из iana-hardware, то entPhysicalClass содержит соответствующее перечисляемое значение IANAPhysicalClass. В иных случаях entPhysicalClass содержит IANAPhysicalClass other(1). Производителям рекомендуется задавать идентификатор (выведенный из идентификатора iana-hardware, если это возможно) для каждого своего (enterprise-specific) идентификатора регистрации, применяемого в entPhysicalVendorType, и использовать этот идентификатор для листа class.

В таблице 1 указаны узлы данных YANG и соответствующие объекты ENTITY-MIB.

Таблица 1. Узлы модели данных YANG и связанные объекты ENTITY-MIB.

 

Узел данных YANG в /hardware/component

Объект ENTITY-MIB

name

entPhysicalName

class

entPhysicalClass
entPhysicalVendorType

physical-index

entPhysicalIndex

description

entPhysicalDescr

parent

entPhysicalContainedIn

parent-rel-pos

entPhysicalParentRelPos

contains-child

entPhysicalChildIndex

hardware-rev

entPhysicalHardwareRev

firmware-rev

entPhysicalFirmwareRev

software-rev

entPhysicalSoftwareRev

serial-num

entPhysicalSerialNum

mfg-name

entPhysicalMfgName

model-name

entPhysicalModelName

alias

entPhysicalAlias

asset-id

entPhysicalAssetID

is-fru

entPhysicalIsFRU

mfg-date

entPhysicalMfgDate

uri

entPhysicalUris

uuid

entPhysicalUUID

 

5. Связь с ENTITY-SENSOR-MIB

Если устройство реализует ENTITY-SENSOR-MIB [RFC3433], каждая записи в списке /hardware/component, имеющая контейнер sensor-data, отображается на EntPhySensorEntry.

В таблице 2 указаны узлы данных YANG и соответствующие объекты ENTITY-SENSOR-MIB.

Таблица 2. Узлы модели данных YANG и связанные объекты ENTITY-SENSOR-MIB.

 

Узел данных в YANG in/hardware/component/sensor-data

Объект ENTITY-SENSOR-MIB

value

entPhySensorValue

value-type

entPhySensorType

value-scale

entPhySensorScale

value-precision

entPhySensorPrecision

oper-status

entPhySensorOperStatus

units-display

entPhySensorUnitsDisplay

value-timestamp

entPhySensorValueTimeStamp

value-update-rate

entPhySensorValueUpdateRate

 

6. Связь с ENTITY-STATE-MIB

Если устройство реализует ENTITY-STATE-MIB [RFC4268], каждая запись в списке /hardware/component, имеющая контейнер state, отображается на EntStateEntry.

В таблице 3 указаны узлы данных YANG и соответствующие объекты ENTITY-STATE-MIB.

Таблица 3. Узлы модели данных YANG и связанные объекты ENTITY-STATE-MIB.

 

Узел данных в YANG /hardware/component/state

Объект ENTITY-STATE-MIB

state-last-changed

entStateLastChanged

admin-state

entStateAdmin

oper-state

entStateOper

usage-state

entStateUsage

alarm-state

entStateAlarm

standby-state

entStateStandby

 

7. Модули YANG для оборудования

7.1. Модуль ietf-hardware

Этот модуль YANG импоритрует определения типов (typedef) из [RFC6991].

   <CODE BEGINS> file "ietf-hardware@2018-03-13.yang"

  module ietf-hardware {
    yang-version 1.1;
    namespace "urn:ietf:params:xml:ns:yang:ietf-hardware";
    prefix hw;

    import ietf-inet-types {
      prefix inet;
    }
    import ietf-yang-types {
      prefix yang;
    }
    import iana-hardware {
      prefix ianahw;
    }

    organization
      "IETF NETMOD (Network Modeling) Working Group";

    contact
      "WG Web:   <https://datatracker.ietf.org/wg/netmod/> 
       WG List:  <mailto:netmod@ietf.org> 

       Editor:   Andy Bierman
                 <mailto:andy@yumaworks.com> 

       Editor:   Martin Bjorklund
                 <mailto:mbj@tail-f.com> 

       Editor:   Jie Dong
                 <mailto:jie.dong@huawei.com> 

       Editor:   Dan Romascanu
                 <mailto:dromasca@gmail.com>"; 

    description
      "Модуль содержит определения YANG для управления оборудованием.
       
       Модель данных разработана для архитектуры NMDA, 
       определённой в 8342.

       Авторские права (Copyright (c) 2018) принадлежат IETF Trust
       и лицам, указанным в качестве авторов кода. Все права защищены.

       Распространение и использование в исходной или двоичной форме с
       изменениями или без таковых разрешено в соответствии с лицензией
       Simplified BSD, изложенной в разделе 4  IETF Trust's Legal
       Provisions применительно к документам IETF
       (http://trustee.ietf.org/license-info). 
 
       Эта версия данного модуля YANG является частью RFC 8348, где
       правовые вопросы рассмотрены более полно.";

    revision 2018-03-13 {
      description
        "Исходный выпуск.";
      reference
        "RFC 8348: A YANG Data Model for Hardware Management";
    }

    /*
     * Свойства (функции)
     */

    feature entity-mib {
      description
        "Указывает, что устройство реализует ENTITY-MIB.";
      reference
        "RFC 6933: Entity MIB (Version 4)";
    }

    feature hardware-state {
      description
        "Указывает поддержку объектов ENTITY-STATE-MIB.";
      reference
        "RFC 4268: Entity State MIB";
    }

    feature hardware-sensor {
      description
        "Указывает поддержку объектов ENTITY-SENSOR-MIB.";
      reference
        "RFC 3433: Entity Sensor Management Information Base";
    }

    /*
     * Определения типов
     */

    typedef admin-state {
      type enumeration {
        enum unknown {
          value 1;
          description
            "Ресурс не способен сообщить административный статус.";
        }
        enum locked {
          value 2;
          description
            "Использование устройства запрещено административно.";
        }
        enum shutting-down {
          value 3;
          description
            "Использование ресурсов ограничено текущими экземплярами.";
        }
        enum unlocked {
          value 4;
          description
            "Использование ресурса запрещено не административно.";
        }
      }
      description
        "Представляет возможные административные состояния.";
      reference
        "RFC 4268: Entity State MIB - EntityAdminState";
    }

    typedef oper-state {
      type enumeration {
        enum unknown {
          value 1;
          description
            "Ресурс не способен сообщить рабочее состояние.";
        }
        enum disabled {
          value 2;
          description
            "Ресурс неработоспособен.";
        }
        enum enabled {
          value 3;
          description
            "Ресурс частично или полностью работоспособен.";
        }
        enum testing {
          value 4;
          description
            "Ресурс в настоящее время тестируется, поэтому не
             способен сообщить своё рабочее состояние.";
        }
      }
      description
        "Возможные значения рабочего состояния.";
      reference
        "RFC 4268: Entity State MIB - EntityOperState";
    }

    typedef usage-state {
      type enumeration {
        enum unknown {
          value 1;
          description
            "Ресурс не способен сообщить состояние использования.";
        }
        enum idle {
          value 2;
          description
            "ресурс не обслуживает пользователей.";
        }
        enum active {
          value 3;
          description
            "Ресурс используется и имеет достаточно возможностей для
             предоставления другим пользователям.";
        }
        enum busy {
          value 4;
          description
            "Ресурс используется, но не имеет возможностей для
             предоставления другим пользователям.";
        }
      }
      description
        "Возможные значения состояния использования.";
      reference
        "RFC 4268: Entity State MIB -  EntityUsageState";
    }

    typedef alarm-state {
      type bits {
        bit unknown {
          position 0;
          description
            "Ресурс не способен сообщить статус состояния тревоги.";
        }
        bit under-repair {
          position 1;
          description
            "Ресурс ремонтируется и этот бит (в зависимости от
             реализации) может лишать смысла другие биты строки.";
        }
        bit critical {
          position 2;
          description
            "Для ресурса действует хотя бы один критический 
             сигнал тревоги.";
        }
        bit major {
          position 3;
          description
            "Для ресурса действует хотя бы один важный (major) 
             сигнал тревоги.";
        }
        bit minor {
          position 4;
          description
            "Для ресурса действует хотя бы один маловажный (minor) 
             сигнал тревоги.";
        }
        bit warning {
          position 5;
          description
            "Для ресурса действует хотя бы одно предупреждение.";
        }
        bit indeterminate {
          position 6;
          description
            "Для ресурса действует хотя бы один сигнал тревоги,
             важность которого невозможно определить.";
        }
      }
      description
        "Возможные значения сигналов тревоги. Сигналом служит
         сохраняющаяся индикация ошибки или предупреждения.

         Когда в этом атрибуте не установлен ни один бит, активных
         сигналов тревоги нет, и ресурс не находится в ремонте.";
      reference
        "RFC 4268: Entity State MIB - EntityAlarmStatus";
    }

    typedef standby-state {
      type enumeration {
        enum unknown {
          value 1;
          description
            "Ресурс не способен сообщить статус ожидания (standby).";
        }
        enum hot-standby {
          value 2;
          description
            "Ресурс не предоставляет услуг, но сразу может принять роль
             резервного ресурса без необходимости инициализации и будет
             содержать ту же информацию, которая была в резервируемом.";
        }
        enum cold-standby {
          value 3;
          description
            "Ресурс является резервным для другого ресурса, но не сможет 
             сразу принять роль резервируемого и требует инициализации.";
        }
        enum providing-service {
          value 4;
          description
            "Ресурс предоставляет услуги.";
        }
      }
      description
        "Возможные значения статуса ожидания (standby).";
      reference
        "RFC 4268: Entity State MIB - EntityStandbyStatus";
    }

    typedef sensor-value-type {
      type enumeration {
        enum other {
          value 1;
          description
            "Измерения (меры), отличные от указанных ниже.";
        }
        enum unknown {
          value 2;
          description
            "Неизвестная мера или произвольные относительные числа";
        }
        enum volts-AC {
          value 3;
          description
            "Мера электрического потенциала (переменный ток).";
        }
        enum volts-DC {
          value 4;
          description
            "Мера электрического потенциала (постоянный ток).";
        }
        enum amperes {
          value 5;
          description
            "Мера электрического тока.";
        }
        enum watts {
          value 6;
          description
            "Мера мощности.";
        }
        enum hertz {
          value 7;
          description
            "Мера частоты.";
        }
        enum celsius {
          value 8;
          description
            "Мера температуры.";
        }
        enum percent-RH {
          value 9;
          description
            "Мера относительной влажности (%).";
        }
        enum rpm {
          value 10;
          description
            "Число оборотов в минуту.";
        }
        enum cmm {
          value 11;
          description
            "Число кубометров в минуту (воздушный поток).";
        }
        enum truth-value {
          value 12;
          description
            "Логическое значение - 1 (true) или 2 (false)";
        }
      }
      description
        "Узел с этим типом данных представляет физическое значение датчика.
         Фактическая величина определяется этим узлом и связанным узлом
         коэффициента измерения sensor-value-scale.

         Узлы этого типа СЛЕДУЕТ определять вместе с узлами типа
         sensor-value-scale и sensor-value-precision. Эти три типа служат
         для указания семантики узла типа sensor-value.";
      reference
        "RFC 3433: Entity Sensor Management Information Base -
                   EntitySensorDataType";
    }

    typedef sensor-value-scale {
      type enumeration {
        enum yocto {
          value 1;
          description
            "Коэффициент измерения 10^-24.";
        }
        enum zepto {
          value 2;
          description
            "Коэффициент измерения 10^-21.";
        }
        enum atto {
          value 3;
          description
            "Коэффициент измерения 10^-18.";
        }
        enum femto {
          value 4;
          description
            "Коэффициент измерения 10^-15.";
        }
        enum pico {
          value 5;
          description
            "Коэффициент измерения 10^-12.";
        }
        enum nano {
          value 6;
          description
            "Коэффициент измерения 10^-9.";
        }
        enum micro {
          value 7;
          description
            "Коэффициент измерения 10^-6.";
        }
        enum milli {
          value 8;
          description
            "Коэффициент измерения 10^-3.";
        }
        enum units {
          value 9;
          description
            "Коэффициент измерения 10^0.";
        }
        enum kilo {
          value 10;
          description
            "Коэффициент измерения 10^3.";
        }
        enum mega {
          value 11;
          description
            "Коэффициент измерения 10^6.";
        }
        enum giga {
          value 12;
          description
            "Коэффициент измерения 10^9.";
        }
        enum tera {
          value 13;
          description
            "Коэффициент измерения 10^12.";
        }
        enum peta {
          value 14;
          description
            "Коэффициент измерения 10^15.";
        }
        enum exa {
          value 15;
          description
            "Коэффициент измерения 10^18.";
        }
        enum zetta {
          value 16;
          description
            "Коэффициент измерения 10^21.";
        }
        enum yotta {
          value 17;
          description
            "Коэффициент измерения 10^24.";
        }
      }
      description
        "Узел этого типа представляет коэффициент измерения, для
         единиц международной системы СИ. Фактическое значение
         определяется этим узлом и соответствующим sensor-value-type.

         Узлы этого типа СЛЕДУЕТ определять вместе с узлами типа
         sensor-value-type и sensor-value-precision. Эти три типа служат
         для указания семантики узла типа sensor-value.";
      reference
        "RFC 3433: Entity Sensor Management Information Base -
                   EntitySensorDataScale";
    }

    typedef sensor-value-precision {
      type int8 {
        range "-8 .. 9";
      }
      description
        "Узел этого типа представляет точность значения датчика.

         Узлы этого типа СЛЕДУЕТ определять вместе с узлами типа
         sensor-value-type и sensor-value-scale. Эти три типа служат
         для указания семантики узла типа sensor-value.";

         Значения от 1 до 9, указывающее число десятичных цифр дробной
         части, соответствующего значения sensor-value с фиксированной
         точкой (запятой). Значение от -8 до -1 указывают число значимых
         (точных) цифр  соответствующего значения sensor-value с
         фиксированной точкой. Значение 0 указывает, что соответствующее
         значение sensor-value не является числом с фиксированной точкой.

         Разработчики серверов должны выбирать значение для узла
         sensor-value-precision так, чтобы точность и значащие цифры
         связанного значения sensor-value указывались корректно. 
         Например, узел, представляющий датчик температуры, способный
         измерять значения от 0 до 100 C с шагом 0,1 C и точностью 
         +/- 0,05 C, будет иметь sensor-value-precision 1, 
         sensor-value-scale units и sensor-value от 0 до 1000. 
         Значение sensor-value будет интерпретироваться как C * 10.";
      reference
        "RFC 3433: Entity Sensor Management Information Base -
                   EntitySensorPrecision";
    }

    typedef sensor-value {
      type int32 {
        range "-1000000000 .. 1000000000";
      }
      description
       "Узел этого типа представляет значение датчика.

        Узлы этого типа СЛЕДУЕТ определять вместе с узлами типа
        sensor-value-type, sensor-value-scale и sensor-value-precision. 
        Эти три типа служат для указания семантики узла данного типа.";

        Если связанный узел sensor-value-type имеет значение voltsAC,
        voltsDC, amperes, watts, hertz, celsius или cmm, узел этого типа
        ДОЛЖЕН содержать значение с фиксированной точкой (запятой) из
        диапазона -999999999 - 999999999. Значение -1000000000 указывает
        исчерпание (underflow), 1000000000 — переполнение (overflow). 
        Значение sensor-value-precision указывает число цифр дробной 
        части, представленных в связанном узле sensor-value.

        Если связанный узел sensor-value-type имеет значение percentRH,
        этот узел ДОЛЖЕН содержать число от 0 до 100.

        Если связанный узел sensor-value-type имеет значение rpm,
        этот узел ДОЛЖЕН содержать число от -999999999 до +999999999.

        Если связанный узел sensor-value-type имеет значение 
        truth-value, этот узел ДОЛЖЕН содержать 1 (true) или 2 (false).

        Если связанный узел sensor-value-type имеет значение other или
        unknown,  этот узел ДОЛЖЕН содержать число от -1000000000 до
        1000000000.";
      reference
        "RFC 3433: Entity Sensor Management Information Base -
                   EntitySensorValue";
    }

    typedef sensor-status {
      type enumeration {
        enum ok {
          value 1;
          description
            "Указывает возможность получить значение датчика.";
        }
        enum unavailable {
          value 2;
          description
            "Указывает, что сервер сейчас не может получить значение 
             датчика.";
        }
        enum nonoperational {
          value 3;
          description
            "Указывает, что сервер считает датчик неисправным. Это может
             быть аппаратный отказ (обрыв провода) или мягкая причина
             вроде выхода за пределы диапазона, дрожание или скачки.";
        }
      }
      description
        "Узел этого типа представляет рабочее состояние физического
         датчика.";
      reference
        "RFC 3433: Entity Sensor Management Information Base -
                   EntitySensorStatus";
    }

    /*
     * Узлы данных
     */

    container hardware {
      description
        "Узлы, представляющие компоненты. Если сервер поддерживает 
         настройку аппаратных компонентов, эта модель данных создаётся 
         в хранилище конфигурации, поддерживаемом сервером. Эти 
         сведения обеспечивает leaf-list datastore для модуля
         ietf-hardware в библиотеке YANG.";

      leaf last-change {
        type yang:date-and-time;
        config false;
        description
          "Время изменения списка /hardware/component в рабочем 
           состоянии.";
      }

      list component {
        key name;
        description
          "Список компонентов. Когда сервер обнаруживает новый
           аппаратный компонент, он инициализирует запись списка в
           рабочем состоянии. Если сервер не поддерживает настройку
           аппаратных компонентов, записи списка в рабочем состоянии
           инициализируются значениями для всех узлов, найденных 
           реализацией. В остальных случаях выполняются указанные
           ниже процедуры.

             1. Если в списке /hardware/component предполагаемой 
                (intended) конфигурации есть запись со значениями узлов
                class, parent и parent-rel-pos, равными обнаруженным,
                запись списка в рабочем состоянии инициализируется 
                настроенными значениями, включая name.

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

           Если список /hardware/component в предполагаемой конфигурации
           изменён, система ДОЛЖНА вести себя как при реинициализации
           и следовать процедуре п. (1).";
        reference
          "RFC 6933: Entity MIB (Version 4) - entPhysicalEntry";

        leaf name {
          type string;
          description
            "Имя компонента (может отличаться от entPhysicalName).";
        }

        leaf class {
          type identityref {
            base ianahw:hardware-class;
          }
          mandatory true;
          description
            "Оборудование общего назначения.";
          reference
            "RFC 6933: Entity MIB (Version 4) - entPhysicalClass";
        }

        leaf physical-index {
          if-feature entity-mib;
          type int32 {
            range "1..2147483647";
          }
          config false;
          description
            "Представляет entPhysicalIndex для entPhysicalEntry.";
          reference
            "RFC 6933: Entity MIB (Version 4) - entPhysicalIndex";
        }

        leaf description {
          type string;
          config false;
          description
            "Текстовое описание компонента. Узлу следует содержать 
             строку, указывающую производителя компонента и следует
             указывать разные значения для каждой версии или модели.";
          reference
            "RFC 6933: Entity MIB (Version 4) - entPhysicalDescr";
        }

        leaf parent {
          type leafref {
            path "../../component/name";
            require-instance false;
          }
          description
            "Имя компонента, физически включающего этот компонент. Если
             этот лист не создаётся, это указывает, что компонент не 
             содержится в каком-либо ином компоненте.

             Если физический компонент входит в несколько физических
             компонентов (например, модуль двойной ширины), узел 
             содержит имя одного из этих компонентов. Реализация ДОЛЖНА
             использовать одно имя для каждого экземпляра узла.";
          reference
            "RFC 6933: Entity MIB (Version 4) - entPhysicalContainedIn";
        }

        leaf parent-rel-pos {
          type int32 {
            range "0 .. 2147483647";
          }
          description
            "Указывает относительное положение дочернего компонента
             среди братских, к каковым относятся компоненты, имеющие 
             одно значение узла parent и одно базовое отождествление
             для узла class. Отметим, что второе правило обеспечивает
             реализациям гибкость нумерации компонентов. Например,
             некоторые реализации могут иметь одну серию номеров для
             всех компонентов, производных от ianahw:port, а другие -
             применять для них разные серии номеров (скажем, одну для
             разъемов RJ45, другую для SFP).";

          reference
            "RFC 6933: Entity MIB (Version 4) -
                       entPhysicalParentRelPos";
        }

        leaf-list contains-child {
          type leafref {
            path "../../component/name";
          }
          config false;
          description
            "Имя содержащегося компонента.";
          reference
            "RFC 6933: Entity MIB (Version 4) - entPhysicalChildIndex";
        }

        leaf hardware-rev {
          type string;
          config false;
          description
            "Фирменная строка аппаратного выпуска компонента. 
             Предпочтительно указывать идентификатор, помещаемый 
             производителем на сам компонент (при наличии).";
          reference
            "RFC 6933: Entity MIB (Version 4) -
                       entPhysicalHardwareRev";
        }

        leaf firmware-rev {
          type string;
          config false;
          description
            "Фирменная строка выпуска микрокода для компонента.";
          reference
            "RFC 6933: Entity MIB (Version 4) -
                       entPhysicalFirmwareRev";
        }

        leaf software-rev {
          type string;
          config false;
          description
            "Фирменная строка программного выпуска компонента.";
          reference
            "RFC 6933: Entity MIB (Version 4) -
                       entPhysicalSoftwareRev";
        }

        leaf serial-num {
          type string;
          config false;
          description
            "Фирменная строка серийного номера компонента.
             Предпочтительно указывать номер, помещаемый 
             производителем на сам компонент (при наличии).";
          reference
            "RFC 6933: Entity MIB (Version 4) - entPhysicalSerialNum";
        }

        leaf mfg-name {
          type string;
          config false;
          description
            "Название изготовителя для физического компонента.
             Предпочтительно указывать название, помещаемое 
             изготовителем на сам компонент (при наличии).";

             Отметим, что сравнение значений model-name, firmware-rev,
             software-rev и serial-num имеет смысл лишь для 
             компонентов с одним значением mfg-name.

             Если заданное изготовителем название физического
             компонента неизвестно серверу, этот узел не создаётся.";
          reference
            "RFC 6933: Entity MIB (Version 4) - entPhysicalMfgName";
        }

        leaf model-name {
          type string;
          config false;
          description
            "Фирменная строка идентификатора модели физического
             компонента. Предпочтительно указывать видимый клиенту
             код, напечатанный на самом компоненте (при наличии).

             Если заданный производителем код модели физического
             компонента неизвестно серверу, этот узел не создаётся.";
          reference
            "RFC 6933: Entity MIB (Version 4) - entPhysicalModelName";
        }

        leaf alias {
          type string;
          description
            "Псевдоним имени компонента, заданный администратором сети,
             сохраняющийся при выключении или перезагрузке компонента.

             Если псевдоним не задан, сервер МОЖЕТ установить для узла
             локально уникальное значение в рабочем состоянии.

             Реализация сервера МОЖЕТ сопоставить этот лист с объектом
             MIB entPhysicalAlias. Такой реализации нужен механизм для
             обработки различий в размере и разрешённых символах между
             этим листом и entPhysicalAlias. Задание такого механизма
             выходит за рамки этого документа.";
          reference
            "RFC 6933: Entity MIB (Version 4) - entPhysicalAlias";
        }

        leaf asset-id {
          type string;
          description
            "Заданный пользователем идентификатор отслеживания активов
             для компонента.

             Реализация сервера МОЖЕТ сопоставить этот лист с объектом
             MIB entPhysicalAssetID. Такой реализации нужен механизм для
             обработки различий в размере и разрешённых символах между
             этим листом и entPhysicalAssetID. Задание такого механизма
             выходит за рамки этого документа.";
          reference
            "RFC 6933: Entity MIB (Version 4) - entPhysicalAssetID";
        }

        leaf is-fru {
          type boolean;
          config false;
          description
            "Указывает, считает ли производитель этот компонент
             заменяемым в полевых условиях. Значение true указывает
             возможность такой замены. Для всех компонентов, постоянно
             содержащихся в этом компоненте для этого узла следует
             возвращать значение false.";
          reference
            "RFC 6933: Entity MIB (Version 4) - entPhysicalIsFRU";
        }

        leaf mfg-date {
          type yang:date-and-time;
          config false;
          description
            "Дата изготовления компонента.";
          reference
            "RFC 6933: Entity MIB (Version 4) - entPhysicalMfgDate";
        }

        leaf-list uri {
          type inet:uri;
          description
            "Идентификационные сведения о компоненте.";
          reference
            "RFC 6933: Entity MIB (Version 4) - entPhysicalUris";
        }

        leaf uuid {
          type yang:uuid;
          config false;
          description
            "Глобально уникальный идентификатор (UUID) компонента.";
          reference
            "RFC 6933: Entity MIB (Version 4) - entPhysicalUUID";
        }

        container state {
          if-feature hardware-state;
          description
            "Относящиеся к состоянию узлы";
          reference
            "RFC 4268: Entity State MIB";

          leaf state-last-changed {
            type yang:date-and-time;
            config false;
            description
              "Дата и время смены любого из значений admin-state, 
               oper-state, usage-state, alarm-state или
               standby-state для этого компонента.

               Если с момента последней реинициализации системы не было
               изменений, узел содержит дату и время инициализации 
               локальной системы. Если не было изменений с момента
               установки компонента в локальную систему, указывается
               дата и время такой установки.";
            reference
              "RFC 4268: Entity State MIB - entStateLastChanged";
          }

          leaf admin-state {
            type admin-state;
            description
              "Административное состояние компонента. Указывает
               административное разрешение обслуживать другие компоненты
               в иерархии содержания, а также других пользователей этих
               услуг, заданных мерами, выходящими за рамки этого модуля.

               Некоторые компоненты раскрывают лишь часть остальных
               значений административного состояния. Некоторые 
               компоненты нельзя блокировать, поэтому узел показывает
               лишь статус unlocked. Другие компоненты нельзя аккуратно
               отключить и узел не показывает статус shutting-down.";
            reference
              "RFC 4268: Entity State MIB - entStateAdmin";
          }

          leaf oper-state {
            type oper-state;
            config false;
            description
              "Рабочее состояние компонента. Отметим, что этот узел не
               за административным статусом, т. е. административный
               статус down не означает рабочего статуса disabled.

               Отметим, что некоторые реализации могут не поддерживать
               точное отображение oper-state, когда admin-state для узла
               отличается от unlocked. В таких случаях узел ДОЛЖЕН иметь
               значение unknown.";
            reference
              "RFC 4268: Entity State MIB - entStateOper";
          }

          leaf usage-state {
            type usage-state;
            config false;
            description
              "Состояние использования компонента, отражающее его
               способность обслуживать дополнительные компоненты в
               иерархии содержания.

               Некоторые компоненты раскрывают лишь часть значений 
               статуса использования. Компоненты, не способные 
               обслуживать какие-либо компоненты в иерархии размещения,
               всегда будут иметь статус busy. В некоторых случаях 
               компонент может поддерживать лишь ещё один компонент в
               иерархии размещения и будет раскрывать лишь значение
               idle или busy.";
            reference
              "RFC 4268: Entity State MIB - entStateUsage";
          }

          leaf alarm-state {
            type alarm-state;
            config false;
            description
              "Состояние сигналов тревоги для компонента без учёта 
               сигналов в дочерних компонентах иерархии размещения.";
            reference
              "RFC 4268: Entity State MIB - entStateAlarm";
          }

          leaf standby-state {
            type standby-state;
            config false;
            description
              "Состояние ожидания (standby) для компонента.

               Некоторые компоненты раскрывают лишь часть значений 
               статуса ожидания. Компоненты, не способные работать
               в режиме ожидания будут иметь для этого узла значение
               providing-service.";
            reference
              "RFC 4268: Entity State MIB - entStateStandby";
          }
        }

        container sensor-data {
          when 'derived-from-or-self(../class,
                                     "ianahw:sensor")' {
            description
              "Узлы данных для компонентов типа sensor";
          }
          if-feature hardware-sensor;
          config false;

          description
            "Относящиеся к датчикам узлы.";
          reference
            "RFC 3433: Entity Sensor Management Information Base";

          leaf value {
            type sensor-value;
            description
              "Последнее измерение, полученное сервером от датчика.

               Клиенту, периодически просматривающему этот узел, 
               следует извлекать и узлы value-type, value-scale и
               value-precision, поскольку они тоже могут изменяться.";
            reference
              "RFC 3433: Entity Sensor Management Information Base -
                         entPhySensorValue";
          }

          leaf value-type {
            type sensor-value-type;
            description
              "Тип единиц, связанных со значением датчика";
            reference
              "RFC 3433: Entity Sensor Management Information Base -
                         entPhySensorType";
          }

          leaf value-scale {
            type sensor-value-scale;
            description
              "Коэффициент (степень 10) измерения для значения узла";
            reference
              "RFC 3433: Entity Sensor Management Information Base -
                         entPhySensorScale";
          }

          leaf value-precision {
            type sensor-value-precision;
            description
              "Число десятичных знаков точности значения узла.";
            reference
              "RFC 3433: Entity Sensor Management Information Base -
                         entPhySensorPrecision";
          }

          leaf oper-status {
            type sensor-status;
            description
              "Рабочее состояние датчика.";
            reference
              "RFC 3433: Entity Sensor Management Information Base -
                         entPhySensorOperStatus";
          }

          leaf units-display {
            type string;
            description
              "Текстовое описание единиц, которые следует применять при
               выводе значения датчика.";
            reference
              "RFC 3433: Entity Sensor Management Information Base -
                         entPhySensorUnitsDisplay";
          }

          leaf value-timestamp {
            type yang:date-and-time;
            description
              "Время последнего извлечения сервером статуса или
               значения этого датчика.";
            reference
              "RFC 3433: Entity Sensor Management Information Base -
                         entPhySensorValueTimeStamp";
          }

          leaf value-update-rate {
            type uint32;
            units "milliseconds";
            description
              "Интервал, с которым сервер обновляет связанный узел 
               value в миллисекундах. Значение 0 указывает, что:

                - значение датчика обновляется по запросу (например,
                  при опросе сервером для get-request);

                - значение датчика обновляется при его изменении
                  (по событию);

                - сервер не знает частоты обновления.";
            reference
              "RFC 3433: Entity Sensor Management Information Base -
                         entPhySensorValueUpdateRate";
          }
        }
      }
    }

    /*
     * Уведомления
     */

    notification hardware-state-change {
      description
        "Уведомление hardware-state-change создается при изменении
         значения /hardware/last-change в рабочем состоянии.";
      reference
        "RFC 6933: Entity MIB (Version 4) - entConfigChange";
    }

    notification hardware-state-oper-enabled {
      if-feature hardware-state;
      description
        "Уведомление hardware-state-oper-enabled указывает переход
         компонента в состояние enabled.";

      leaf name {
        type leafref {
          path "/hardware/component/name";
        }
        description
          "Имя компонента, перешедшего в состояние enabled.";
      }
      leaf admin-state {
        type leafref {
          path "/hardware/component/state/admin-state";
        }
        description
          "Административное состояние компонента.";
      }
      leaf alarm-state {
        type leafref {
          path "/hardware/component/state/alarm-state";
        }
        description
          "Статус сигналов тревоги для компонента.";
      }
      reference
        "RFC 4268: Entity State MIB - entStateOperEnabled";
    }

    notification hardware-state-oper-disabled {
      if-feature hardware-state;
      description
        "Уведомление hardware-state-oper-disabled указывает переход
         компонента в состояние disabled.";

      leaf name {
        type leafref {
          path "/hardware/component/name";
        }
        description
          "Имя компонента, перешедшего в состояние disabled.";
      }
      leaf admin-state {
        type leafref {
          path "/hardware/component/state/admin-state";
        }
        description
          "Административное состояние компонента.";
      }
      leaf alarm-state {
        type leafref {
          path "/hardware/component/state/alarm-state";
        }
        description
          "Статус сигналов тревоги для компонента.";
      }
      reference
        "RFC 4268: Entity State MIB - entStateOperDisabled";
    }

  }
   <CODE ENDS>

7.2. Модуль iana-hardware

   <CODE BEGINS> file "iana-hardware@2018-03-13.yang"

   module iana-hardware {
     yang-version 1.1;
     namespace "urn:ietf:params:xml:ns:yang:iana-hardware";
     prefix ianahw;

     organization "IANA";
     contact
       "        Internet Assigned Numbers Authority

        Postal: ICANN
                12025 Waterfront Drive, Suite 300
                Los Angeles, CA  90094-2536
                United States of America

        Tel:    +1 310 301 5800
        E-Mail: iana@iana.org>";

     description
       "Заданные IANA идентификаторы классов оборудования.

        Последний выпуск этого модуля YANG доступен на сайте IANA.

        Запросы на выделение новых значений следует направлять в IANA
        по адресу iana@iana.org. 

        Авторские права (Copyright (c) 2018) принадлежат IETF Trust
        и лицам, указанным в качестве авторов кода. Все права защищены.

        Распространение и использование в исходной или двоичной форме с
        изменениями или без таковых разрешено в соответствии с лицензией
        Simplified BSD, изложенной в разделе 4  IETF Trust's Legal
        Provisions применительно к документам IETF
        (http://trustee.ietf.org/license-info). 

        Эта версия данного модуля YANG является частью RFC 8348, где
        правовые вопросы рассмотрены более полно.";
     reference
       "https://www.iana.org/assignments/yang-parameters"; 

     revision 2018-03-13 {
       description
         "Исходный выпуск.";
       reference
         "RFC 8348: A YANG Data Model for Hardware Management";
     }

     /*
      * Идентификаторы (отождествления)
      */

     identity hardware-class {
       description
         "Базовый идентификатор для всех классов оборудования.";
     }

     identity unknown {
       base ianahw:hardware-class;
       description
         "Неизвестный серверу класс оборудования.";
     }

     identity chassis {
       base ianahw:hardware-class;
       description
         "Применяется для классов оборудования, служащих контейнерами 
          для сетевого оборудования. Любой класс физических компонентов,
          кроме стека, может содержаться в шасси. Шасси могут входить
          лишь в стек.";
     }

     identity backplane {
       base ianahw:hardware-class;
       description
         "Применяется для классов оборудования, представляющих 
          устройства для агрегирования и пересылки сетевого трафика,
          такие как общая шина (backplane) в модульном коммутаторе 
          Ethernet. Отметим, что реализация может считать шину одним
          физическим компонентом, который на деле реализован в виде
          множества дискретных физических компонентов (в шасси стека).";
     }

     identity container {
       base ianahw:hardware-class;
       description
         "Применяется для классов оборудования, способных включать
          съёмные физические элементы, возможно разных типов. Например, 
          каждое (заполненное или пустое) гнездо шасси моделируется
          как контейнер. Отметим, что все съёмные физические компоненты
          (например, сменные модули, вентиляторы, блоки питания)
          следует моделировать внутри компонента-контейнера. Известные 
          контейнеры (включая пустые) следует моделировать агенту.";
     }

     identity power-supply {
       base ianahw:hardware-class;
       description
         "Идентификатор для компонентов электропитания.";
     }

     identity fan {
       base ianahw:hardware-class;
       description
         "Идентификатор для вентиляторов и иных узлов охлаждения.";
     }

     identity sensor {
       base ianahw:hardware-class;
       description
         "Идентификатор для некоторых типов датчиков, таких как датчик
          температуры в шасси маршрутизатора.";
     }

     identity module {
       base ianahw:hardware-class;
       description
         "Идентификатор для некоторых классов самодостаточных подсистем.
          Если компонент-модуль является съёмным, его следует 
          моделировать в компоненте-контейнере, иначе - напрямую в 
          другом физическом компоненте (шасси или другой модуль).";
     }

     identity port {
       base ianahw:hardware-class;
       description
         "Идентификатор для сетевых портов, способных передавать или
          принимать сетевой трафик.";
     }

     identity stack {
       base ianahw:hardware-class;
       description
         "Идентификатор для суперконтейнеров (возможно, виртуальных),
          предназначенных для группировки нескольких объектов шасси.
          Стек может быть реализован виртуальным кабелем, физическим 
          кабелем между разными шасси, или несколькими кабелями. Стек
          не следует моделировать внутри других физических компонентов,
          но он может входить в другой стек. В стек следует включать
          лишь шасси и другие стеки.";
     }

     identity cpu {
       base ianahw:hardware-class;
       description
         "Идентификатор для центральных процессоров.";
     }

     identity energy-object {
       base ianahw:hardware-class;
       description
         "Идентификатор оборудования, относящегося к энергии, т. е. 
          элементов оборудования, являющихся частью коммуникационной 
          сети (подключённого к ней), которая отслеживается, 
          контролируется или предназначена для управления другим 
          устройством в части потребления энергии.";
     }

     identity battery {
       base ianahw:hardware-class;
       description
         "Идентификатор батареи питания.";
     }

     identity storage-drive {
       base ianahw:hardware-class;
       description
         "Идентификатор оборудования, способного хранить данные в
          качестве основной функции, например, диски (HDD, SSD, SSHD), 
          хранилища объектов (OSD) и т. п.";
     }
   }
   <CODE ENDS>

8. Взаимодействие с IANA

Этот документ задаёт исходный выпуск поддерживаемого IANA модуля YANG iana-hardware. Этот модуль предназначен для отображения модуля IANA-ENTITY-MIB, чтобы при добавлении нового перечисляемого в IANAPhysicalClass такой же класс добавлялся как отождествление, выведенное из ianahw:hardware-class. При обновлении модуля YANG iana-hardware должен добавляться новый оператор revision перед уже имеющимися.

8.1. Регистрация URI

Этот документ регистрирует три URI в реестре IETF XML Registry [RFC3688] в соответствии с форматом RFC 3688.

     URI: urn:ietf:params:xml:ns:yang:iana-hardware
     Registrant Contact: The IESG.
     XML: N/A, регистрируемый URI является пространством имён XML.

     URI: urn:ietf:params:xml:ns:yang:ietf-hardware
     Registrant Contact: The IESG.
     XML: N/A, регистрируемый URI является пространством имён XML.

     URI: urn:ietf:params:xml:ns:yang:ietf-hardware-state
     Registrant Contact: The IESG.
     XML: N/A, регистрируемый URI является пространством имён XML.

8.2. Регистрация модулей YANG

Этот документ регистрирует три модуля YANG в реестре YANG Module Names [RFC6020].

     name:         iana-hardware
     namespace:    urn:ietf:params:xml:ns:yang:iana-hardware
     prefix:       ianahw
     reference:    RFC 8348

     name:         ietf-hardware
     namespace:    urn:ietf:params:xml:ns:yang:ietf-hardware
     prefix:       hw
     reference:    RFC 8348

     name:         ietf-hardware-state
     namespace:    urn:ietf:params:xml:ns:yang:ietf-hardware-state
     prefix:       hw-state
     reference:    RFC 8348

9. Вопросы безопасности

Заданные в этом документе модули YANG определяют схемы для данных, которые разработаны для доступа через протоколы управления сетью, такие как NETCONF [RFC6241] и RESTCONF [RFC8040]. Нижним уровнем NETCONF является защищённый транспорт с обязательной реализацией Secure Shell (SSH) [RFC6242]. Нижним уровнем RESTCONF служит HTTPS с обязательной реализацией защищённого транспорта TLS [RFC5246].

Модель управления доступом NETCONF [RFC8341] обеспечивает средства, позволяющие предоставить доступ лишь конкретным пользователям NETCONF и RESTCONF к предопределённому подмножеству доступных в NETCONF или RESTCONF протокольных операций и содержимого.

В модуле YANG ietf-hardware имеется множество узлов данных, доступных для записи, создания, удаления (т. е. с принятым по умолчанию config true). Эти узлы могут быть чувствительными или уязвимыми в некоторых сетевых средах. Операции записи (например, edit-config) в такие узлы без подобающей защиты могут оказывать негативное влияние на работу сети. Ниже указаны ветви и узлы данных и их уязвимости.

/hardware/component/admin-state

Установка для этого узла значения locked или shutting-down может нарушать работу служб, начиная от работающих на порту и заканчивая работой устройства в целом, в зависимости от типа компонента.

Некоторые из доступных для чтения узлов этого модуля YANG могут быть конфиденциальными или уязвимыми в некоторых сетевых средах. Важно контролировать доступ к считыванию таких узлов данных (например, через операции get, get-config, notification). Ниже указаны ветви и узлы данных конфиденциальные или уязвимые в плане их чтения.

/hardware/component

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

/hardware/component/sensor-data/value

Эти узлы могут раскрывать значения физических датчиков устройства.

/hardware/component/state

Доступ к этим узлам позволяет определить активные и ожидающие (резервные) ресурсы.

10. Литература

10.1. Нормативные документы

[RFC2119] Bradner, S., «Key words for use in RFCs to Indicate Requirement Levels», BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.

[RFC3433] Bierman, A., Romascanu, D., and K. Norseth, «Entity Sensor Management Information Base», RFC 3433, DOI 10.17487/RFC3433, December 2002, <https://www.rfc-editor.org/info/rfc3433>.

[RFC3688] Mealling, M., «The IETF XML Registry», BCP 81, RFC 3688, DOI 10.17487/RFC3688, January 2004, <https://www.rfc-editor.org/info/rfc3688>.

[RFC4268] Chisholm, S. and D. Perkins, «Entity State MIB», RFC 4268, DOI 10.17487/RFC4268, November 2005, <https://www.rfc-editor.org/info/rfc4268>.

[RFC5246] Dierks, T. and E. Rescorla, «The Transport Layer Security (TLS) Protocol Version 1.2», RFC 5246, DOI 10.17487/RFC5246, August 2008, <https://www.rfc-editor.org/info/rfc5246>.

[RFC6020] Bjorklund, M., Ed., «YANG — A Data Modeling Language for the Network Configuration Protocol (NETCONF)», RFC 6020, DOI 10.17487/RFC6020, October 2010, <https://www.rfc-editor.org/info/rfc6020>.

[RFC6241] Enns, R., Ed., Bjorklund, M., Ed., Schoenwaelder, J., Ed., and A. Bierman, Ed., «Network Configuration Protocol (NETCONF)», RFC 6241, DOI 10.17487/RFC6241, June 2011, <https://www.rfc-editor.org/info/rfc6241>.

[RFC6242] Wasserman, M., «Using the NETCONF Protocol over Secure Shell (SSH)», RFC 6242, DOI 10.17487/RFC6242, June 2011, <https://www.rfc-editor.org/info/rfc6242>.

[RFC6933] Bierman, A., Romascanu, D., Quittek, J., and M. Chandramouli, «Entity MIB (Version 4)», RFC 6933, DOI 10.17487/RFC6933, May 2013, <https://www.rfc-editor.org/info/rfc6933>.

[RFC6991] Schoenwaelder, J., Ed., «Common YANG Data Types», RFC 6991, DOI 10.17487/RFC6991, July 2013, <https://www.rfc-editor.org/info/rfc6991>.

[RFC7950] Bjorklund, M., Ed., «The YANG 1.1 Data Modeling Language», RFC 7950, DOI 10.17487/RFC7950, August 2016, <https://www.rfc-editor.org/info/rfc7950>.

[RFC8040] Bierman, A., Bjorklund, M., and K. Watsen, «RESTCONF Protocol», RFC 8040, DOI 10.17487/RFC8040, January 2017, <https://www.rfc-editor.org/info/rfc8040>.

[RFC8174] Leiba, B., «Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words», BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.

[RFC8341] Bierman, A. and M. Bjorklund, «Network Configuration Access Control Model», STD 91, RFC 8341, DOI 10.17487/RFC8341, March 2018, <https://www.rfc-editor.org/info/rfc8341>.

[RFC8342] Bjorklund, M., Schoenwaelder, J., Shafer, P., Watsen, K., and R. Wilton, «Network Management Datastore Architecture (NMDA)», RFC 8342, DOI 10.17487/RFC8342, March 2018, <https://www.rfc-editor.org/info/rfc8342>.

10.2. Дополнительная литература

[RFC8340] Bjorklund, M. and L. Berger, Ed., «YANG Tree Diagrams», BCP 215, RFC 8340, DOI 10.17487/RFC8340, March 2018, <https://www.rfc-editor.org/info/rfc8340>.

Приложение A. Модель данных состояния оборудования

В этом ненормативном приложении представлено временное решение для реализаций, ещё не поддерживающих архитектуру NMDA, определённую в [RFC8342]. Ниже показана структура модуля.

   module: ietf-hardware-state
     x--ro hardware
        x--ro last-change?   yang:date-and-time
        x--ro component* [name]
           x--ro name              string
           x--ro class             identityref
           x--ro physical-index?   int32 {entity-mib}?
           x--ro description?      string
           x--ro parent?           -> ../../component/name
           x--ro parent-rel-pos?   int32
           x--ro contains-child*   -> ../../component/name
           x--ro hardware-rev?     string
           x--ro firmware-rev?     string
           x--ro software-rev?     string
           x--ro serial-num?       string
           x--ro mfg-name?         string
           x--ro model-name?       string
           x--ro alias?            string
           x--ro asset-id?         string
           x--ro is-fru?           boolean
           x--ro mfg-date?         yang:date-and-time
           x--ro uri*              inet:uri
           x--ro uuid?             yang:uuid
           x--ro state {hardware-state}?
           |  x--ro state-last-changed?   yang:date-and-time
           |  x--ro admin-state?          hw:admin-state
           |  x--ro oper-state?           hw:oper-state
           |  x--ro usage-state?          hw:usage-state
           |  x--ro alarm-state?          hw:alarm-state
           |  x--ro standby-state?        hw:standby-state
           x--ro sensor-data {hardware-sensor}?
              x--ro value?               hw:sensor-value
              x--ro value-type?          hw:sensor-value-type
              x--ro value-scale?         hw:sensor-value-scale
              x--ro value-precision?     hw:sensor-value-precision
              x--ro oper-status?         hw:sensor-status
              x--ro units-display?       string
              x--ro value-timestamp?     yang:date-and-time
              x--ro value-update-rate?   uint32

     notifications:
       x---n hardware-state-change
       x---n hardware-state-oper-enabled {hardware-state}?
       |  x--ro name?          -> /hardware/component/name
       |  x--ro admin-state?   -> /hardware/component/state/admin-state
       |  x--ro alarm-state?   -> /hardware/component/state/alarm-state
       x---n hardware-state-oper-disabled {hardware-state}?
          x--ro name?          -> /hardware/component/name
          x--ro admin-state?   -> /hardware/component/state/admin-state
          x--ro alarm-state?   -> /hardware/component/state/alarm-state

A.1. Модуль YANG для состояния оборудования

   <CODE BEGINS> file "ietf-hardware-state@2018-03-13.yang"

   module ietf-hardware-state {
     yang-version 1.1;
     namespace "urn:ietf:params:xml:ns:yang:ietf-hardware-state";
     prefix hw-state;

     import ietf-inet-types {
       prefix inet;
     }
     import ietf-yang-types {
       prefix yang;
     }
     import iana-hardware {
       prefix ianahw;
     }
     import ietf-hardware {
       prefix hw;
     }

     organization
       "IETF NETMOD (Network Modeling) Working Group";

     contact
       "WG Web:   <https://datatracker.ietf.org/wg/netmod/> 
        WG List:  <mailto:netmod@ietf.org> 

        Editor:   Andy Bierman
                  <mailto:andy@yumaworks.com> 

        Editor:   Martin Bjorklund
                  <mailto:mbj@tail-f.com> 

        Editor:   Jie Dong
                  <mailto:jie.dong@huawei.com> 

        Editor:   Dan Romascanu
                  <mailto:dromasca@gmail.com>"; 

     description
       "Этот модуль содержит определения YANG для мониторинга
        оборудования.

        Эта модель данных разработана как временное решение для 
        реализаций, ещё не поддерживающих архитектуру NMDA, заданную в
        RFC 8342. Такие реализации не могут корректно применять модуль 
        ietf-hardware, поскольку без поддержки NMDA невозможно 
        различать экземпляры узлов рабочей конфигурации и рабочего
        состояния.

        Модель данных в этом модуле совпадает с моделью в ietf-hardware, 
        но для всех узлов задано config false.

        Если сервер, реализующий этот модуль, но не поддерживающий NMDA,
        поддерживает настройку аппаратных компонентов, ему СЛЕДУЕТ
        реализовать модуль ietf-hardware в хранилищах конфигурации.
        Соответствующие данные состояния представлены в ветви
        /hw-state:hardware.

        Авторские права (Copyright (c) 2018) принадлежат IETF Trust
        и лицам, указанным в качестве авторов кода. Все права защищены.

        Распространение и использование в исходной или двоичной форме с
        изменениями или без таковых разрешено в соответствии с лицензией
        Simplified BSD, изложенной в разделе 4  IETF Trust's Legal
        Provisions применительно к документам IETF
        (http://trustee.ietf.org/license-info). 

        Эта версия данного модуля YANG является частью RFC 8348, где
        правовые вопросы рассмотрены более полно.";

     revision 2018-03-13 {
       description
         "Исходный выпуск.";
       reference
         "RFC 8348: A YANG Data Model for Hardware Management";
     }

     /*
      * Свойства (функции)
      */
     feature entity-mib {
       status deprecated;
       description
         "Указывает, что устройство реализует ENTITY-MIB.";
       reference
         "RFC 6933: Entity MIB (Version 4)";
     }

     feature hardware-state {
       status deprecated;
       description
         "Указывает, что устройство поддерживает ENTITY-STATE-MIB.";
       reference
         "RFC 4268: Entity State MIB";
     }

     feature hardware-sensor {
       status deprecated;
       description
         "Указывает, что устройство поддерживает ENTITY-SENSOR-MIB.";
       reference
         "RFC 3433: Entity Sensor Management Information Base";
     }

     /*
      * Узлы данных
      */

     container hardware {
       config false;
       status deprecated;
       description
         "Узлы данных, представляющие компоненты.";

       leaf last-change {
         type yang:date-and-time;
         status deprecated;
         description
           "Время изменения списка /hardware/component в рабочем 
            состоянии.";
       }

       list component {
         key name;
         status deprecated;
         description
           "Список компонентов. Когда сервер обнаруживает новый
            аппаратный компонент, он инициализирует запись списка в
            рабочем состоянии. Если сервер не поддерживает настройку
            аппаратных компонентов, записи списка в рабочем состоянии
            инициализируются значениями для всех узлов, найденных 
            реализацией. В остальных случаях выполняются указанные
            ниже процедуры.

              1. Если в списке /hardware/component предполагаемой 
                 (intended) конфигурации есть запись со значениями узлов
                 class, parent и parent-rel-pos, равными обнаруженным,
                 запись списка в рабочем состоянии, выполняются 
                 указанные ниже действия.

              1a. Если настроенная запись имеет значение mfg-name, 
                  равное найденному или mfg-nameне удаётся найти, запись
                  списка в рабочем состоянии инициализируется заданными
                  значения для всех настраиваемых узлов, включая name.

                  В иных случаях запись в списке рабочего состояния
                  значениями всех узлов, найденными реализацией. 
                  Реализация может установить сигнал тревоги,
                  указывающий, что mfg-name не соответствует условиям.
                  Способ реализации этого документ не задаёт.

              1b. В ином случае (нет соответствующей записи конфигурации)
                  записи списка в рабочем состоянии инициализируется 
                  значениями для всех узлов, найденных реализацией.

            Если список /hardware/component в предполагаемой конфигурации
            изменен, система ДОЛЖНА вести себя как при реинициализации
            и следовать процедуре п. (1).";
         reference
           "RFC 6933: Entity MIB (Version 4) - entPhysicalEntry";

         leaf name {
           type string;
           status deprecated;
           description
             "Имя компонента (может отличаться от entPhysicalName).";
         }

         leaf class {
           type identityref {
             base ianahw:hardware-class;
           }
           mandatory true;
           status deprecated;
           description
             "Оборудование общего назначения.";
           reference
             "RFC 6933: Entity MIB (Version 4) - entPhysicalClass";
         }

         leaf physical-index {
           if-feature entity-mib;
           type int32 {
             range "1..2147483647";
           }
           status deprecated;
           description
             "Представляет entPhysicalIndex для entPhysicalEntry.";
           reference
             "RFC 6933: Entity MIB (Version 4) - entPhysicalIndex";
         }

         leaf description {
           type string;
           status deprecated;
           description
             "Текстовое описание компонента. Узлу следует содержать 
              строку, указывающую производителя компонента и следует
              указывать разные значения для каждой версии или модели.";
           reference
             "RFC 6933: Entity MIB (Version 4) - entPhysicalDescr";
         }

         leaf parent {
           type leafref {
             path "../../component/name";
             require-instance false;
           }
           status deprecated;
           description
             "Имя компонента, физически включающего этот компонент. Если
              этот лист не создаётся, это указывает, что компонент не 
              содержится в каком-либо ином компоненте.

              Если физический компонент входит в несколько физических
              компонентов (например, модуль двойной ширины), узел 
              содержит имя одного из этих компонентов. Реализация ДОЛЖНА
              использовать одно имя для каждого экземпляра узла.";
           reference
             "RFC 6933: Entity MIB (Version 4) -
                        entPhysicalContainedIn";
         }

         leaf parent-rel-pos {
           type int32 {
             range "0 .. 2147483647";
           }
           status deprecated;
           description
             "Указывает относительное положение дочернего компонента
              среди братских, к каковым относятся компоненты, имеющие 
              одно значение узла parent и одно базовое отождествление
              для узла class. Отметим, что второе правило обеспечивает
              реализациям гибкость нумерации компонентов. Например,
              некоторые реализации могут иметь одну серию номеров для
              всех компонентов, производных от ianahw:port, а другие -
              применять для них разные серии номеров (скажем, одну для
              разъёмов RJ45, другую для SFP).";

           reference
             "RFC 6933: Entity MIB (Version 4) -
                        entPhysicalParentRelPos";
         }

         leaf-list contains-child {
           type leafref {
             path "../../component/name";
           }
           status deprecated;
           description
             "Имя содержащегося компонента.";
           reference
             "RFC 6933: Entity MIB (Version 4) - entPhysicalChildIndex";
         }

         leaf hardware-rev {
           type string;
           status deprecated;
           description
             "Фирменная строка аппаратного выпуска компонента. 
              Предпочтительно указывать идентификатор, помещаемый 
              производителем на сам компонент (при наличии).";
           reference
             "RFC 6933: Entity MIB (Version 4) -
                        entPhysicalHardwareRev";
         }

         leaf firmware-rev {
           type string;
           status deprecated;
           description
             "Фирменная строка выпуска микрокода для компонента.";
           reference
             "RFC 6933: Entity MIB (Version 4) -
                        entPhysicalFirmwareRev";
         }

         leaf software-rev {
           type string;
           status deprecated;
           description
            "Фирменная строка программного выпуска компонента.";
           reference
             "RFC 6933: Entity MIB (Version 4) -
                        entPhysicalSoftwareRev";
         }

         leaf serial-num {
           type string;
           status deprecated;
           description
             "Фирменная строка серийного номера компонента.
              Предпочтительно указывать номер, помещаемый 
              производителем на сам компонент (при наличии).";
           reference
             "RFC 6933: Entity MIB (Version 4) - entPhysicalSerialNum";
         }

         leaf mfg-name {
           type string;
           status deprecated;
           description
             "Название изготовителя для физического компонента.
              Предпочтительно указывать название, помещаемое 
              изготовителем на сам компонент (при наличии).";

              Отметим, что сравнение значений model-name, firmware-rev,
              software-rev и serial-num имеет смысл лишь для 
              компонентов с одним значением mfg-name.

              Если заданное изготовителем название физического
              компонента неизвестно серверу, этот узел не создаётся.";
           reference
             "RFC 6933: Entity MIB (Version 4) - entPhysicalMfgName";
         }

         leaf model-name {
           type string;
           status deprecated;
           description
             "Фирменная строка идентификатора модели физического
              компонента. Предпочтительно указывать видимый клиенту
              код, напечатанный на самом компоненте (при наличии).

              Если заданный производителем код модели физического
              компонента неизвестно серверу, этот узел не создается.";
           reference
             "RFC 6933: Entity MIB (Version 4) - entPhysicalModelName";
         }

         leaf alias {
           type string;
           status deprecated;
           description
             "Псевдоним имени компонента, заданный администратором сети,
              сохраняющийся при выключении или перезагрузке компонента.

              Если псевдоним не задан, сервер МОЖЕТ установить для узла
              локально уникальное значение в рабочем состоянии.

              Реализация сервера МОЖЕТ сопоставить этот лист с объектом
              MIB entPhysicalAlias. Такой реализации нужен механизм для
              обработки различий в размере и разрешённых символах между
              этим листом и entPhysicalAlias. Задание такого механизма
              выходит за рамки этого документа.";
           reference
             "RFC 6933: Entity MIB (Version 4) - entPhysicalAlias";
         }

         leaf asset-id {
           type string;
           status deprecated;
           description
             "Заданный пользователем идентификатор отслеживания активов
              для компонента.

              Реализация сервера МОЖЕТ сопоставить этот лист с объектом
              MIB entPhysicalAssetID. Такой реализации нужен механизм для
              обработки различий в размере и разрешённых символах между
              этим листом и entPhysicalAssetID. Задание такого механизма
              выходит за рамки этого документа.";
           reference
             "RFC 6933: Entity MIB (Version 4) - entPhysicalAssetID";
         }

         leaf is-fru {
           type boolean;
           status deprecated;
           description
             "Указывает, считает ли производитель этот компонент
              заменяемым в полевых условиях. Значение true указывает
              возможность такой замены. Для всех компонентов, постоянно
              содержащихся в этом компоненте для этого узла следует
              возвращать значение false.";
           reference
             "RFC 6933: Entity MIB (Version 4) - entPhysicalIsFRU";
         }

         leaf mfg-date {
           type yang:date-and-time;
           status deprecated;
           description
             "Дата изготовления компонента.";
           reference
             "RFC 6933: Entity MIB (Version 4) - entPhysicalMfgDate";
         }

         leaf-list uri {
           type inet:uri;
           status deprecated;
           description
             "Идентификационные сведения о компоненте.";
           reference
             "RFC 6933: Entity MIB (Version 4) - entPhysicalUris";
         }

         leaf uuid {
           type yang:uuid;
           status deprecated;
           description
             "Глобально уникальный идентификатор (UUID) компонента.";
           reference
             "RFC 6933: Entity MIB (Version 4) - entPhysicalUUID";
         }

         container state {
           if-feature hardware-state;
           status deprecated;
           description
             "Относящиеся к состоянию узлы.";
           reference
             "RFC 4268: Entity State MIB";

           leaf state-last-changed {
             type yang:date-and-time;
             status deprecated;
             description
               "Дата и время смены любого из значений admin-state, 
                oper-state, usage-state, alarm-state или
                standby-state для этого компонента.

                Если с момента последней реинициализации системы не было
                изменений, узел содержит дату и время инициализации 
                локальной системы. Если не было изменений с момента
                установки компонента в локальную систему, указывается
                дата и время такой установки.";
             reference
               "RFC 4268: Entity State MIB - entStateLastChanged";
           }

           leaf admin-state {
             type hw:admin-state;
             status deprecated;
             description
               "Административное состояние компонента. Указывает
                административное разрешение обслуживать другие компоненты
                в иерархии содержания, а также других пользователей этих
                услуг, заданных мерами, выходящими за рамки этого модуля.

                Некоторые компоненты раскрывают лишь часть остальных
                значений административного состояния. Некоторые 
                компоненты нельзя блокировать, поэтому узел показывает
                лишь статус unlocked. Другие компоненты нельзя аккуратно
                отключить и узел не показывает статус shutting-down.";
             reference
               "RFC 4268: Entity State MIB - entStateAdmin";
           }

           leaf oper-state {
             type hw:oper-state;
             status deprecated;
             description
               "Рабочее состояние компонента. Отметим, что этот узел не
                за административным статусом, т. е. административный
                статус down не означает рабочего статуса disabled.

                Отметим, что некоторые реализации могут не поддерживать
                точное отображение oper-state, когда admin-state для узла
                отличается от unlocked. В таких случаях узел ДОЛЖЕН иметь
                значение unknown.";
             reference
               "RFC 4268: Entity State MIB - entStateOper";
           }

           leaf usage-state {
             type hw:usage-state;
             status deprecated;
             description
               "Состояние использования компонента, отражающее его
                способность обслуживать дополнительные компоненты в
                иерархии содержания.

                Некоторые компоненты раскрывают лишь часть значений 
                статуса использования. Компоненты, не способные 
                обслуживать какие-либо компоненты в иерархии размещения,
                всегда будут иметь статус busy. В некоторых случаях 
                компонент может поддерживать лишь ещё один компонент в
                иерархии размещения и будет раскрывать лишь значение
                idle или busy.";
             reference
               "RFC 4268: Entity State MIB - entStateUsage";
           }

           leaf alarm-state {
             type hw:alarm-state;
             status deprecated;
             description
               "Состояние сигналов тревоги для компонента без учета
                сигналов в дочерних компонентах иерархии размещения.";
             reference
               "RFC 4268: Entity State MIB - entStateAlarm";
           }

           leaf standby-state {
             type hw:standby-state;
             status deprecated;
             description
               "Состояние ожидания (standby) для компонента.

                Некоторые компоненты раскрывают лишь часть значений 
                статуса ожидания. Компоненты, не способные работать
                в режиме ожидания будут иметь для этого узла значение
                providing-service.";
             reference
               "RFC 4268: Entity State MIB - entStateStandby";
           }
         }

         container sensor-data {
           when 'derived-from-or-self(../class,
                                      "ianahw:sensor")' {
             description
               "Узлы данных для компонентов типа sensor";
           }
           if-feature hardware-sensor;
           status deprecated;

           description
             "Связанные с датчиками узлы.";
           reference
             "RFC 3433: Entity Sensor Management Information Base";

           leaf value {
             type hw:sensor-value;
             status deprecated;
             description
               "Последнее измерение, полученное сервером от датчика.

                Клиенту, периодически просматривающему этот узел, 
                следует извлекать и узлы value-type, value-scale и
                value-precision, поскольку они тоже могут изменяться.";
             reference
               "RFC 3433: Entity Sensor Management Information Base -
                          entPhySensorValue";
           }

           leaf value-type {
             type hw:sensor-value-type;
             status deprecated;
             description
               "Тип единиц, связанных со значением датчика";
             reference
               "RFC 3433: Entity Sensor Management Information Base -
                          entPhySensorType";
           }

           leaf value-scale {
             type hw:sensor-value-scale;
             status deprecated;
             description
               "Коэффициент (степень 10) измерения для значения узла";
             reference
               "RFC 3433: Entity Sensor Management Information Base -
                          entPhySensorScale";
           }

           leaf value-precision {
             type hw:sensor-value-precision;
             status deprecated;
             description
               "Число десятичных знаков точности значения узла.";
             reference
               "RFC 3433: Entity Sensor Management Information Base -
                          entPhySensorPrecision";
           }

           leaf oper-status {
             type hw:sensor-status;
             status deprecated;
             description
               "Рабочее состояние датчика.";
             reference
               "RFC 3433: Entity Sensor Management Information Base -
                          entPhySensorOperStatus";
           }

           leaf units-display {
             type string;
             status deprecated;
             description
               "Текстовое описание единиц, которые следует применять при
                выводе значения датчика.";
             reference
               "RFC 3433: Entity Sensor Management Information Base -
                          entPhySensorUnitsDisplay";
           }

           leaf value-timestamp {
             type yang:date-and-time;
             status deprecated;
             description
               "Время последнего извлечения сервером статуса или
                значения этого датчика.";
             reference
               "RFC 3433: Entity Sensor Management Information Base -
                          entPhySensorValueTimeStamp";
           }

           leaf value-update-rate {
             type uint32;
             units "milliseconds";
             status deprecated;
             description
               "Интервал, с которым сервер обновляет связанный узел 
                value в миллисекундах. Значение 0 указывает, что:

                 - значение датчика обновляется по запросу (например,
                   при опросе сервером для get-request);

                 - значение датчика обновляется при его изменении
                   (по событию);
 
                 - сервер не знает частоты обновления.";
             reference
               "RFC 3433: Entity Sensor Management Information Base -
                          entPhySensorValueUpdateRate";
           }
         }
       }
     }

     /*
      * Уведомления
      */

     notification hardware-state-change {
       status deprecated;
       description
        "Уведомление hardware-state-change создается при изменении
         значения /hardware/last-change в рабочем состоянии.";
       reference
         "RFC 6933: Entity MIB (Version 4) - entConfigChange";
     }

     notification hardware-state-oper-enabled {
       if-feature hardware-state;
       status deprecated;
       description
         "Уведомление hardware-state-oper-enabled указывает переход
          компонента в состояние enabled.";

       leaf name {
         type leafref {
           path "/hardware/component/name";
         }
         status deprecated;
         description
           "Имя компонента, перешедшего в состояние enabled.";
       }
       leaf admin-state {
         type leafref {
           path "/hardware/component/state/admin-state";
         }
         status deprecated;
         description
           "Административное состояние компонента.";
       }
       leaf alarm-state {
         type leafref {
           path "/hardware/component/state/alarm-state";
         }
         status deprecated;
         description
           "Состояние сигналов тревоги для компонента.";
       }
       reference
         "RFC 4268: Entity State MIB - entStateOperEnabled";
     }

     notification hardware-state-oper-disabled {
       if-feature hardware-state;
       status deprecated;
       description
         "Уведомление hardware-state-oper-disabled указывает переход
          компонента в состояние disabled.";

       leaf name {
         type leafref {
           path "/hardware/component/name";
         }
         status deprecated;
         description
           "Имя компонента, перешедшего в состояние disabled.";
       }
       leaf admin-state {
         type leafref {
           path "/hardware/component/state/admin-state";
         }
         status deprecated;
         description
           "Административное состояние компонента.";
       }
       leaf alarm-state {
         type leafref {
           path "/hardware/component/state/alarm-state";
         }
         status deprecated;
         description
           "Состояние сигналов тревоги для компонента.";
       }
       reference
         "RFC 4268: Entity State MIB - entStateOperDisabled";
     }

   }
   <CODE ENDS>

Благодарности

Авторы благодарны Bart Bogaert, Timothy Carey, William Lupton и Juergen Schoenwaelder за комментарии к предварительным вариантам этого документа.

Адреса авторов

Andy Bierman
YumaWorks
Email: andy@yumaworks.com
 
Martin Bjorklund
Tail-f Systems
Email: mbj@tail-f.com
 
Jie Dong
Huawei Technologies
Email: jie.dong@huawei.com
 
Dan Romascanu
Email: dromasca@gmail.com

Перевод на русский язык

Николай Малых

nmalykh@protokols.ru

1Internet Engineering Task Force — комиссия по решению инженерных задач Internet.

2Internet Engineering Steering Group — комиссия по инженерным разработкам Internet.

Рубрика: RFC | Оставить комментарий

RFC 8349 A YANG Data Model for Routing Management (NMDA Version)

Internet Engineering Task Force (IETF)                         L. Lhotka
Request for Comments: 8349                                        CZ.NIC
Obsoletes: 8022                                                A. Lindem
Category: Standards Track                                  Cisco Systems
ISSN: 2070-1721                                                    Y. Qu
                                                                  Huawei
                                                              March 2018

A YANG Data Model for Routing Management (NMDA Version)

Модель данных YANG для управления маршрутизацией (версия NMDA)

PDF

Аннотация

Документ задаёт 3 модуля YANG и 1 субмодуль, которые совместно формируют модель данных ядра маршрутизации, которая служит основой для настройки и управления подсистемой маршрутизации. Предполагается, что эти модули будут дополнены модулями YANG, определяющими модели данных для протоколов плоскости управления, маршрутных фильтров и других функций. Модель данных ядра маршрутизации обеспечивает базовые блоки для таких расширений — маршруты, базы маршрутных данных (Routing Information Base или RIB) и протоколы плоскости управления.

Модули YANG в этом документе соответствуют архитектуре хранилищ данных управления сетью (Network Management Datastore Architecture или NMDA). Этот документ отменяет RFC 8022.

Статус документа

Документ относится к категории Internet Standards Track.

Документ является результатом работы IETF1 и представляет согласованный взгляд сообщества IETF. Документ прошёл открытое обсуждение и был одобрен для публикации IESG2. Дополнительную информацию о стандартах Internet можно найти в разделе 2 в RFC 7841.

Информацию о текущем статусе документа, ошибках и способах обратной связи можно найти по ссылке https://www.rfc-editor.org/info/rfc8349.

Авторские права

Авторские права (Copyright (c) 2018) принадлежат IETF Trust и лицам, указанным в качестве авторов документа. Все права защищены.

К документу применимы права и ограничения, указанные в BCP 78 и IETF Trust Legal Provisions и относящиеся к документам IETF (http://trustee.ietf.org/license-info), на момент публикации данного документа. Прочтите упомянутые документы внимательно. Фрагменты программного кода, включённые в этот документ, распространяются в соответствии с упрощённой лицензией BSD, как указано в параграфе 4.e документа IETF Trust Legal Provisions, без каких-либо гарантий (как указано в Simplified BSD License).

1. Введение

Этот документ задаёт перечисленные ниже модули YANG.

  • ietf-routing содержит базовые компоненты модели данных маршрутизации.

  • ietf-ipv4-unicast-routing дополняет модуль ietf-routing данными для индивидуальной адресации IPv4.

  • ietf-ipv6-unicast-routing дополняет модуль ietf-routing данными для индивидуальной адресации IPv6 unicast. Его субмодуль ietf-ipv6-router-advertisements дополняет модули ietf-interfaces [RFC8343] и ietf-ip [RFC8344] переменными настройки маршрутизаторов IPv6, требуемыми [RFC4861].

Эти модули совместно определяют ядро модели данных маршрутизации, предназначенное в качестве основы для будущих моделей более изощрённых систем маршрутизации. Хотя эти три модуля можно напрямую применять для простых устройств IP со статической маршрутизацией (см. Приложение B), их основная задача состоит в предоставлении блоков для создания более сложных моделей данных, включающих несколько протоколов плоскости управления, групповую маршрутизацию, дополнительные семейства адресов и расширенные функции, такие как фильтрация маршрутов или маршрутизация на основе правил. Поэтому предполагается, что базовая модель будет дополнена многочисленными модулями, определёнными различными рабочими группами IETF.

Модули YANG в этом документе соответствуют архитектуре NMDA [RFC8342]. Документ отменяет RFC 8022 [RFC8022].

2. Термины и обозначения

Ключевые слова должно (MUST), недопустимо (MUST NOT), требуется (REQUIRED), нужно (SHALL), не следует (SHALL NOT), следует (SHOULD), не нужно (SHOULD NOT), рекомендуется (RECOMMENDED), не рекомендуется (NOT RECOMMENDED), возможно (MAY), необязательно (OPTIONAL) в данном документе интерпретируются в соответствии с BCP 14 [RFC2119] [RFC8174] тогда и только тогда, когда они выделены шрифтом, как показано здесь.

Ниже перечислены термины, определённые в [RFC8342]:

  • client — клиент;

  • server — сервер;

  • configuration — конфигурация;

  • system state — состояние системы;

  • operational state — рабочее состояние;

  • intended configuration — предполагаемая конфигурация.

Ряд терминов определён в [RFC7950]:

  • action — действие;

  • augment — дополнение;

  • container — контейнер;

  • data model — модель данных;

  • data node — узел данных;

  • feature — свойство, функция;

  • leaf — лист;

  • list — список;

  • mandatory node — обязательный узел;

  • module — модуль;

  • presence container — контейнер присутствия;

  • schema tree — дерево схемы;

  • RPC (Remote Procedure Call) operation — вызов удалённой процедуры.

2.1. Новые термины

core routing data model — ядро модели данных

Модель данных YANG, включающая модули ietf-routing, ietf-ipv4-unicast-routing, ietf-ipv6-unicast-routing.

direct route — прямой маршрут

Маршрут в подключённую напрямую сеть.

Routing Information Base (RIB) — база маршрутной информации

Объект, содержащий список маршрутов вместе с другой информацией (см. параграф 5.2).

system-controlled entry — управляемый системой объект (сущность)

Запись в списке операционного состояния (config false), создаваемая системой независимо от того, что было настроено явно (см. параграф 4.1).

user-controlled entry — управляемый пользователем объект (сущность)

Запись в списке операционного состояния (config false), которая создаётся и удаляется как прямое следствие некоторых конфигурационных изменений (см. параграф 4.1).

2.2. Диаграммы деревьев

Диаграммы деревьев в этом документе используют обозначения, определённые в [RFC8340].

2.3. Префиксы имён узлов данных

В этом документе имена узлов данных, действий и других объектов модели данных используются без префиксов, если из контекста очевидно, в каком модуле YANG они определены. В остальных случаях имена указываются со стандартным префиксом соответствующего модуля YANG (Таблица 1).

Таблица 1. Префиксы и модули YANG.

Префикс

Модуль YANG

Документ

if

ietf-interfaces

[RFC8343]

ip

ietf-ip

[RFC8344]

rt

ietf-routing

Раздел 7

v4ur

ietf-ipv4-unicast-routing

Раздел 8

v6ur

ietf-ipv6-unicast-routing

Раздел 9

yang

ietf-yang-types

[RFC6991]

inet

ietf-inet-types

[RFC6991]

3. Цели

При разработке ядра модели данных маршрутизации преследовались указанные ниже цели.

  • Модели данных следует поддерживать работу с основными семействами адресов (в частности, IPv4 и IPv6) для индивидуальной (unicast) и групповой (multicast) маршрутизации, а также многопротокольной коммутации по меткам (Multiprotocol Label Switching или MPLS).

  • Простым системам маршрутизации IP (например, применяющим лишь статические маршруты) следует быть настраиваемыми в идеальном случае без необходимости разрабатывать дополнительные модули YANG.

  • Схема ядра маршрутизации должна допускать сложные реализации, включающие множество RIB и протоколов плоскости управления, а также управляемое распространение маршрутной информации.

  • Поскольку производитель устройств захотят сопоставить модели данных, основанные на базовой схеме, с фирменными моделями данных и интерфейсами настройки, схеме следует быть достаточно гибкой, чтобы облегчить такое сопоставление и воспринимать модели данных с иной логикой.

4. Устройство ядра модели данных маршрутизации

Модель данных ядра маршрутизации состоит из трёх модулей YANG и одного субмодуля. Модуль ietf-routing определяет базовые компоненты системы маршрутизации. Два других модуля — ietf-ipv4-unicast-routing и ietf-ipv6-unicast-routing — дополняют ietf-routing узлами данных для индивидуальной маршрутизации IPv4 и IPv6, соответственно. Модуль ietf-ipv6-unicast-routing имеет субмодуль ietf-ipv6-router-advertisements, дополняющий модули ietf-interfaces [RFC8343] и ietf-ip [RFC8344] переменными конфигурации для анонсов IPv6 RA, требуемыми [RFC4861].

+--rw routing
   +--rw router-id?                 yang:dotted-quad
   +--ro interfaces
   |  +--ro interface*   if:interface-ref
   +--rw control-plane-protocols
   |  +--rw control-plane-protocol* [type name]
   |     +--rw type             identityref
   |     +--rw name             string
   |     +--rw description?     string
   |     +--rw static-routes
   |        +--rw v4ur:ipv4
   |        |     ...
   |        +--rw v6ur:ipv6
   |              ...
   +--rw ribs
      +--rw rib* [name]
         +--rw name              string
         +--rw address-family?   identityref
         +--ro default-rib?      boolean {multiple-ribs}?
         +--ro routes
         |  +--ro route*
         |        ...
         +---x active-route
         |  +---w input
         |  |  +---w v4ur:destination-address?   inet:ipv4-address
         |  |  +---w v6ur:destination-address?   inet:ipv6-address
         |  +--ro output
         |        ...
         +--rw description?      string

Рисунок 1. Иерархия данных.


На рисунке 1 дано краткое представление иерархий, а полные деревья данных приведены в Приложении A. Как можно видеть на рисунке 1, модель данных ядра маршрутизации вводит несколько базовых элементов схемы маршрутизации — маршруты, RIB со списками маршрутов, протоколы плоскости управления. Эти элементы более подробно описаны в разделе 5.

4.1. Контролируемые системой и пользователем записи списков

Модель данных ядра маршрутизации определяет несколько списков в дереве схемы (таких как rib), которые в корректно работающем устройстве должны включать как минимум одну запись, а клиент может добавлять записи. В таком списке сервер создаёт требуемый элемент как контролируемую системой запись рабочего состояния, т. е. доступных лишь для чтения списков в контейнере routing. Например, в Приложении D список /routing/ribs/rib имеет 2 контролируемых системой записи — ipv4-master и ipv6-master.

Дополнительные записи, контролируемые пользователем, могут быть созданы в конфигурации клиентом, например, с помощью протокола NETCONF (Network Configuration Protocol). Если сервер воспринимает контролируемую пользователем запись, она тоже появляется появляется в рабочей версии списка.

Соответствующие записи обеих версий списка (предполагаемая конфигурация и рабочее состояние) [RFC8342] имеют одно значение ключа списка.

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

Удаление контролируемой пользователем записи из предполагаемой конфигурации ведёт к удалению соответствующей записи из списка рабочего состояния. Если клиент удаляет контролируемую системой запись из предполагаемой конфигурации, удаляется лишь дополнительная конфигурация, заданная удаляемой записью, а соответствующая запись рабочего состояния остаётся.

5. Базовые элементы

В этом разделе описаны важные компоненты модели данных ядра маршрутизации.

5.1. Маршруты

Маршруты являются базовыми элементами системы маршрутизации. Модель данных ядра маршрутизации определяет лишь минимальный набор атрибутов маршрута, перечисленных ниже.

destination-prefix — префикс адресата

Адресный префикс, задающий набор адресатов, для которых может применяться маршрут. Обязательный атрибут.

route-preference — предпочтительность маршрута

Целое число, называемое также административной дистанцией (administrative distance), которое служит для выбора предпочтительного маршрута из числа ведущих к префиксу. Меньшее значение указывает более предпочтительный маршрут.

next-hop — следующий узел пересылки

Определяет выходной интерфейс и/или адрес(а) следующего узла пересылки (next-hop) или выполняемую для пакета специальную операцию.

Маршруты в первую очередь являются состояниями системы и отображаются в записях таблиц RIB (параграф 5.2), но могут присутствовать и в данных конфигурации, например, как заданные вручную статические маршруты. В последнем случае настраиваемые атрибуты маршрутов обычно являются подмножеством атрибутов, определённых для RIB.

5.2. Базы RIB

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

В модели данных ядра маршрутизации базы RIB представляются записями в списке /routing/ribs/rib рабочего состояния. Содержимое RIB контролируется и управляется операциями протокола плоскости управления, которые могут приводить к добавлению, удалению и изменению маршрутов. Включаются также операции псевдопротоколов static и/или direct, описанные в параграфе 5.3.1.

Для каждого поддерживаемого семейства адресов одна база RIB должна быть помечена как принятая по умолчанию (default RIB), куда протоколы плоскости управления помещают маршруты по умолчанию.

Простые реализации маршрутизаторов, не анонсирующие свойство multiple-ribs, обычно создают одну контролируемую системой базу RIB для семейства адресов и помечают её как RIB. Более сложные реализации, анонсирующие multiple-ribs, поддерживают несколько RIB на семейство адресов, которые могут служить для маршрутизации на основе правил и других целей.

Для списка rib определено указанное ниже действие (см. параграф 7.15 в [RFC7950]).

  • active-route возвращает активный маршрут RIB для адреса получателя, заданного входным параметром действия.

5.3. Протоколы плоскости управления

Модель данных ядра маршрутизации предоставляет открытую структуру для определения множества экземпляров протоколов плоскости управления, например для протоколов маршрутизации L3. Каждому экземпляру протокола плоскости управления должен назначаться тип, который выводится из базового отождествлений rt:control-plane-protocol. Модель данных ядра маршрутизации определяет два отождествления для псевдопротоколов — direct и static (параграф 5.3.1).

Можно настроить несколько экземпляров протоколов плоскости управления одного типа.

5.3.1. Псевдопротоколы маршрутизации

Модель данных ядра маршрутизации определяет два особых типа протоколов маршрутизации — direct и static. Оба являются псевдопротоколами, что означает привязку к локальному устройству и отсутствие обмена маршрутными данными со смежными маршрутизаторами.

Каждая реализация модели данных ядра маршрутизации должна обеспечивать один экземпляр псевдопротокола типа direct, являющийся источником прямых маршрутов для всех настроенных семейств адресов. Прямые маршруты обычно предоставляет ядро операционной системы на основе адресов сетевых интерфейсов (параграф 6.2).

Псевдопротокол типа static позволяет задать маршруты вручную. Его можно (но не требуется) настроить в нескольких экземплярах, хотя в типичных конфигурациях применяется лишь один экземпляр.

5.3.2. Определение новых протоколов плоскости управления

Предполагается, что будут разработаны модули YANG с моделями данных для дополнительных типов протоколов плоскости управления. Такие модули будут определять узлы данных для конкретных протоколов и интегрировать их с моделью ядра маршрутизации, как описано ниже.

  • Должны определяться новые отождествления для протоколов плоскости управления, а в качестве базового для них должен устанавливаться идентификатор rt:control-plane-protocol или производный от него.

  • Могут определяться дополнительные атрибуты маршрутов, предпочтительно в одном месте, путём задания группировок YANG. Новые атрибуты должны задаваться путём дополнения определений узлов /rt:routing/rt:ribs/rt:rib/rt:routes/rt:route, а также могут помещаться в другие места дерева схемы.

  • Узлы данных для новых протоколов могут определяться дополнением узлов данных control-plane-protocol в ветви /routing.

Дополненные узлы данных для нового протокола следует делать условными с помощью оператора when и они будут действительны лишь при совпадении rt:type или rt:source-protocol (или производного от него) с отождествлением нового протокола.

Рекомендуется также инкапсулировать связанные с протоколом узлы данных в контейнер присутствия с подходящим именем. Такой контейнер может содержать обязательные узлы данных, которые в ином случае будут запрещены на верхнем уровне дополнения.

Указанные выше шаги реализованы в примере модуля YANG для протокола RIP (Routing Information Protocol), приведённом в Приложении C.

5.4. Параметры анонсов маршрутизаторов IPv6

Модуль YANG ietf-ipv6-router-advertisements (параграф 9.1) с субмодулем ietf-ipv6-unicast-routing дополняет дерево схемы интерфейсов IPv6 определениями приведённых ниже переменных, требуемых параграфом 6.2.1 [RFC4861].

  • send-advertisements;

  • max-rtr-adv-interval;

  • min-rtr-adv-interval;

  • managed-flag;

  • other-config-flag;

  • link-mtu;

  • reachable-time;

  • retrans-timer;

  • cur-hop-limit;

  • default-lifetime;

  • prefix-list (список префиксов для анонсирования).

С каждым префиксом в списке связаны приведённые ниже параметры.

  • valid-lifetime;

  • on-link-flag;

  • preferred-lifetime;

  • autonomous-flag.

Примечания.

  1. Флаг IsRouter, требуемый [RFC4861], реализован в модуле ietf-ip [RFC8344] (лист ip:forwarding).

  2. Спецификация обнаружения соседей (ND) [RFC4861] позволяет реализациям выбирать для параметров valid-lifetime и preferred-lifetime сохранение в последующих анонсах или их уменьшение в реальном масштабе времени. Однако второй вариант проблематичен, поскольку значения могут быть сброшены в (большие) настроенные значения после перезагрузки конфигурации. Кроме того, не известно ни одной реализации, использующей декрементирование. Поэтому в субмодуле ietf-ipv6-router-advertisements применяется первый вариант с постоянными значениями.

6. Взаимодействия с другими модулями YANG

Семантика модели данных ядра маршрутизации зависит также от нескольких параметров конфигурации, определённых в других модулях YANG.

6.1. Модуль ietf-interfaces

Приведённый ниже логический переключатель определён в модуле YANG ietf-interfaces [RFC8343].

/if:interfaces/if:interface/if:enabled

При установке значения false для интерфейса сетевого уровня на этом интерфейсе должны отключаться все функции маршрутизации и пересылки пакетов.

6.2. Модуль ietf-ip

Приведённые ниже логические переключатели определены в модуле YANG ietf-ip [RFC8344].

/if:interfaces/if:interface/ip:ipv4/ip:enabled

При установке значения false для интерфейса сетевого уровня на этом интерфейсе должны отключаться все функции маршрутизации и пересылки пакетов IPv4.

/if:interfaces/if:interface/ip:ipv4/ip:forwarding

При установке значения false для интерфейса сетевого уровня пересылка дейтаграмм IPv4 через этот интерфейс должна быть отключена. Однако интерфейс может участвовать в других функциях маршрутизации IPv4, например, в протоколах маршрутизации.

/if:interfaces/if:interface/ip:ipv6/ip:enabled

При установке значения false для интерфейса сетевого уровня на этом интерфейсе должны отключаться все функции маршрутизации и пересылки пакетов IPv6.

/if:interfaces/if:interface/ip:ipv6/ip:forwarding

При установке значения false для интерфейса сетевого уровня пересылка дейтаграмм IPv6 через этот интерфейс должна быть отключена. Однако интерфейс может участвовать в других функциях маршрутизации IPv6, например, в протоколах маршрутизации.

В дополнение к этому модуль ietf-ip позволяет настраивать адреса и префиксы или маски IPv4 и IPv6 на интерфейсах сетевого уровня. Настройка этих параметров на включённом интерфейсе должна приводить к немедленному созданию соответствующего прямого маршрута. Префикс адресата для этого маршрута устанавливается в соответствии с настроенным адресом IP и префиксом или маской сети, а интерфейс устанавливается как выходной для маршрута.

7. Модуль управления маршрутизацией

   <CODE BEGINS> file "ietf-routing@2018-03-13.yang"

   module ietf-routing {
     yang-version "1.1";
     namespace "urn:ietf:params:xml:ns:yang:ietf-routing";
     prefix "rt";

     import ietf-yang-types {
       prefix "yang";
     }

     import ietf-interfaces {
       prefix "if";
       description
         "Версия модуля ietf-interfaces, совместимая с архитектурой
          хранилища данных сетевого управления (NMDA), нужна для работы";
     }

     organization
       "IETF NETMOD (Network Modeling) Working Group";
     contact
       "WG Web:   <https://datatracker.ietf.org/wg/netmod/>
        WG List:  <mailto:rtgwg@ietf.org>

        Editor:   Ladislav Lhotka
                  <mailto:lhotka@nic.cz>
                  Acee Lindem
                  <mailto:acee@cisco.com>
                  Yingzhen Qu
                  <mailto:yingzhen.qu@huawei.com>";

     description
       "Этот модуль YANG определяет важные компоненты для управления
        подсистемой маршрутизации. Модель полностью соответствует 
        архитектуре хранилища данных управления сетью (NMDA).

        Авторские права (Copyright (c) 2018) принадлежат IETF Trust и
        лицам, указанным как авторы. Все права защищены.

        Распространение и применение модуля в исходной или двоичной 
        форме с изменениями или без таковых разрешено в соответствии с
        лицензией Simplified BSD License, изложенной в параграфе 4.c
        IETF Trust's Legal Provisions Relating to IETF Documents
        (https://trustee.ietf.org/license-info). 

        Эта версия модуля YANG является частью RFC 8349, где правовые
        аспекты приведены более полно.";
     revision 2018-03-13 {
       description
         "Версия для архитектуры хранилища данных управления сетью (NMDA).";
       reference
         "RFC 8349: A YANG Data Model for Routing Management
                    (NMDA Version)";
     }

     revision 2016-11-04 {
          description
            "Первая версия.";
          reference
            "RFC 8022: A YANG Data Model for Routing Management";
     }

     /* Свойства */
     feature multiple-ribs {
       description
         "Это свойство показывает, что сервер поддерживает определённые
          пользователем базы RIB.

          Серверам, не анонсирующим это свойство, СЛЕДУЕТ предоставлять
          в точности 1 контролируемую системой RIB на поддерживаемое
          семейство адресов и делать её default RIB. Эта RIB появляется
          как запись в списке /routing/ribs/rib.";
     }

     feature router-id {
       description
         "Показывает, что сервер поддерживает явный 32-битовый идентификатор
          router ID, используемый некоторыми протоколами маршрутизации.

          Серверы, не поддерживающие это свойство, устанавливают router ID
          алгоритмически, обычно выбирая один из настроенных адресов IPv4.
          Однако алгоритм зависит от реализации.";
     }

     /* Отождествления */

     identity address-family {
       description
         "Базовое отождествление из которого выводятся отождествления
          семейств адресов.";
     }

     identity ipv4 {
       base address-family;
       description
         "Представляет семейство адресов IPv4.";
     }

     identity ipv6 {
       base address-family;
       description
         "Представляет семейство адресов IPv6.";
     }

     identity control-plane-protocol {
       description
         "Базовое отождествление из которого выводятся отождествления
          протоколов плоскости управления.";
     }

     identity routing-protocol {
       base control-plane-protocol;
       description
         "Отождествление, из которого выводятся отождествления 
          протоколов маршрутизации L3.";
     }

     identity direct {
       base routing-protocol;
       description
         "Псевдопротокол маршрутизации, обеспечивающий маршруты в 
          непосредственно подключённые сети.";
     }

     identity static {
       base routing-protocol;
       description
         "Псевдопротокол статической маршрутизации (Static).";
     }

     /* Определения типов */

     typedef route-preference {
       type uint32;
       description
         "Применяется для предпочтений маршрута.";
     }

     /* Группировки */

     grouping address-family {
       description
         "Идентификация листа семейства адресов.";
       leaf address-family {
         type identityref {
           base address-family;
         }
         mandatory true;
         description
           "Семейство адресов.";
       }
     }

     grouping router-id {
       description
         "Обеспечивает значение router ID.";
       leaf router-id {
         type yang:dotted-quad;
         description
           "32-битовое число в формате с разделением байтов точками, 
            служащее некоторым протоколам маршрутизации для идентификации
            маршрутизатора.";
         reference
           "RFC 2328: OSPF Version 2";
       }
     }

     grouping special-next-hop {
       description
         "Лист с перечислением специальных next hop.";
       leaf special-next-hop {
         type enumeration {
           enum blackhole {
             description
               "Отбросить пакет без уведомления.";
           }
           enum unreachable {
             description
               "Отбросить пакет с передачей отправителю сообщения об
                ошибке, указывающего недоступность адресата.";
           }
           enum prohibit {
             description
               " Отбросить пакет с передачей отправителю сообщения об
                ошибке, указывающего административный запрет коммуникаций.";

           }
           enum receive {
             description
               "Пакет был получен локальной системой.";
           }
         }
         description
           "Опции для специальных next hop.";
       }
     }

     grouping next-hop-content {
       description
         "Базовые параметры для next hop в статических маршрутах.";
       choice next-hop-options {
         mandatory true;
         description
           "Опции для next hop в статических маршрутах.

            Предполагается добавление других случаев с помощью операторов
            augment в других модулях.";
         case simple-next-hop {
           description
             "Представляет простой вариант next hop, состоящий из адреса
              и/или выходного интерфейса.

              Модули для семейств адресов ДОЛЖНЫ дополнять этот вариант
              листом с адресом next-hop из данного семейства.";
           leaf outgoing-interface {
             type if:interface-ref;
             description
               "Имя выходного интерфейса.";
           }
         }
         case special-next-hop {
           uses special-next-hop;
         }
         case next-hop-list {
           container next-hop-list {
             description
               "Контейнер для нескольких next hop.";
             list next-hop {
               key "index";
               description
                 "Запись в списке next-hop.

                  Модули для семейств адресов ДОЛЖНЫ дополнять этот 
                  список листом, с адресом next-hop своего семейства.";
               leaf index {
                 type string;
                 description
                   "Заданный пользователем идентификатор, применяемый 
                    для однозначного указания записи next-hop в списке.
                    Значение этого индекса не имеет семантического смысла
                    кроме ссылки на запись списка.";
               }
               leaf outgoing-interface {
                 type if:interface-ref;
                 description
                   "Имя выходного интерфейса.";
               }
             }
           }
         }
       }
     }

     grouping next-hop-state-content {
       description
         "Базовые параметры состояния next hop.";
       choice next-hop-options {
         mandatory true;
         description
           "Опции для next hop.

            Предполагается добавление других опций с помощью операторов
            augment из других модулей, например, для рекурсии next hop.";
         case simple-next-hop {
           description
             "Это представляет единственную запись next hop с адресом 
              next-hop и/или выходным интерфейсом.

              Модули для семейств адресов ДОЛЖНЫ дополнять этот список
              адресом next-hop из своего семейства.";
           leaf outgoing-interface {
             type if:interface-ref;
             description
               "Имя выходного интерфейса.";
           }
         }
         case special-next-hop {
           uses special-next-hop;
         }
         case next-hop-list {
           container next-hop-list {
             description
               "Контейнер для нескольких next hop.";
             list next-hop {
               description
                 "Запись в списке next-hop.

                  Модули для семейств адресов ДОЛЖНЫ дополнять этот 
                  список адресом next-hop из своего семейства.";
               leaf outgoing-interface {
                 type if:interface-ref;
                 description
                   "Имя выходного интерфейса .";
               }
             }
           }
         }
       }
     }

     grouping route-metadata {
       description
         "Базовые метаданные маршрута.";
       leaf source-protocol {
         type identityref {
           base routing-protocol;
         }
         mandatory true;
         description
           "Тип протокола маршрутизации, создавшего маршрут.";
       }
       leaf active {
         type empty;
         description
           "Наличие этого листа указывает, что маршрут предпочтительней
            других маршрутов в данному префиксу в той же базе RIB.";
       }
       leaf last-updated {
         type yang:date-and-time;
         description
           "Время последнего изменения маршрута. Если маршрут никогда не
            менялся, это будет время вставки маршрута в RIB.";
       }
     }

     /* Узлы данных */

     container routing {
       description
         "Параметры конфигурации подсистемы маршрутизации.";
       uses router-id {
         if-feature "router-id";
         description
           "Поддержка глобального router ID. Протоколы маршрутизации,
            применяющие router ID, могут использовать или переопределить
            этот параметр.";
       }
       container interfaces {
         config false;
         description
           "Интерфейсы сетевого уровня, применяемые для маршрутизации.";
         leaf-list interface {
           type if:interface-ref;
           description
             "Каждая запись указывает имя настроенного интерфейса
              сетевого уровня.";
         }
       }
       container control-plane-protocols {
         description
           "Поддержка экземпляров протокола плоскости управления.";
         list control-plane-protocol {
           key "type name";
           description
             "Каждая запись содержит экземпляр протокола плоскости управления.";
           leaf type {
             type identityref {
               base control-plane-protocol;
             }
             description
               "Тип протокола плоскости управления - отождествления, 
                выведенное из базового отождествления control-plane-protocol.";
           }
           leaf name {
             type string;
             description
               "Произвольное имя экземпляра протокола плоскости управления.";
           }

           leaf description {
             type string;
             description
               "Текстовое описание экземпляра протокола плоскости управления.";
           }
           container static-routes {
             when "derived-from-or-self(../type, 'rt:static')" {
               description
                 "Этот контейнер действителен лишь для протокола static.";
             }
             description
               "Поддержка псевдопротокола static.

                Модули для семейств адресов дополняют этот узел
                своими списками маршрутов.";
           }
         }
       }
       container ribs {
         description
           "Поддержка для баз RIB.";
         list rib {
           key "name";
           description
             "Каждая запись содержит конфигурацию для базы RIB, 
              указанной ключом name.

              Записи с тем же ключом, что и у управляемой системой
              записи в списке /routing/ribs/rib, применяются для 
              настройки параметров этой записи. Остальные определяют
              дополнительные RIB, управляемые пользователем.";
           leaf name {
             type string;
             description
               "Имя RIB.

                Для управляемых системой записей значение этого листа
                должно совпадать с именем соответствующей записи в
                рабочем состоянии.

                Для контролируемых пользователем записей можно задавать
                любое имя.";
           }
           uses address-family {
             description
               "Семейство адресов управляемой системой RIB.";
           }

           leaf default-rib {
             if-feature "multiple-ribs";
             type boolean;
             default "true";
             config false;
             description
               "Флаг, имеющий значение true лишь для принятой по 
                умолчанию RIB в данном семействе адресов.

                По умолчанию протоколы плоскости управления 
                помещают свои маршруты в принятые по умолчанию RIB.";
           }
           container routes {
             config false;
             description
               "Текущее содержимое RIB.";
             list route {
               description
                 "Маршрутная запись RIB. Этот узел данных ДОЛЖЕН 
                  дополняться сведениями для маршрутов каждого
                  семейства адресов.";
               leaf route-preference {
                 type route-preference;
                 description
                   "Этот атрибут маршрута, называемый также administrative
                    distance, позволяет выбрать предпочтительный маршрут 
                    из числа ведущих к одному префиксу. Меньшее значение 
                    указывает более предпочтительный маршрут.";
               }
               container next-hop {
                 description
                   "Атрибут next-hop для маршрута.";
                 uses next-hop-state-content;
               }
               uses route-metadata;
             }
           }
           action active-route {
             description
               "Возвращает активный маршрут RIB, используемый для 
                адреса получателя.

                Модули для семейств адресов ДОЛЖНЫ дополнять входные 
                параметры листом destination-address.";
             output {
               container route {
                 description
                   "Активный маршрут RIB для заданного адресата.

                    Если в RIB нет маршрута к адресату, вывод будет пустым.

                    Модули для семейств адресов ДОЛЖНЫ дополнять этот
                    контейнер соответствующим содержимым маршрута.";
                 container next-hop {
                   description
                     "Атрибут next-hop для маршрута .";
                   uses next-hop-state-content;
                 }
                 uses route-metadata;
               }
             }
           }
           leaf description {
             type string;
             description
               "Текстовое описание RIB.";
           }
         }
       }
     }

     /*
      * Перечисленные ниже узлы данных признаны устаревшими и отменены
      * архитектурой хранилищ данных управления сетью NMDA, описанной
      * в RFC 8342.
      */
     container routing-state {
       config false;
       status obsolete;
       description
         "Данные состояния для подсистемы маршрутизации.";
       uses router-id {
         status obsolete;
         description
           "Глобальный идентификатор router ID.

            Может настраиваться или автоматически выбираться реализацией.";
       }
       container interfaces {
         status obsolete;
         description
           "Интерфейсы сетевого уровня, применяемые для маршрутизации.";
         leaf-list interface {
           type if:interface-state-ref;
           status obsolete;
           description
             "Каждая запись указывает имя интерфейса сетевого уровня.";
         }
       }
       container control-plane-protocols {
         status obsolete;
         description
           "Контейнер для списка экземпляров протоколов маршрутизации.";
         list control-plane-protocol {
           key "type name";
           status obsolete;
           description
             "Данные состояния для экземпляра протокола плоскости управления.

              Реализация ДОЛЖНА предоставлять в точности 1 управляемый 
              системой экземпляр псевдопротокола direct. Конфигурация МОЖЕТ
              создавать другие экземпляры протоколов плоскости управления.";
           leaf type {
             type identityref {
               base control-plane-protocol;
             }
             status obsolete;
             description
               "Тип протокола плоскости управления.";
           }
           leaf name {
             type string;
             status obsolete;
             description
               "Имя экземпляра протокола плоскости управления.

                Для управляемых системой экземпляров это имя является 
                постоянным, т. е. его НЕ СЛЕДУЕТ менять при перезагрузке.";
           }
         }
       }
       container ribs {
         status obsolete;
         description
           "Контейнер для таблиц RIB.";
         list rib {
           key "name";
           min-elements 1;
           status obsolete;
           description
             "Каждая запись представляет RIB, указанную ключом name. Все'
              маршруты в RIB ДОЛЖНЫ относиться к одному семейству адресов.

              Реализации СЛЕДУЕТ обеспечивать одну управляемую системой
              базу RIB для каждого поддерживаемого семейства адресов.";
           leaf name {
             type string;
             status obsolete;
             description
               "Имя RIB.";
           }
           uses address-family {
             status obsolete;
             description
               "Семейство адресов RIB.";
           }
           leaf default-rib {
             if-feature "multiple-ribs";
             type boolean;
             default "true";
             status obsolete;
             description
               "Флаг, имеющий значение true лишь в случае, когда RIB
                является default RIB для данного семейства адресов.

                По умолчанию протоколы плоскости управления помещают
                свои маршруты в default RIB.";
           }
           container routes {
             status obsolete;
             description
               "Текущее содержимое RIB.";
             list route {
               status obsolete;
               description
                 "Запись маршрута в RIB. Этот узел данных ДОЛЖЕН 
                  дополняться данными для маршрутов каждого семейства
                  адресов.";
               leaf route-preference {
                 type route-preference;
                 status obsolete;
                 description
                   "Этот атрибут маршрута, называемый также administrative
                    distance, позволяет выбрать предпочтительный маршрут 
                    из числа ведущих к одному префиксу. Меньшее значение 
                    указывает более предпочтительный маршрут.";
               }
               container next-hop {
                 status obsolete;
                 description
                   "Атрибут next-hop для маршрута.";
                 uses next-hop-state-content {
                   status obsolete;
                   description
                     "Рабочее состояние атрибута next-hop для маршрута.";
                 }
               }
               uses route-metadata {
                 status obsolete;
                 description
                   "Метаданные маршрута.";
               }
             }
           }
           action active-route {
             status obsolete;
             description
               "Возвращает активный маршрут RIB, используемый для
                адреса получателя.

                Модули для семейств адресов ДОЛЖНЫ дополнять входные
                параметры листом destination-address.";
             output {
               container route {
                 status obsolete;
                 description
                   "Активный маршрут RIB для указанного адресата.

                    При отсутствии в RIB маршрута к адресату ничего
                    не выводится.

                    Модули для семейств адресов ДОЛЖНЫ дополнять этот
                    контейнер соответствующим содержимым маршрутов.";
                 container next-hop {
                   status obsolete;
                   description
                     "Атрибут next-hop для маршрута.";
                   uses next-hop-state-content {
                     status obsolete;
                     description
                       "Данные состояния активного маршрута.";
                   }
                 }
                 uses route-metadata {
                   status obsolete;
                   description
                     "Метаданные активного маршрута.";
                 }
               }
             }
           }
         }
       }
     }
   }

   <CODE ENDS>

8. Модуль управления маршрутизацией IPv4

   <CODE BEGINS> file "ietf-ipv4-unicast-routing@2018-03-13.yang"

   module ietf-ipv4-unicast-routing {
     yang-version "1.1";
     namespace
       "urn:ietf:params:xml:ns:yang:ietf-ipv4-unicast-routing";
     prefix "v4ur";

     import ietf-routing {
       prefix "rt";
       description
         "Для работы требуется версия модуля ietf-routing, совместимая 
          с архитектурой хранилищ данных управления сетью (NMDA).";
     }

     import ietf-inet-types {
       prefix "inet";
     }
     organization
       "IETF NETMOD (Network Modeling) Working Group";
     contact
       "WG Web:   <https://datatracker.ietf.org/wg/netmod/>
        WG List:  <mailto:rtgwg@ietf.org>

        Editor:   Ladislav Lhotka
                  <mailto:lhotka@nic.cz>

                  Acee Lindem
                  <mailto:acee@cisco.com>
                  Yingzhen Qu
                  <mailto:yingzhen.qu@huawei.com>";

     description
       "Этот модуль YANG дополняет модуль ietf-routing базовыми параметрами
        для индивидуальной маршрутизации IPv4. Модель полностью соответствует
        архитектуре хранилищ данных управления сетью (NMDA).

        Авторские права (Copyright (c) 2018) принадлежат IETF Trust и
        лицам, указанным как авторы. Все права защищены.

        Распространение и применение модуля в исходной или двоичной 
        форме с изменениями или без таковых разрешено в соответствии с
        лицензией Simplified BSD License, изложенной в параграфе 4.c
        IETF Trust's Legal Provisions Relating to IETF Documents
        (https://trustee.ietf.org/license-info). 

        Эта версия модуля YANG является частью RFC 8349, где правовые
        аспекты приведены более полно.";

     revision 2018-03-13 {
       description
         "Версия для архитектуры хранилища данных управления сетью (NMDA).";
       reference
         "RFC 8349: A YANG Data Model for Routing Management
                    (NMDA Version)";
     }

     revision 2016-11-04 {
          description
            "Исходный выпуск.";
          reference
            "RFC 8022: A YANG Data Model for Routing Management";
     }

     /* Отождествления */

     identity ipv4-unicast {
       base rt:ipv4;
       description
         "Отождествление семейства индивидуальных адресов IPv4.";
     }

     augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route" {
       when "derived-from-or-self(../../rt:address-family, "
          + "'v4ur:ipv4-unicast')" {
         description
           "Это дополнение действительно лишь для IPv4 unicast.";
       }
       description
         "Этот лист дополняет индивидуальный маршрут IPv4.";
       leaf destination-prefix {
         type inet:ipv4-prefix;
         description
           "Префикс адресата IPv4.";
       }
     }

     augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route/"
           + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" {
       when "derived-from-or-self(../../../rt:address-family, "
          + "'v4ur:ipv4-unicast')" {
         description
           "Это дополнение действительно лишь для IPv4 unicast.";
       }
       description
         "Дополнение к simple-next-hop для маршрутов IPv4 unicast.";
       leaf next-hop-address {
         type inet:ipv4-address;
         description
           "Адрес IPv4 для следующего маршрутизатора (next hop).";
       }
     }

     augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route/"
           + "rt:next-hop/rt:next-hop-options/rt:next-hop-list/"
           + "rt:next-hop-list/rt:next-hop" {
       when "derived-from-or-self(../../../../../rt:address-family, "
          + "'v4ur:ipv4-unicast')" {
         description
           "Это дополнение действительно лишь для IPv4 unicast.";
       }
       description
         "Дополнение к next-hop-list для маршрутов IPv4 unicast.";
       leaf address {
         type inet:ipv4-address;
         description
           "Адрес IPv4 для следующего маршрутизатора (next hop).";
       }
     }

     augment
       "/rt:routing/rt:ribs/rt:rib/rt:active-route/rt:input" {
       when "derived-from-or-self(../rt:address-family, "
          + "'v4ur:ipv4-unicast')" {
         description
           "Это дополнение действительно лишь для IPv4 unicast RIB.";
       }
       description
         "Дополняет входной параметр действия active-route.";
       leaf destination-address {
         type inet:ipv4-address;
         description
           "Адрес получателя IPv4.";
       }
     }

     augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/"
           + "rt:output/rt:route" {
       when "derived-from-or-self(../../rt:address-family, "
          + "'v4ur:ipv4-unicast')" {
         description
           "Это дополнение действительно лишь для IPv4 unicast.";
       }
       description
         "Добавляет префикс адресата к отклику действия active-route.";
       leaf destination-prefix {
         type inet:ipv4-prefix;
         description
           "Префикс адресата IPv4.";
       }
     }

     augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/"
           + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/"
           + "rt:simple-next-hop" {
       when "derived-from-or-self(../../../rt:address-family, "
          + "'v4ur:ipv4-unicast')" {
         description
           "Это дополнение действительно лишь для IPv4 unicast.";
       }
       description
         "Дополняет вариант simple-next-hop в ответе на действие
          active-route.";
       leaf next-hop-address {
         type inet:ipv4-address;
         description
           "Адрес IPv4 для следующего маршрутизатора (next hop).";
       }
     }

     augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/"
           + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/"
           + "rt:next-hop-list/rt:next-hop-list/rt:next-hop" {
       when "derived-from-or-self(../../../../../rt:address-family, "
          + "'v4ur:ipv4-unicast')" {
         description
           "Это дополнение действительно лишь для IPv4 unicast.";
       }
       description
         "Дополняет вариант next-hop-list в ответе на действие
          active-route.";
       leaf next-hop-address {
         type inet:ipv4-address;
         description
           "Адрес IPv4 для следующего маршрутизатора (next hop).";
       }
     }

     augment "/rt:routing/rt:control-plane-protocols/"
           + "rt:control-plane-protocol/rt:static-routes" {
       description
         "Дополнение определяет псевдопротокол static с данными,
          относящимися к IPv4 unicast.";
       container ipv4 {
         description
           "Поддержка экземпляра псевдопротокола static со списком
            маршрутов.";
         list route {
           key "destination-prefix";
           description
             "Список статических маршрутов.";
           leaf destination-prefix {
             type inet:ipv4-prefix;
             mandatory true;
             description
               "Префикс адресата IPv4.";
           }
           leaf description {
             type string;
             description
               "Текстовое описание маршрута.";
           }
           container next-hop {
             description
               "Поддержка для next-hop.";
             uses rt:next-hop-content {
               augment "next-hop-options/simple-next-hop" {
                 description
                   "Дополнение варианта simple-next-hop в статических 
                    маршрутах IPv4.";
                 leaf next-hop-address {
                   type inet:ipv4-address;
                   description
                     "Адрес IPv4 для следующего маршрутизатора (next hop).";
                 }
               }
               augment "next-hop-options/next-hop-list/next-hop-list/"
                     + "next-hop" {
                 description
                   "Дополнение варианта next-hop-list в статических 
                   маршрутах IPv4.";
                 leaf next-hop-address {
                   type inet:ipv4-address;
                   description
                     "Адрес IPv4 для следующего маршрутизатора (next hop).";
                 }
               }
             }
           }
         }
       }
     }

     /*
      * Перечисленные ниже узлы данных признаны устаревшими и отменены
      * архитектурой хранилищ данных управления сетью NMDA, описанной
      * в RFC 8342.
      */
     augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route" {
       when "derived-from-or-self(../../rt:address-family, "
            + "'v4ur:ipv4-unicast')" {
         description
           "Это дополнение действительно лишь для IPv4 unicast.";
       }
       status obsolete;
       description
         "Этот лист дополняет индивидуальный маршрут IPv4.";
       leaf destination-prefix {
         type inet:ipv4-prefix;
         status obsolete;
         description
           "Префикс получателя IPv4.";
       }
     }
     augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/"
             + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" {
       when "derived-from-or-self(
               ../../../rt:address-family, 'v4ur:ipv4-unicast')" {
         description
           "Это дополнение действительно лишь для IPv4 unicast.";
       }
       status obsolete;
       description
         "Дополняет вариант simple-next-hop в маршрутах IPv4 unicast.";
       leaf next-hop-address {
         type inet:ipv4-address;
         status obsolete;
         description
           "Адрес IPv4 для следующего маршрутизатора (next hop).";
       }
     }
     augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/"
             + "rt:next-hop/rt:next-hop-options/rt:next-hop-list/"
             + "rt:next-hop-list/rt:next-hop" {
       when "derived-from-or-self(../../../../../rt:address-family,
               'v4ur:ipv4-unicast')" {
         description
           "Это дополнение действительно лишь для IPv4 unicast.";
       }
       status obsolete;
       description
         "Дополняет вариант next-hop-list в маршрутах IPv4 unicast.";
       leaf address {
         type inet:ipv4-address;
         status obsolete;
         description
           "Адрес IPv4 для следующего маршрутизатора (next hop).";
       }
     }
     augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/"
             + "rt:input" {
       when "derived-from-or-self(../rt:address-family,
               'v4ur:ipv4-unicast')" {
         description
           "Это дополнение действительно лишь для IPv4 unicast.";
       }
       status obsolete;
       description
         "Добавляет входной параметр для действия active-route.";
       leaf destination-address {
         type inet:ipv4-address;
         status obsolete;
         description
           "Адрес получателя IPv4.";
       }
     }
     augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/"
             + "rt:output/rt:route" {
       when "derived-from-or-self(../../rt:address-family,
               'v4ur:ipv4-unicast')" {
         description
           "Это дополнение действительно лишь для IPv4 unicast.";
       }
       status obsolete;
       description
         "Добавляет префикс адресата к действию active-route.";
       leaf destination-prefix {
         type inet:ipv4-prefix;
         status obsolete;
         description
           "Префикс адресата IPv4.";
       }
     }
     augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/"
             + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/"
             + "rt:simple-next-hop" {
       when "derived-from-or-self(../../../rt:address-family,
               'v4ur:ipv4-unicast')" {
         description
           "Это дополнение действительно лишь для IPv4 unicast.";
       }
       status obsolete;
       description
         "Дополняет вариант simple-next-hop в отклике на действие
          active-route.";
       leaf next-hop-address {
         type inet:ipv4-address;
         status obsolete;
         description
           "Адрес IPv4 для следующего маршрутизатора (next hop).";
       }
     }
     augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/"
             + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/"
             + "rt:next-hop-list/rt:next-hop-list/rt:next-hop" {
       when "derived-from-or-self(../../../../../rt:address-family,
               'v4ur:ipv4-unicast')" {
         description
           "Это дополнение действительно лишь для IPv4 unicast.";
       }
       status obsolete;
       description
         "Дополняет вариант next-hop-list в отклике на действие
          active-route.";
       leaf next-hop-address {
         type inet:ipv4-address;
         status obsolete;
         description
           "Адрес IPv4 для следующего маршрутизатора (next hop).";
       }
     }
   }

   <CODE ENDS>

9. Модуль управления маршрутизацией IPv6

   <CODE BEGINS> file "ietf-ipv6-unicast-routing@2018-03-13.yang"

   module ietf-ipv6-unicast-routing {
     yang-version "1.1";
     namespace
       "urn:ietf:params:xml:ns:yang:ietf-ipv6-unicast-routing";
     prefix "v6ur";

     import ietf-routing {
       prefix "rt";
       description
         "Для работы требуется версия модуля ietf-routing, совместимая 
          с архитектурой хранилищ данных управления сетью (NMDA).";
     }

     import ietf-inet-types {
       prefix "inet";
       description
         "Для работы требуется версия модуля ietf-interfaces, совместимая 
          с архитектурой хранилищ данных управления сетью (NMDA).";
     }

     include ietf-ipv6-router-advertisements {
       revision-date 2018-03-13;
     }

     organization
       "IETF NETMOD (Network Modeling) Working Group";
     contact
       "WG Web:   <https://datatracker.ietf.org/wg/netmod/> 
        WG List:  <mailto:rtgwg@ietf.org>

        Editor:   Ladislav Lhotka
                  <mailto:lhotka@nic.cz>
                  Acee Lindem
                  <mailto:acee@cisco.com>
                  Yingzhen Qu
                  <mailto:yingzhen.qu@huawei.com>";

     description
       "Этот модуль YANG дополняет модуль ietf-routing базовыми параметрами
        для индивидуальной маршрутизации IPv6. Модель полностью соответствует
        архитектуре хранилищ данных управления сетью (NMDA).

        Авторские права (Copyright (c) 2018) принадлежат IETF Trust и
        лицам, указанным как авторы. Все права защищены.

        Распространение и применение модуля в исходной или двоичной 
        форме с изменениями или без таковых разрешено в соответствии с
        лицензией Simplified BSD License, изложенной в параграфе 4.c
        IETF Trust's Legal Provisions Relating to IETF Documents
        (https://trustee.ietf.org/license-info). 

        Эта версия модуля YANG является частью RFC 8349, где правовые
        аспекты приведены более полно.";

     revision 2018-03-13 {
       description
         "Версия для архитектуры хранилища данных управления сетью (NMDA).";
       reference
         "RFC 8349: A YANG Data Model for Routing Management
                    (NMDA Version)";
     }

     /* Отождествления */

     revision 2016-11-04 {
          description
            "Исходный выпуск.";
          reference
            "RFC 8022: A YANG Data Model for Routing Management";
     }

     identity ipv6-unicast {
       base rt:ipv6;
       description
         "Представляет семейство индивидуальных адресов IPv6.";
     }

     augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route" {
       when "derived-from-or-self(../../rt:address-family, "
          + "'v6ur:ipv6-unicast')" {
         description
           "Это дополнение действительно лишь для IPv6 unicast.";
       }
       description
         "Дополняет индивидуальный маршрут IPv6.";
       leaf destination-prefix {
         type inet:ipv6-prefix;
         description
           "Префикс получателя IPv6.";
       }
     }

     augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route/"
           + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" {
       when "derived-from-or-self(../../../rt:address-family, "
          + "'v6ur:ipv6-unicast')" {
         description
           "Это дополнение действительно лишь для IPv6 unicast.";
       }
       description
         "Дополняет случай simple-next-hop в индивидуальном маршруте IPv6.";
       leaf next-hop-address {
         type inet:ipv6-address;
         description
           "Адрес IPv6 следующего интервала.";
       }
     }

     augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route/"
           + "rt:next-hop/rt:next-hop-options/rt:next-hop-list/"
           + "rt:next-hop-list/rt:next-hop" {
       when "derived-from-or-self(../../../../../rt:address-family, "
          + "'v6ur:ipv6-unicast')" {
         description
           "Это дополнение действительно лишь для IPv6 unicast.";
       }
       description
         "Этот лист дополняет случай next-hop-list индивидуальных
          маршрутов IPv6.";
       leaf address {
         type inet:ipv6-address;
         description
           "Адрес IPv6 следующего интервала.";
       }
     }

     augment
       "/rt:routing/rt:ribs/rt:rib/rt:active-route/rt:input" {
       when "derived-from-or-self(../rt:address-family, "
          + "'v6ur:ipv6-unicast')" {
         description
           "Это дополнение действительно лишь для IPv6 unicast RIB.";
       }
       description
         "Добавляет входной параметр действия active-route.";
       leaf destination-address {
         type inet:ipv6-address;
         description
           "Адрес получателя IPv6.";
       }
     }

     augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/"
           + "rt:output/rt:route" {
       when "derived-from-or-self(../../rt:address-family, "
          + "'v6ur:ipv6-unicast')" {
         description
           "Действительно только для IPv6 unicast.";
       }
       description
         "Добавляет префикс получателя в отклик на действие active-route.";
       leaf destination-prefix {
         type inet:ipv6-prefix;
         description
           "Префикс получателя IPv6.";
       }
     }

     augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/"
           + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/"
           + "rt:simple-next-hop" {
       when "derived-from-or-self(../../../rt:address-family, "
          + "'v6ur:ipv6-unicast')" {
         description
           "Это дополнение действительно лишь для IPv6 unicast.";
       }
       description
         "Дополняет случай simple-next-hop в отклике на действие active-route.";
       leaf next-hop-address {
         type inet:ipv6-address;
         description
           "Адрес IPv6 следующего интервала.";
       }
     }

     augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/"
           + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/"
           + "rt:next-hop-list/rt:next-hop-list/rt:next-hop" {
       when "derived-from-or-self(../../../../../rt:address-family, "
          + "'v6ur:ipv6-unicast')" {
         description
           "Это дополнение действительно лишь для IPv6 unicast.";
       }
       description
         "Дополняет случай next-hop-list в отклике на действие active-route.";
       leaf next-hop-address {
         type inet:ipv6-address;
         description
           "Адрес IPv6 следующего интервала.";
       }
     }

     /* Дополнения узлов данных */

     augment "/rt:routing/rt:control-plane-protocols/"
           + "rt:control-plane-protocol/rt:static-routes" {
       description
         "Определяет псевдопротокол static данными, относящимися к IPv6 unicast.";
       container ipv6 {
         description
           "Поддержка экземпляра псевдопротокола static включает список маршрутов.";
         list route {
           key "destination-prefix";
           description
             "Список статических маршрутов.";
           leaf destination-prefix {
             type inet:ipv6-prefix;
             mandatory true;
             description
               "Префикс получателя IPv6.";
           }
           leaf description {
             type string;
             description
               "Текстовое описание маршрута.";
           }
           container next-hop {
             description
               "Следующий интервал маршрута.";
             uses rt:next-hop-content {
               augment "next-hop-options/simple-next-hop" {
                 description
                   "Дополняет случай simple-next-hop в статическом маршруте IPv6.";
                 leaf next-hop-address {
                   type inet:ipv6-address;
                   description
                     "Адрес IPv6 следующего интервала.";
                 }
               }
               augment "next-hop-options/next-hop-list/next-hop-list/"
                     + "next-hop" {
                 description
                   "Дополняет случай next-hop-list в статическом маршруте IPv6.";
                 leaf next-hop-address {
                   type inet:ipv6-address;
                   description
                     "Адрес IPv6 следующего интервала.";
                 }
               }
             }
           }
         }
       }
     }

     /*
      * Перечисленные ниже узлы данных признаны устаревшими и отменены
      * архитектурой хранилищ данных управления сетью NMDA, описанной
      * в RFC 8342.
      */
     augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route" {
       when "derived-from-or-self(../../rt:address-family,
               'v6ur:ipv6-unicast')" {
         description
           "Это дополнение действительно лишь для IPv6 unicast.";
       }
       status obsolete;
       description
         "Дополняет индивидуальный маршрут IPv6.";
       leaf destination-prefix {
         type inet:ipv6-prefix;
         status obsolete;
         description
           "Префикс получателя IPv6.";
       }
     }
     augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/"
             + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" {
       when "derived-from-or-self(../../../rt:address-family,
               'v6ur:ipv6-unicast')" {
         description
           "Это дополнение действительно лишь для IPv6 unicast.";
       }
       status obsolete;
       description
         "Дополняет случай simple-next-hop в индивидуальном маршруте IPv6.";
       leaf next-hop-address {
         type inet:ipv6-address;
         status obsolete;
         description
           "Адрес IPv6 следующего интервала.";
       }
     }
     augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/"
             + "rt:next-hop/rt:next-hop-options/rt:next-hop-list/"
             + "rt:next-hop-list/rt:next-hop" {
       when "derived-from-or-self(../../../../../rt:address-family,
               'v6ur:ipv6-unicast')" {
         description
           "Это дополнение действительно лишь для IPv6 unicast.";
       }
       status obsolete;
       description
         "Дополняет случай next-hop-list в индивидуальном маршруте IPv6.";
       leaf address {
         type inet:ipv6-address;
         status obsolete;
         description
           "Адрес IPv6 следующего интервала.";
       }
     }
     augment "/rt:routing-state/rt:ribs/rt:rib/"
             + "rt:active-route/rt:input" {
       when "derived-from-or-self(../rt:address-family,
               'v6ur:ipv6-unicast')" {
         description
           "Это дополнение действительно лишь для IPv6 unicast.";
       }
       status obsolete;
       description
         "Добавляет входной параметр действия active-route.";
       leaf destination-address {
         type inet:ipv6-address;
         status obsolete;
         description
           "Адрес получателя IPv6.";
       }
     }
     augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/"
             + "rt:output/rt:route" {
       when "derived-from-or-self(../../rt:address-family,
               'v6ur:ipv6-unicast')" {
         description
           "Это дополнение действительно лишь для IPv6 unicast.";
       }
       status obsolete;
       description
         "Добавляет префикс получателя в отклик на действие active-route.";
       leaf destination-prefix {
         type inet:ipv6-prefix;
         status obsolete;
         description
           "Префикс получателя IPv6.";
       }
     }
     augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/"
             + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/"
             + "rt:simple-next-hop" {
       when "derived-from-or-self(../../../rt:address-family,
               'v6ur:ipv6-unicast')" {
         description
           "Это дополнение действительно лишь для IPv6 unicast.";
       }
       status obsolete;
       description
         "Добавляет случай simple-next-hop в отклике на active-route.";
       leaf next-hop-address {
         type inet:ipv6-address;
         status obsolete;
         description
           "Адрес IPv6 следующего интервала.";
       }
     }
     augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/"
             + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/"
             + "rt:next-hop-list/rt:next-hop-list/rt:next-hop" {
       when "derived-from-or-self(../../../../../rt:address-family,
               'v6ur:ipv6-unicast')" {
         description
           "Это дополнение действительно лишь для IPv6 unicast.";
       }
       status obsolete;
       description
         "Добавляет случай next-hop-list в отклике на active-route.";
       leaf next-hop-address {
         type inet:ipv6-address;
         status obsolete;
         description
           "Адрес IPv6 следующего интервала.";
       }
     }
   }

   <CODE ENDS>

9.1. Субмодуль IPv6 RA

   <CODE BEGINS> file "ietf-ipv6-router-advertisements@2018-03-13.yang"

   submodule ietf-ipv6-router-advertisements {
     yang-version "1.1";

     belongs-to ietf-ipv6-unicast-routing {
       prefix "v6ur";
     }

     import ietf-inet-types {
       prefix "inet";
     }

     import ietf-interfaces {
       prefix "if";
       description
         "Нужна версия модуля ietf-interfaces, совместимая с архитектурой 
          сетевых хранилищ данных (NMDA).";
     }

     import ietf-ip {
       prefix "ip";
       description
         "Нужна версия модуля ietf-ip, совместимая с архитектурой 
          сетевых хранилищ данных (NMDA).";
     }

     organization
       "IETF NETMOD (Network Modeling) Working Group";
     contact
       "WG Web:   <https://datatracker.ietf.org/wg/netmod/>
        WG List:  <mailto:rtgwg@ietf.org>

        Editor:   Ladislav Lhotka
                  <mailto:lhotka@nic.cz>
                  Acee Lindem
                  <mailto:acee@cisco.com>
                  Yingzhen Qu
                  <mailto:yingzhen.qu@huawei.com>";

     description
       "Этот модуль YANG дополняет модуль ietf-ip параметрами для анонсов
        маршрутизаторов IPv6 (RA). Модель полностью соответствует архитектуре
        сетевых хранилищ данных (NMDA).

        Авторские права (Copyright (c) 2018) принадлежат IETF Trust и
        лицам, указанным как авторы. Все права защищены.

        Распространение и применение модуля в исходной или двоичной 
        форме с изменениями или без таковых разрешено в соответствии с
        лицензией Simplified BSD License, изложенной в параграфе 4.c
        IETF Trust's Legal Provisions Relating to IETF Documents
        (https://trustee.ietf.org/license-info). 

        Эта версия модуля YANG является частью RFC 8349, где правовые
        аспекты приведены более полно.";

     reference
       "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";

     revision 2018-03-13 {

       description
         "Выпуск для архитектуры сетевых хранилищ данных (NMDA).";
       reference
         "RFC 8349: A YANG Data Model for Routing Management
                    (NMDA Version)";
     }

     revision 2016-11-04 {
          description
            "Первоначальный выпуск.";
          reference
            "RFC 8022: A YANG Data Model for Routing Management";
     }

     augment "/if:interfaces/if:interface/ip:ipv6" {
       description
         "Добавляет в конфигурацию интерфейсов параметры IPv6 RA.";
       container ipv6-router-advertisements {
         description
           "Поддержка анонсов маршрутизаторов IPv6.";
         leaf send-advertisements {
           type boolean;
           default "false";
           description
             "Флаг, указывающий передаёт ли маршрутизатор периодические
              сообщения RA и отвечает ли на сообщения RS.";
           reference
             "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
                        - AdvSendAdvertisements";
         }
         leaf max-rtr-adv-interval {
           type uint16 {
             range "4..65535";
           }
           units "seconds";
           default "600";
           description
             "Максимальное время, разрешённое между отправкой групповых
              незапрошенных анонсов RA с интерфейса.";
           reference
             "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
                        - MaxRtrAdvInterval";
         }
         leaf min-rtr-adv-interval {
           type uint16 {
             range "3..1350";
           }
           units "seconds";
           must ". <= 0.75 * ../max-rtr-adv-interval" {
             description
               "НЕДОПУСТИМО значение больше 75% max-rtr-adv-interval.";
           }
           description
             "Минимальное время, разрешённое между отправкой групповых
              незапрошенных анонсов RA с интерфейса.

              Если этот лист не задан, используется принятое по 
              умолчанию значение:

              - если max-rtr-adv-interval >= 9 секунд, принято значение
                0,33 * max-rtr-adv-interval;

              - иначе используется значение 0,75 * max-rtr-adv-interval.";
           reference
             "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
                        - MinRtrAdvInterval";
         }
         leaf managed-flag {
           type boolean;
           default "false";
           description
             "Значение, помещаемое в поле флага Managed address
              configuration анонсов RA.";
           reference
             "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
                        - AdvManagedFlag";
         }
         leaf other-config-flag {
           type boolean;
           default "false";
           description
             "Значение, помещаемое в поле флага Other configuration
              анонсов RA.";
           reference
             "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
                        - AdvOtherConfigFlag";
         }
         leaf link-mtu {
           type uint32;
           default "0";
           description
             "Значение, помещаемое в опции MTU, передаваемые маршрутизатором.
              Значение 0 говорит, что опции MTU не передаются.";
           reference
             "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
                        - AdvLinkMTU";
         }
         leaf reachable-time {
           type uint32 {
             range "0..3600000";
           }
           units "milliseconds";
           default "0";
           description
             "Значение, помещаемое в поле Reachable Time анонсов RA, 
              передаваемых маршрутизатором. Значение 0 говорит о 
              незаданном (этим маршрутизатором) времени.";
           reference
             "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
                        - AdvReachableTime";
         }
         leaf retrans-timer {
           type uint32;
           units "milliseconds";
           default "0";
           description
             "Значение, помещаемое в поле Retrans Timer анонсов RA, 
              передаваемых маршрутизатором. Значение 0 говорит о
              незаданном (этим маршрутизатором) времени.";
           reference
             "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
                        - AdvRetransTimer";
         }
         leaf cur-hop-limit {
           type uint8;
           description
             "Значение, помещаемое в поле Cur Hop Limit анонсов RA, 
              передаваемых маршрутизатором. Значение 0 говорит о
              незаданном (этим маршрутизатором) счётчике.

              Если этот параметр не задан, устройству СЛЕДУЕТ применять
              заданное IANA значение принятого по умолчанию параметра IPv4
              TTL, которое действовало на момент реализации.";
           reference
             "RFC 3232: Assigned Numbers: RFC 1700 is Replaced by
                        an On-line Database
              RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
                        - AdvCurHopLimit
              IANA: IP Parameters
                    (https://www.iana.org/assignments/ip-parameters)"; 
         }
         leaf default-lifetime {
           type uint16 {
             range "0..65535";
           }
           units "seconds";
           description
             "Значение, помещаемое в поле Router Lifetime анонсов RA, 
              передаваемых с интерфейса (в секундах). Лист ДОЛЖЕН иметь
              значение 0 или число из интервала max-rtr-adv-interval - 
              9000 (секунд). Значение 0 указывает, что маршрутизатор не
              используется по умолчанию. Ограничения могут быть 
              переопределены документами, описывающими работу IPv6 на
              разных канальных уровнях.

              Если параметр не задан, устройству СЛЕДУЕТ применять
              значение 3 * max-rtr-adv-interval.";
           reference
             "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
                        - AdvDefaultLifetime";
         }
         container prefix-list {
           description
             "Поддержка размещения префиксов в опциях Prefix
              Information анонсов RA, передаваемых через интерфейс.

              Префиксы, анонсируемые по умолчанию, но не имеющие 
              записей в дочернем списке prefix. Анонсируются с
              принятыми по умолчанию значениями параметров.

              Префикс link-local НЕ СЛЕДУЕТ включать в число анонсируемых.";
           reference
             "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
                        - AdvPrefixList";
           list prefix {
             key "prefix-spec";
             description
               "Поддержка записей для анонсируемых префиксов.";
             leaf prefix-spec {
               type inet:ipv6-prefix;
               description
                 "Адресный префикс IPv6.";
             }
             choice control-adv-prefixes {
               default "advertise";
               description
                 "(1) префикс, явно удаляемый из числа анонсируемых, или 
                  (2) параметры, с которыми анонсируется префикс (по 
                  умолчанию).";
               leaf no-advertise {
                 type empty;
                 description
                   "Префикс, который не будет анонсироваться.

                    Это можно использовать для удаления префикса из числа
                    анонсируемых по умолчанию.";
               }
               case advertise {
                 leaf valid-lifetime {
                   type uint32;
                   units "seconds";
                   default "2592000";
                   description
                     "Значение, помещаемое в поле Valid Lifetime опции
                      Prefix Information. Все 1 (0xffffffff) представляют
                      бесконечность.";
                   reference
                     "RFC 4861: Neighbor Discovery for IP version 6
                                (IPv6) - AdvValidLifetime";
                 }
                 leaf on-link-flag {
                   type boolean;
                   default "true";
                   description
                     "Значение, помещаемое в поле флага on-link 
                      (L-bit) опции Prefix Information.";
                   reference
                     "RFC 4861: Neighbor Discovery for IP version 6
                                (IPv6) - AdvOnLinkFlag";
                 }
                 leaf preferred-lifetime {
                   type uint32;
                   units "seconds";
                   must ". <= ../valid-lifetime" {
                     description
                       "НЕДОПУСТИМО указывать значение больше valid-lifetime.";
                   }
                   default "604800";
                   description
                     "Значение, помещаемое в поле Preferred Lifetime опции
                      Prefix Information. Все 1 (0xffffffff) представляют
                      бесконечность.";
                   reference
                     "RFC 4861: Neighbor Discovery for IP version 6
                                (IPv6) - AdvPreferredLifetime";
                 }
                 leaf autonomous-flag {
                   type boolean;
                   default "true";
                   description
                     "Значение, помещаемое в поле Autonomous Flag опции
                      Prefix Information.";
                   reference
                     "RFC 4861: Neighbor Discovery for IP version 6
                                (IPv6) - AdvAutonomousFlag";
                 }
               }
             }
           }
         }
       }
     }

     /*
      * Перечисленные ниже узлы данных признаны устаревшими и отменены
      * архитектурой хранилищ данных управления сетью NMDA, описанной
      * в RFC 8342.
      */
     augment "/if:interfaces-state/if:interface/ip:ipv6" {
       status obsolete;
       description
         "Дополняет данные статуса интерфейса параметрами IPv6 RA.";
       container ipv6-router-advertisements {
         status obsolete;
         description
           "Параметры анонсов IPv6 RA.";
         leaf send-advertisements {
           type boolean;
           status obsolete;
           description
             "Флаг, указывающий, передаёт ли маршрутизатор периодические
              анонсы RA и отвечает ли на сообщения RS.";
         }
         leaf max-rtr-adv-interval {
           type uint16 {
             range "4..1800";
           }
           units "seconds";
           status obsolete;
           description
             "Максимальное разрешённое время между отправкой групповых
              незапрошенных анонсов RA через интерфейс.";
         }
         leaf min-rtr-adv-interval {
           type uint16 {
             range "3..1350";
           }
           units "seconds";
           status obsolete;
           description
             "Минимальное разрешённое время между отправкой групповых
              незапрошенных анонсов RA через интерфейс.";
         }
         leaf managed-flag {
           type boolean;
           status obsolete;
           description
             "Значение, помещаемое в поле флага Managed address
              configuration в анонсах RA.";
         }
         leaf other-config-flag {
           type boolean;
           status obsolete;
           description
             "Значение, помещаемое в поле флага Other configuration
              в анонсах RA.";
         }
         leaf link-mtu {
           type uint32;
           status obsolete;
           description
             "Значение, помещаемое в опции MTU, передаваемые маршрутизатором.
              Значение 0 говорит, что опции MTU не передаются.";
         }
         leaf reachable-time {
           type uint32 {
             range "0..3600000";
           }
           units "milliseconds";
           status obsolete;
           description
             "Значение, помещаемое в поле Reachable Time анонсов RA,
              передаваемых маршрутизатором. Значение 0 указывает, что
              поле не задано (этим маршрутизатором).";
         }
         leaf retrans-timer {
           type uint32;
           units "milliseconds";
           status obsolete;
           description
             "Значение, помещаемое в поле Retrans Timer анонсов RA,
              передаваемых маршрутизатором. Значение 0 указывает, что
              поле не задано (этим маршрутизатором).";
         }
         leaf cur-hop-limit {
           type uint8;
           status obsolete;
           description
             " Значение, помещаемое в поле Cur Hop Limit анонсов RA,
              передаваемых маршрутизатором. Значение 0 указывает, что
              поле не задано (этим маршрутизатором).";
         }
         leaf default-lifetime {
           type uint16 {
             range "0..9000";
           }
           units "seconds";
           status obsolete;
           description
             " Значение, помещаемое в поле Router Lifetime  анонсов RA,
              передаваемых через интерфейс (в секундах). Значение 0
              говорит, что маршрутизатор не используется по умолчанию.";
         }
         container prefix-list {
           status obsolete;
           description
             "Список префиксов, помещаемых в опции Prefix Information
              анонсов RA, передаваемых через интерфейс.

              По умолчанию это все префиксы, которые маршрутизатор 
              анонсирует через протоколы маршрутизации, как on-link для
              интерфейса, передающего анонс.";
           list prefix {
             key "prefix-spec";
             status obsolete;
             description
               "Анонсируемый префикс и его параметры.";
             leaf prefix-spec {
               type inet:ipv6-prefix;
               status obsolete;
               description
                 "Адресный префикс IPv6.";
             }
             leaf valid-lifetime {
               type uint32;
               units "seconds";
               status obsolete;
               description
                 "Значение, помещаемое в поле Valid Lifetime опции
                  Prefix Information. Все 1 (0xffffffff) указывают
                  бесконечность.

                  Реализации СЛЕДУЕТ сохранять это значение постоянным
                  в последующих анонсах, пока смена не задана явно в
                  конфигурации.";
             }
             leaf on-link-flag {
               type boolean;
               status obsolete;
               description
                 "Значение, помещаемое в поле флага on-link (L-bit)
                  опции Prefix Information.";
             }
             leaf preferred-lifetime {
               type uint32;
               units "seconds";
               status obsolete;
               description
                 "Значение, помещаемое в поле Preferred Lifetime опции
                  Prefix Information (в секундах). Все 1 (0xffffffff) 
                  представляют бесконечность.

                  Реализации СЛЕДУЕТ сохранять это значение постоянным
                  в последующих анонсах, пока смена не задана явно в
                  конфигурации.";
             }
             leaf autonomous-flag {
               type boolean;
               status obsolete;
               description
                 "Значение, помещаемое в поле Autonomous Flag опции
                  Prefix Information.";
             }
           }
         }
       }
     }
   }
   <CODE ENDS>

10. Взаимодействие с IANA

В [RFC8022] зарегистрированы указанные ниже пространства имён URI в реестре IETF XML Registry [RFC3688]. Агентство IANA обновило ссылки с указанием данного документа.

   URI: urn:ietf:params:xml:ns:yang:ietf-routing
   Registrant Contact: The IESG.
   XML: N/A; the requested URI is an XML namespace.

   URI: urn:ietf:params:xml:ns:yang:ietf-ipv4-unicast-routing
   Registrant Contact: The IESG.
   XML: N/A; the requested URI is an XML namespace.

   URI: urn:ietf:params:xml:ns:yang:ietf-ipv6-unicast-routing
   Registrant Contact: The IESG.
   XML: N/A; the requested URI is an XML namespace.

В [RFC8022] зарегистрированы указанные ниже модули YANG в реестре YANG Module Names [RFC6020]. Агентство IANA (1) обновило модули в соответствии с этим документом и (2) внесло ссылки на этот документ.

   Name:      ietf-routing
   Namespace: urn:ietf:params:xml:ns:yang:ietf-routing
   Prefix:    rt
   Reference: RFC 8349

   Name:      ietf-ipv4-unicast-routing
   Namespace: urn:ietf:params:xml:ns:yang:ietf-ipv4-unicast-routing
   Prefix:    v4ur
   Reference: RFC 8349

   Name:      ietf-ipv6-unicast-routing
   Namespace: urn:ietf:params:xml:ns:yang:ietf-ipv6-unicast-routing
   Prefix:    v6ur
   Reference: RFC 8349

Этот документ регистрирует указанный ниже субмодуль YANG в реестре YANG Module Names [RFC6020].

   Name:      ietf-ipv6-router-advertisements
   Module:    ietf-ipv6-unicast-routing
   Reference: RFC 8349

11. Вопросы безопасности

Заданные этим документом модули YANG определяют схему данных, предназначенных для доступа по протоколам сетевого управления, таким как NETCONF [RFC6241] и RESTCONF [RFC8040]. Нижним уровнем NETCONF является защищённый транспортный уровень с обязательной реализацией защищённого транспорта Secure Shell (SSH) [RFC6242]. Нижним уровнем RESTCONF является HTTPS с обязательной реализацией защищённого транспорта TLS [RFC5246].

Модель управления доступом NETCONF [RFC8341] обеспечивает средства, позволяющие предоставить отдельным пользователям NETCONF или RESTCONF доступ к предопределённому подмножеству протокольных операций и содержимого NETCONF и RESTCONF.

В этих модулях YANG определено множество узлов данных с возможностью записи, создания, удаления (т. е. по умолчанию установлено config true). Такие узлы могут считаться чувствительными или уязвимыми в некоторых сетевых средах. Операции записи в такие узлы (например, edit-config) без подобающей защиты могут оказывать негативное влияние на работу сети. Ниже указаны субдеревья и узлы данных с возможными уязвимостями.

/routing/control-plane-protocols/control-plane-protocol

Этот список указывает настроенные на устройстве протоколы плоскости управления.

/routing/ribs/rib

Этот список указывает настроенные на устройстве базы RIB.

Некоторые из доступных для чтения узлов данных в этих модулях YANG могут считаться чувствительными или уязвимыми в некоторых сетевых средах. Важен правильный контроль операций чтения таких узлов данных (например, через get, get-config, notification). Ниже указаны субдеревья и узлы данных с возможными уязвимостями.

/routing/control-plane-protocols/control-plane-protocol

Эти списки содержат настроенные на устройстве протоколы плоскости управления. Чувствительные сведения из таких списков приведены в моделях плоскостей управления.

/routing/ribs/rib

Этот список указывает базы RIB и их содержимое на устройстве. Доступ к этим сведениям может раскрывать топологию сети и другие данные.

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

/routing/ribs/rib/active-route

Результат этих операций RPC возвращает маршруты к конкретным адресатам. Доступ к этим сведениям может раскрывать топологию сети и взаимоотношения (например, клиентов и серверов). Кроме того, используемые сетевым устройством маршруты могут служить для организации атак на проходящий через устройство трафик.

12. Литература

12.1. Нормативные документы

[RFC2119] Bradner, S., «Key words for use in RFCs to Indicate Requirement Levels», BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.

[RFC3688] Mealling, M., «The IETF XML Registry», BCP 81, RFC 3688, DOI 10.17487/RFC3688, January 2004, <https://www.rfc-editor.org/info/rfc3688>.

[RFC4861] Narten, T., Nordmark, E., Simpson, W., and H. Soliman, «Neighbor Discovery for IP version 6 (IPv6)», RFC 4861, DOI 10.17487/RFC4861, September 2007, <https://www.rfc-editor.org/info/rfc4861>.

[RFC5246] Dierks, T. and E. Rescorla, «The Transport Layer Security (TLS) Protocol Version 1.2», RFC 5246, DOI 10.17487/RFC5246, August 2008, <https://www.rfc-editor.org/info/rfc5246>.

[RFC6020] Bjorklund, M., Ed., «YANG — A Data Modeling Language for the Network Configuration Protocol (NETCONF)», RFC 6020, DOI 10.17487/RFC6020, October 2010, <https://www.rfc-editor.org/info/rfc6020>.

[RFC6241] Enns, R., Ed., Bjorklund, M., Ed., Schoenwaelder, J., Ed., and A. Bierman, Ed., «Network Configuration Protocol (NETCONF)», RFC 6241, DOI 10.17487/RFC6241, June 2011, <https://www.rfc-editor.org/info/rfc6241>.

[RFC6242] Wasserman, M., «Using the NETCONF Protocol over Secure Shell (SSH)», RFC 6242, DOI 10.17487/RFC6242, June 2011, <https://www.rfc-editor.org/info/rfc6242>.

[RFC6991] Schoenwaelder, J., Ed., «Common YANG Data Types», RFC 6991, DOI 10.17487/RFC6991, July 2013, <https://www.rfc-editor.org/info/rfc6991>.

[RFC7950] Bjorklund, M., Ed., «The YANG 1.1 Data Modeling Language», RFC 7950, DOI 10.17487/RFC7950, August 2016, <https://www.rfc-editor.org/info/rfc7950>.

[RFC8022] Lhotka, L. and A. Lindem, «A YANG Data Model for Routing Management», RFC 8022, DOI 10.17487/RFC8022, November 2016, <https://www.rfc-editor.org/info/rfc8022>.

[RFC8040] Bierman, A., Bjorklund, M., and K. Watsen, «RESTCONF Protocol», RFC 8040, DOI 10.17487/RFC8040, January 2017, <https://www.rfc-editor.org/info/rfc8040>.

[RFC8174] Leiba, B., «Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words», BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.

[RFC8341] Bierman, A. and M. Bjorklund, «Network Configuration Access Control Model», STD 91, RFC 8341, DOI 10.17487/RFC8341, March 2018, <https://www.rfc-editor.org/info/rfc8341>.

[RFC8342] Bjorklund, M., Schoenwaelder, J., Shafer, P., Watsen, K., and R. Wilton, «Network Management Datastore Architecture (NMDA)», RFC 8342, DOI 10.17487/RFC8342, March 2018, <https://www.rfc-editor.org/info/rfc8342>.

[RFC8343] Bjorklund, M., «A YANG Data Model for Interface Management», RFC 8343, DOI 10.17487/RFC8343, March 2018, <https://www.rfc-editor.org/info/rfc8343>.

[RFC8344] Bjorklund, M., «A YANG Data Model for IP Management», RFC 8344, DOI 10.17487/RFC8344, March 2018, <https://www.rfc-editor.org/info/rfc8344>.

[W3C.REC-xml-20081126] Bray, T., Paoli, J., Sperberg-McQueen, M., Maler, E., and F. Yergeau, «Extensible Markup Language (XML) 1.0 (Fifth Edition)», World Wide Web Consortium Recommendation REC-xml-20081126, November 2008, <https://www.w3.org/TR/2008/REC-xml-20081126>.

12.2. Дополнительная литература

[RFC7224] Bjorklund, M., «IANA Interface Type YANG Module», RFC 7224, DOI 10.17487/RFC7224, May 2014, <https://www.rfc-editor.org/info/rfc7224>.

[RFC7895] Bierman, A., Bjorklund, M., and K. Watsen, «YANG Module Library», RFC 7895, DOI 10.17487/RFC7895, June 2016, <https://www.rfc-editor.org/info/rfc7895>.

[RFC7951] Lhotka, L., «JSON Encoding of Data Modeled with YANG», RFC 7951, DOI 10.17487/RFC7951, August 2016, <https://www.rfc-editor.org/info/rfc7951>.

[RFC8340] Bjorklund, M. and L. Berger, Ed., «YANG Tree Diagrams», BCP 215, RFC 8340, DOI 10.17487/RFC8340, March 2018, <https://www.rfc-editor.org/info/rfc8340>.

[YANG-Guidelines] Bierman, A., «Guidelines for Authors and Reviewers of YANG Data Model Documents», Work in Progress3, draft-ietf-netmod-rfc6087bis-20, March 2018.

Приложение A. Полное дерево схемы

В этом приложении представлено полное дерево модели данных ядра маршрутизации. Обозначения приведены в [RFC8340]. Тип данных каждого узла представлен справа от соответствующей строки.

   module: ietf-routing
     +--rw routing
     |  +--rw router-id?                 yang:dotted-quad
     |  +--ro interfaces
     |  |  +--ro interface*   if:interface-ref
     |  +--rw control-plane-protocols
     |  |  +--rw control-plane-protocol* [type name]
     |  |     +--rw type             identityref
     |  |     +--rw name             string
     |  |     +--rw description?     string
     |  |     +--rw static-routes
     |  |        +--rw v4ur:ipv4
     |  |        |  +--rw v4ur:route* [destination-prefix]
     |  |        |     +--rw v4ur:destination-prefix
     |  |        |     |       inet:ipv4-prefix
     |  |        |     +--rw v4ur:description?          string
     |  |        |     +--rw v4ur:next-hop
     |  |        |        +--rw (v4ur:next-hop-options)
     |  |        |           +--:(v4ur:simple-next-hop)
     |  |        |           |  +--rw v4ur:outgoing-interface?
     |  |        |           |  |       if:interface-ref
     |  |        |           |  +--rw v4ur:next-hop-address?
     |  |        |           |          inet:ipv4-address
     |  |        |           +--:(v4ur:special-next-hop)
     |  |        |           |  +--rw v4ur:special-next-hop?
     |  |        |           |          enumeration
     |  |        |           +--:(v4ur:next-hop-list)
     |  |        |              +--rw v4ur:next-hop-list
     |  |        |                 +--rw v4ur:next-hop* [index]
     |  |        |                    +--rw v4ur:index
     |  |        |                    |       string
     |  |        |                    +--rw v4ur:outgoing-interface?
     |  |        |                    |       if:interface-ref
     |  |        |                    +--rw v4ur:next-hop-address?
     |  |        |                            inet:ipv4-address
     |  |        +--rw v6ur:ipv6
     |  |           +--rw v6ur:route* [destination-prefix]
     |  |              +--rw v6ur:destination-prefix
     |  |              |       inet:ipv6-prefix
     |  |              +--rw v6ur:description?          string
     |  |              +--rw v6ur:next-hop
     |  |                 +--rw (v6ur:next-hop-options)
     |  |                    +--:(v6ur:simple-next-hop)
     |  |                    |  +--rw v6ur:outgoing-interface?
     |  |                    |  |       if:interface-ref
     |  |                    |  +--rw v6ur:next-hop-address?
     |  |                    |          inet:ipv6-address
     |  |                    +--:(v6ur:special-next-hop)
     |  |                    |  +--rw v6ur:special-next-hop?
     |  |                    |          enumeration
     |  |                    +--:(v6ur:next-hop-list)
     |  |                       +--rw v6ur:next-hop-list
     |  |                          +--rw v6ur:next-hop* [index]
     |  |                             +--rw v6ur:index
     |  |                             |       string
     |  |                             +--rw v6ur:outgoing-interface?
     |  |                             |       if:interface-ref
     |  |                             +--rw v6ur:next-hop-address?
     |  |                                     inet:ipv6-address
     |  +--rw ribs
     |     +--rw rib* [name]
     |        +--rw name              string
     |        +--rw address-family    identityref
     |        +--ro default-rib?      boolean {multiple-ribs}?
     |        +--ro routes
     |        |  +--ro route*
     |        |     +--ro route-preference?          route-preference
     |        |     +--ro next-hop
     |        |     |  +--ro (next-hop-options)
     |        |     |     +--:(simple-next-hop)
     |        |     |     |  +--ro outgoing-interface?
     |        |     |     |  |       if:interface-ref
     |        |     |     |  +--ro v4ur:next-hop-address?
     |        |     |     |  |       inet:ipv4-address
     |        |     |     |  +--ro v6ur:next-hop-address?
     |        |     |     |          inet:ipv6-address
     |        |     |     +--:(special-next-hop)
     |        |     |     |  +--ro special-next-hop?        enumeration
     |        |     |     +--:(next-hop-list)
     |        |     |        +--ro next-hop-list
     |        |     |           +--ro next-hop*
     |        |     |              +--ro outgoing-interface?
     |        |     |              |       if:interface-ref
     |        |     |              +--ro v4ur:address?
     |        |     |              |       inet:ipv4-address
     |        |     |              +--ro v6ur:address?
     |        |     |                      inet:ipv6-address
     |        |     +--ro source-protocol            identityref
     |        |     +--ro active?                    empty
     |        |     +--ro last-updated?              yang:date-and-time
     |        |     +--ro v4ur:destination-prefix?   inet:ipv4-prefix
     |        |     +--ro v6ur:destination-prefix?   inet:ipv6-prefix
     |        +---x active-route
     |        |  +---w input
     |        |  |  +---w v4ur:destination-address?   inet:ipv4-address
     |        |  |  +---w v6ur:destination-address?   inet:ipv6-address
     |        |  +--ro output
     |        |     +--ro route
     |        |        +--ro next-hop
     |        |        |  +--ro (next-hop-options)
     |        |        |     +--:(simple-next-hop)
     |        |        |     |  +--ro outgoing-interface?
     |        |        |     |  |       if:interface-ref
     |        |        |     |  +--ro v4ur:next-hop-address?
     |        |        |     |  |       inet:ipv4-address
     |        |        |     |  +--ro v6ur:next-hop-address?
     |        |        |     |          inet:ipv6-address
     |        |        |     +--:(special-next-hop)
     |        |        |     |  +--ro special-next-hop?
     |        |        |     |          enumeration
     |        |        |     +--:(next-hop-list)
     |        |        |        +--ro next-hop-list
     |        |        |           +--ro next-hop*
     |        |        |              +--ro outgoing-interface?
     |        |        |              |       if:interface-ref
     |        |        |              +--ro v4ur:next-hop-address?
     |        |        |              |       inet:ipv4-address
     |        |        |              +--ro v6ur:next-hop-address?
     |        |        |                      inet:ipv6-address
     |        |        +--ro source-protocol            identityref
     |        |        +--ro active?                    empty
     |        |        +--ro last-updated?
     |        |        |       yang:date-and-time
     |        |        +--ro v4ur:destination-prefix?
     |        |        |       inet:ipv4-prefix
     |        |        +--ro v6ur:destination-prefix?
     |        |                inet:ipv6-prefix
     |        +--rw description?      string
     o--ro routing-state
        o--ro router-id?                 yang:dotted-quad
        o--ro interfaces
        |  o--ro interface*   if:interface-state-ref
        o--ro control-plane-protocols
        |  o--ro control-plane-protocol* [type name]
        |     o--ro type    identityref
        |     o--ro name    string
        o--ro ribs
           o--ro rib* [name]
              o--ro name              string
              o--ro address-family    identityref
              o--ro default-rib?      boolean {multiple-ribs}?
              o--ro routes
              |  o--ro route*
              |     o--ro route-preference?          route-preference
              |     o--ro next-hop
              |     |  o--ro (next-hop-options)
              |     |     o--:(simple-next-hop)
              |     |     |  o--ro outgoing-interface?
              |     |     |  |       if:interface-ref
              |     |     |  o--ro v4ur:next-hop-address?
              |     |     |  |       inet:ipv4-address
              |     |     |  o--ro v6ur:next-hop-address?
              |     |     |          inet:ipv6-address
              |     |     o--:(special-next-hop)
              |     |     |  o--ro special-next-hop?        enumeration
              |     |     o--:(next-hop-list)
              |     |        o--ro next-hop-list
              |     |           o--ro next-hop*
              |     |              o--ro outgoing-interface?
              |     |              |       if:interface-ref
              |     |              o--ro v4ur:address?
              |     |              |       inet:ipv4-address
              |     |              o--ro v6ur:address?
              |     |                      inet:ipv6-address
              |     o--ro source-protocol            identityref
              |     o--ro active?                    empty
              |     o--ro last-updated?              yang:date-and-time
              |     o--ro v4ur:destination-prefix?   inet:ipv4-prefix
              |     o--ro v6ur:destination-prefix?   inet:ipv6-prefix
              o---x active-route
                 o---w input
                 |  o---w v4ur:destination-address?   inet:ipv4-address
                 |  o---w v6ur:destination-address?   inet:ipv6-address
                 o--ro output
                    o--ro route
                       o--ro next-hop
                       |  o--ro (next-hop-options)
                       |     o--:(simple-next-hop)
                       |     |  o--ro outgoing-interface?
                       |     |  |       if:interface-ref
                       |     |  o--ro v4ur:next-hop-address?
                       |     |  |       inet:ipv4-address
                       |     |  o--ro v6ur:next-hop-address?
                       |     |          inet:ipv6-address
                       |     o--:(special-next-hop)
                       |     |  o--ro special-next-hop?
                       |     |          enumeration
                       |     o--:(next-hop-list)
                       |        o--ro next-hop-list
                       |           o--ro next-hop*
                       |              o--ro outgoing-interface?
                       |              |       if:interface-ref
                       |              o--ro v4ur:next-hop-address?
                       |              |       inet:ipv4-address
                       |              o--ro v6ur:next-hop-address?
                       |                      inet:ipv6-address
                       o--ro source-protocol            identityref
                       o--ro active?                    empty
                       o--ro last-updated?
                       |       yang:date-and-time
                       o--ro v4ur:destination-prefix?
                       |       inet:ipv4-prefix
                       o--ro v6ur:destination-prefix?
                               inet:ipv6-prefix
   module: ietf-ipv6-unicast-routing
     augment /if:interfaces/if:interface/ip:ipv6:
       +--rw ipv6-router-advertisements
          +--rw send-advertisements?    boolean
          +--rw max-rtr-adv-interval?   uint16
          +--rw min-rtr-adv-interval?   uint16
          +--rw managed-flag?           boolean
          +--rw other-config-flag?      boolean
          +--rw link-mtu?               uint32
          +--rw reachable-time?         uint32
          +--rw retrans-timer?          uint32
          +--rw cur-hop-limit?          uint8
          +--rw default-lifetime?       uint16
          +--rw prefix-list
             +--rw prefix* [prefix-spec]
                +--rw prefix-spec           inet:ipv6-prefix
                +--rw (control-adv-prefixes)?
                   +--:(no-advertise)
                   |  +--rw no-advertise?         empty
                   +--:(advertise)
                      +--rw valid-lifetime?       uint32
                      +--rw on-link-flag?         boolean
                      +--rw preferred-lifetime?   uint32
                      +--rw autonomous-flag?      boolean
     augment /if:interfaces-state/if:interface/ip:ipv6:
       o--ro ipv6-router-advertisements
          o--ro send-advertisements?    boolean
          o--ro max-rtr-adv-interval?   uint16
          o--ro min-rtr-adv-interval?   uint16
          o--ro managed-flag?           boolean
          o--ro other-config-flag?      boolean
          o--ro link-mtu?               uint32
          o--ro reachable-time?         uint32
          o--ro retrans-timer?          uint32
          o--ro cur-hop-limit?          uint8
          o--ro default-lifetime?       uint16
          o--ro prefix-list
             o--ro prefix* [prefix-spec]
                o--ro prefix-spec           inet:ipv6-prefix
                o--ro valid-lifetime?       uint32
                o--ro on-link-flag?         boolean
                o--ro preferred-lifetime?   uint32
                o--ro autonomous-flag?      boolean

Приложение B. Минимальная реализация

Некоторые части и опции модели ядра маршрутизации, такие как пользовательские RIB, предназначены лишь для «продвинутых» маршрутизаторов. В этом приложении даны основные (ненормативные) рекомендации по реализации минимального набора доступных функций. Такие реализации подходят для хостов и простых маршрутизаторов.

Минимальная реализация не поддерживает свойство multiple-ribs. Это означает доступность лишь одной контролируемой системой базы RIB для каждого поддерживаемого семейства адресов (IPv4, IPv6 или оба). Эти RIB используются по умолчанию. Управляемые пользователем базы RIB не разрешены.

В дополнение к обязательному экземпляру псевдопротокола direct минимальной реализации следует поддерживать настраиваемые экземпляры псевдопротокола static.

Для хостов, не предназначенных на роль маршрутизаторов, возможность включать передачу анонсов IPv6 RA (параграф 5.4) следует исключить.

Платформы с существенно ограниченными ресурсами могут применять отклонения (deviations) для ограничения модели данных, например, ограничивая число экземпляров контролируемых системой псевдопротоколов static.

Приложение C. Пример добавления нового протокола управления

В этом приложении показано, как можно расширить модель данных ядра маршрутизации для поддержки новых протоколов плоскости управления. Приведённый ниже модуль YANG example-rip предназначен для иллюстрации, а не реального определения модели данных для протокола RIP (Routing Information Protocol). Для краткости в модуле не выполняются некоторые из рекомендаций [YANG-Guidelines] (см. также параграф 5.3.2).

   module example-rip {

     yang-version "1.1";
     namespace "http://example.com/rip";
     prefix "rip";

     import ietf-interfaces {
       prefix "if";
     }

     import ietf-routing {
       prefix "rt";
     }

     identity rip {
       base rt:routing-protocol;
       description
         "Отождествление для Routing Information Protocol (RIP).";
     }

     typedef rip-metric {
       type uint8 {
         range "0..16";
       }
     }

     grouping route-content {
       description
         "Группировка, определяющая атрибуты маршрутов RIP.";
       leaf metric {
         type rip-metric;
       }
       leaf tag {
         type uint16;
         default "0";
         description
           "Этот лист может служить для передачи дополнительных сведений, 
            например, номера автономной системы (autonomous system или AS).";
       }
     }

     augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route" {
       when "derived-from-or-self(rt:source-protocol, 'rip:rip')" {
         description
           "Это дополнение действительно лишь для маршрутов полученных
            от протокола RIP.";
       }
       description
         "Относящиеся к RIP атрибуты маршрутов.";
       uses route-content;
     }

     augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/"
           + "rt:output/rt:route" {
       description
         "Связанные с RIP атрибуты маршрута на выходе active-route RPC.";
       uses route-content;
     }

     augment "/rt:routing/rt:control-plane-protocols/"
           + "rt:control-plane-protocol" {
       when "derived-from-or-self(rt:type,'rip:rip')" {
         description
           "Это дополнение действительно лишь для экземпляров протокола
            маршрутизации типа rip.";
       }
       container rip {
         presence
           "RIP configuration";
         description
           "Конфигурация экземпляра RIP.";
         container interfaces {
           description
             "Конфигурация на уровне интерфейса RIP.";
           list interface {
             key "name";
             description
               "Протокол RIP включён на интерфейсах, имеющих запись в этом
                списке, если для записи явно на задано enabled = false.";
             leaf name {
               type if:interface-ref;
             }
             leaf enabled {
               type boolean;
               default "true";
             }
             leaf metric {
               type rip-metric;
               default "1";
             }
           }
         }
         leaf update-interval {
           type uint8 {
             range "10..60";
           }
           units "seconds";
           default "30";
           description
             "Временной интервал между периодическими обновлениями.";
         }
       }
     }
   }

Приложение D. Пример дерева данных

В этом приложении приведён пример экземпляра дерева данных из рабочего состояния в формате JSON [RFC7951] (пример включает iana-if-type из [RFC7224]).

Данные соответствуют модели, определённой в указанной ниже спецификации библиотеки YANG [RFC7895].

    {
      "ietf-yang-library:modules-state": {
        "module-set-id": "c2e1f54169aa7f36e1a6e8d0865d441d3600f9c4",
        "module": [
          {
            "name": "ietf-routing",
            "revision": "2018-03-13",
            "feature": [
              "multiple-ribs",
              "router-id"
            ],
            "namespace": "urn:ietf:params:xml:ns:yang:ietf-routing",
            "conformance-type": "implement"
          },
          {
            "name": "ietf-ipv4-unicast-routing",
            "revision": "2018-03-13",
            "namespace":
              "urn:ietf:params:xml:ns:yang:ietf-ipv4-unicast-routing",
            "conformance-type": "implement"
          },
          {
            "name": "ietf-ipv6-unicast-routing",
            "revision": "2018-03-13",
            "namespace":
              "urn:ietf:params:xml:ns:yang:ietf-ipv6-unicast-routing",
            "conformance-type": "implement",
            "submodule": [
              {
                "name": "ietf-ipv6-router-advertisements",
                "revision": "2018-03-13"
              }
            ]
          },
          {
            "name": "ietf-interfaces",
            "revision": "2018-02-20",
            "namespace": "urn:ietf:params:xml:ns:yang:ietf-interfaces",
            "conformance-type": "implement"
          },
          {
            "name": "ietf-inet-types",
            "namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types",
            "revision": "2013-07-15",
            "conformance-type": "import"
          },
          {
            "name": "ietf-yang-types",
            "namespace": "urn:ietf:params:xml:ns:yang:ietf-yang-types",
            "revision": "2013-07-15",
            "conformance-type": "import"
          },
          {
            "name": "iana-if-type",
            "namespace": "urn:ietf:params:xml:ns:yang:iana-if-type",
            "revision": "2014-05-08",
            "conformance-type": "implement"
          },
          {
            "name": "ietf-ip",
            "revision": "2018-02-22",
            "namespace": "urn:ietf:params:xml:ns:yang:ietf-ip",
            "conformance-type": "implement"
          }
        ]
      }
    }
+-----------------+
|                 |
|Маршрутизатор ISP|
|                 |
+--------+--------+
         |2001:db8:0:1::2
         |192.0.2.2
         |
         |
         |2001:db8:0:1::1
     eth0|192.0.2.1
+--------+--------+
|                 |
| Маршрутизатор A |
|                 |
+--------+--------+
     eth1|198.51.100.1
         |2001:db8:0:2::1
         |

Рисунок 2. Пример конфигурации сети.


В примере простой сети на рисунке 2 маршрутизатор A использует статические маршруты по умолчанию с маршрутизатором ISP в качестве следующего интервала. Анонсы IPv6 RA настроены лишь для интерфейса eth1 и отключены на eth0.

Экземпляр дерева данных может иметь приведённую ниже форму.

   {
     "ietf-interfaces:interfaces": {
       "interface": [
         {
           "name": "eth0",
           "type": "iana-if-type:ethernetCsmacd",
           "description": "Uplink to ISP.",
           "phys-address": "00:0C:42:E5:B1:E9",
           "oper-status": "up",
           "statistics": {
             "discontinuity-time": "2015-10-24T17:11:27+02:00"
           },
           "ietf-ip:ipv4": {
             "forwarding": true,
             "mtu": 1500,
             "address": [
               {
                 "ip": "192.0.2.1",
                 "prefix-length": 24
               }
             ]
           },
           "ietf-ip:ipv6": {
             "forwarding": true,
             "mtu": 1500,
             "address": [
               {
                 "ip": "2001:0db8:0:1::1",
                 "prefix-length": 64
               }
             ],
             "autoconf": {
               "create-global-addresses": false
             },
             "ietf-ipv6-unicast-routing:ipv6-router-advertisements": {
               "send-advertisements": false
             }
           }
         },
         {
           "name": "eth1",
           "type": "iana-if-type:ethernetCsmacd",
           "description": "Interface to the internal network.",
           "phys-address": "00:0C:42:E5:B1:EA",
           "oper-status": "up",
           "statistics": {
             "discontinuity-time": "2015-10-24T17:11:29+02:00"
           },
           "ietf-ip:ipv4": {
             "forwarding": true,
             "mtu": 1500,
             "address": [
               {
                 "ip": "198.51.100.1",
                 "prefix-length": 24
               }
             ]
           },
           "ietf-ip:ipv6": {
             "forwarding": true,
             "mtu": 1500,
             "address": [
               {
                 "ip": "2001:0db8:0:2::1",
                 "prefix-length": 64
               }
             ],
             "autoconf": {
               "create-global-addresses": false
             },
             "ietf-ipv6-unicast-routing:ipv6-router-advertisements": {
               "send-advertisements": true,
               "prefix-list": {
                 "prefix": [
                   {
                     "prefix-spec": "2001:db8:0:2::/64"
                   }
                 ]
               }
             }
           }
         }
       ]
     },

     "ietf-routing:routing": {
       "router-id": "192.0.2.1",
       "control-plane-protocols": {
         "control-plane-protocol": [
           {
             "type": "ietf-routing:static",
             "name": "st0",
             "description":
               "Static routing is used for the internal network.",
             "static-routes": {
               "ietf-ipv4-unicast-routing:ipv4": {
                 "route": [
                   {
                     "destination-prefix": "0.0.0.0/0",
                     "next-hop": {
                       "next-hop-address": "192.0.2.2"
                     }
                   }
                 ]
               },
               "ietf-ipv6-unicast-routing:ipv6": {
                 "route": [
                   {
                     "destination-prefix": "::/0",
                     "next-hop": {
                       "next-hop-address": "2001:db8:0:1::2"
                     }
                   }
                 ]
               }
             }
           }
         ]
       },
       "ribs": {
         "rib": [
           {
             "name": "ipv4-master",
             "address-family":
               "ietf-ipv4-unicast-routing:ipv4-unicast",
             "default-rib": true,
             "routes": {
               "route": [
                 {
                   "ietf-ipv4-unicast-routing:destination-prefix":
                     "192.0.2.1/24",
                   "next-hop": {
                     "outgoing-interface": "eth0"
                   },
                   "route-preference": 0,
                   "source-protocol": "ietf-routing:direct",
                   "last-updated": "2015-10-24T17:11:27+02:00"
                 },
                 {
                   "ietf-ipv4-unicast-routing:destination-prefix":
                     "198.51.100.0/24",
                   "next-hop": {
                     "outgoing-interface": "eth1"
                   },
                   "source-protocol": "ietf-routing:direct",
                   "route-preference": 0,
                   "last-updated": "2015-10-24T17:11:27+02:00"
                 },
                 {
                   "ietf-ipv4-unicast-routing:destination-prefix":
                     "0.0.0.0/0",
                   "source-protocol": "ietf-routing:static",
                   "route-preference": 5,
                   "next-hop": {
                     "ietf-ipv4-unicast-routing:next-hop-address":
                       "192.0.2.2"
                   },
                   "last-updated": "2015-10-24T18:02:45+02:00"
                 }
               ]
             }
           },
           {
             "name": "ipv6-master",
             "address-family":
               "ietf-ipv6-unicast-routing:ipv6-unicast",
             "default-rib": true,
             "routes": {
               "route": [
                 {
                   "ietf-ipv6-unicast-routing:destination-prefix":
                     "2001:db8:0:1::/64",
                   "next-hop": {
                     "outgoing-interface": "eth0"
                   },
                   "source-protocol": "ietf-routing:direct",
                   "route-preference": 0,
                   "last-updated": "2015-10-24T17:11:27+02:00"
                 },
                 {
                   "ietf-ipv6-unicast-routing:destination-prefix":
                     "2001:db8:0:2::/64",
                   "next-hop": {
                     "outgoing-interface": "eth1"
                   },
                   "source-protocol": "ietf-routing:direct",
                   "route-preference": 0,
                   "last-updated": "2015-10-24T17:11:27+02:00"
                 },
                 {
                   "ietf-ipv6-unicast-routing:destination-prefix":
                     "::/0",
                   "next-hop": {
                     "ietf-ipv6-unicast-routing:next-hop-address":
                       "2001:db8:0:1::2"
                   },
                   "source-protocol": "ietf-routing:static",
                   "route-preference": 5,
                   "last-updated": "2015-10-24T18:02:45+02:00"
                 }
               ]
             }
           }
         ]
       }
     }
   }

Приложение E. Пример отклика на NETCONF Get Data

В этом приложении представлен пример отклика XML [W3C.REC-xml-20081126] на запрос NETCONF <get-data> для хранилища <operational> на устройстве, реализующем приведённую выше модель данных.

   <rpc-reply
    xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
    message-id="101">
    <data>
      <routing
        xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"
        xmlns:or="urn:ietf:params:xml:ns:yang:ietf-origin">

        <router-id or:origin="or:intended">192.0.2.1</router-id>
        <control-plane-protocols or:origin="or:intended">
          <control-plane-protocol>
            <type>ietf-routing:static</type>
            <name>static-routing-protocol</name>
            <static-routes>
              <ietf-ipv4-unicast-routing:ipv4>
                <route>
                  <destination-prefix>0.0.0.0/0</destination-prefix>
                  <next-hop>
                    <next-hop-address>192.0.2.2</next-hop-address>
                  </next-hop>
                </route>
              </ietf-ipv4-unicast-routing:ipv4>
              <ietf-ipv6-unicast-routing:ipv6>
                <route>
                  <destination-prefix>::/0</destination-prefix>
                  <next-hop>
                    <next-hop-address>2001:db8:0:1::2</next-hop-address>
                  </next-hop>
                </route>
              </ietf-ipv6-unicast-routing:ipv6>
            </static-routes>
          </control-plane-protocol>
        </control-plane-protocols>

        <ribs>
          <rib or:origin="or:intended">
            <name>ipv4-master</name>
            <address-family>
              ietf-ipv4-unicast-routing:ipv4-unicast
            </address-family>
            <default-rib>true</default-rib>
            <routes>
              <route>
                <ietf-ipv4-unicast-routing:destination-prefix>
                  192.0.2.1/24
                </ietf-ipv4-unicast-routing:destination-prefix>
                <next-hop>
                  <outgoing-interface>eth0</outgoing-interface>
                </next-hop>
                <route-preference>0</route-preference>
                <source-protocol>ietf-routing:direct</source-protocol>
                <last-updated>2015-10-24T17:11:27+02:00</last-updated>
              </route>
              <route>
                <ietf-ipv4-unicast-routing:destination-prefix>
                  198.51.100.0/24
                </ietf-ipv4-unicast-routing:destination-prefix>
                <next-hop>
                  <outgoing-interface>eth1</outgoing-interface>
                </next-hop>
                <route-preference>0</route-preference>
                <source-protocol>ietf-routing:direct</source-protocol>
                <last-updated>2015-10-24T17:11:27+02:00</last-updated>
              </route>
              <route>
                <ietf-ipv4-unicast-routing:destination-prefix>0.0.0.0/0
                </ietf-ipv4-unicast-routing:destination-prefix>
                <next-hop>
                  <ietf-ipv4-unicast-routing:next-hop-address>192.0.2.2
                  </ietf-ipv4-unicast-routing:next-hop-address>
                </next-hop>
                <route-preference>5</route-preference>
                <source-protocol>ietf-routing:static</source-protocol>
                <last-updated>2015-10-24T18:02:45+02:00</last-updated>
              </route>
            </routes>
          </rib>
          <rib or:origin="or:intended">
            <name>ipv6-master</name>
            <address-family>
              ietf-ipv6-unicast-routing:ipv6-unicast
            </address-family>
            <default-rib>true</default-rib>
            <routes>
              <route>
                <ietf-ipv6-unicast-routing:destination-prefix>
                  2001:db8:0:1::/64
                </ietf-ipv6-unicast-routing:destination-prefix>
                <next-hop>
                  <outgoing-interface>eth0</outgoing-interface>
                </next-hop>
                <route-preference>0</route-preference>
                <source-protocol>ietf-routing:direct</source-protocol>
                <last-updated>2015-10-24T17:11:27+02:00</last-updated>
              </route>
              <route>
                <ietf-ipv6-unicast-routing:destination-prefix>
                  2001:db8:0:2::/64
                </ietf-ipv6-unicast-routing:destination-prefix>
                <next-hop>
                  <outgoing-interface>eth1</outgoing-interface>
                </next-hop>
                <route-preference>0</route-preference>
                <source-protocol>ietf-routing:direct</source-protocol>
                <last-updated>2015-10-24T17:11:27+02:00</last-updated>
              </route>
              <route>
                <ietf-ipv6-unicast-routing:destination-prefix>::/0
                </ietf-ipv6-unicast-routing:destination-prefix>
                <next-hop>
                  <ietf-ipv6-unicast-routing:next-hop-address>
                    2001:db8:0:1::2
                  </ietf-ipv6-unicast-routing:next-hop-address>
                </next-hop>
                <route-preference>5</route-preference>
                <source-protocol>ietf-routing:static</source-protocol>
                <last-updated>2015-10-24T18:02:45+02:00</last-updated>
              </route>
            </routes>
          </rib>
        </ribs>
      </routing>
    </data>
   </rpc-reply>

Благодарности

Авторы благодарны Nitin Bahadur, Martin Bjorklund, Dean Bogdanovic, Joe Clarke, Francis Dupont, Jeff Haas, Joel Halpern, Wes Hardaker, Jia He, Sriganesh Kini, Suresh Krishnan, David Lamparter, Xiang Li, Stephane Litkowski, Andrew McGregor, Jan Medved, Thomas Morin, Tom Petch, Bruno Rijsman, Juergen Schoenwaelder, Phil Shafer, Dave Thaler, Vladimir Vassilev, Rob Wilton, Yi Yang, Derek Man-Kit Yeung, Jeffrey Zhang за их полезные комментарии и предложения.

Адреса авторов

Ladislav Lhotka
CZ.NIC
Email: lhotka@nic.cz
 
Acee Lindem
Cisco Systems
Email: acee@cisco.com
 
Yingzhen Qu
Huawei
2330 Central Expressway
Santa Clara, CA 95050
United States of America
Email: yingzhen.qu@huawei.com

Перевод на русский язык

Николай Малых

nmalykh@protokols.ru

1Internet Engineering Task Force — комиссия по решению инженерных задач Internet.

2Internet Engineering Steering Group — комиссия по инженерным разработкам Internet.

3Опубликовано в RFC 8407. Прим. перев.

Рубрика: RFC | Оставить комментарий

RFC 8340 YANG Tree Diagrams

Internet Engineering Task Force (IETF)                      M. Bjorklund
Request for Comments: 8340                                Tail-f Systems
BCP: 215                                                  L. Berger, Ed.
Category: Best Current Practice                  LabN Consulting, L.L.C.
ISSN: 2070-1721                                               March 2018

Диаграммы деревьев YANG

YANG Tree Diagrams

PDF

Аннотация

В этом документе описан современный синтаксис, применяемый в диаграммах деревьев модулей YANG. Цель документа заключается в сборе всех определений в одном месте. Синтаксис может время от времени обновляться по мере развития языка YANG.

Статус документа

Этот документ относится к категории обмена опытом (Internet Best Current Practice).

Документ является результатом работы IETF1 и представляет согласованное мнение сообщества IETF. Документ был представлен на публичное рассмотрение и был одобрен для публикации IESG2. Дополнительную информацию о документах серии BCP можно найти в разделе 2 RFC 7841.

Информацию о текущем статусе документа, замеченных ошибках и способах обратной связи можно получить по ссылке https://www.rfc-editor.org/info/rfc8340.

Авторские права

Авторские права (Copyright (c) 2018) принадлежат IETF Trust и лицам, указанным в качестве авторов документа. Все права защищены.

К документу применимы права и ограничения, указанные в BCP 78 и IETF Trust Legal Provisions и относящиеся к документам IETF (http://trustee.ietf.org/license-info), на момент публикации данного документа. Прочтите упомянутые документы внимательно. Фрагменты программного кода, включённые в этот документ, распространяются в соответствии с упрощённой лицензией BSD, как указано в параграфе 4.e документа IETF Trust Legal Provisions, без каких-либо гарантий (как указано в Simplified BSD License).

1. Введение

Диаграммы деревьев YANG впервые были опубликованы в RFC 6536. Они используются для упрощённого графического представления модели данных и могут создаваться автоматически с помощью таких инструментов, как pyang [PYANG]. В этом документе описан синтаксис, применяемый в диаграммах деревьев YANG. Предполагается, что этот документ будет обновляться или заменяться по мере внесения изменений в язык YANG [RFC7950].

Сегодня определения синтаксиса, использованного для представления модуля YANG, зачастую включаются в каждый документ с диаграммами деревьев. Такой подход связан с рядом недостатков, поэтому целью данного документа является создание единой точки, где представлены такие определения. Документ не предназначен для ограничения будущих изменений и служит скорей для того, чтобы такие изменения были согласованными и легко идентифицируемыми.

Пример диаграммы дерева можно найти в разделе 3 документа [RFC8343]. Часть такого дерева приведена ниже.

     +--rw interfaces
        +--rw interface* [name]
           +--rw name                        string
           +--rw description?                string
           +--rw type                        identityref
           +--rw enabled?                    boolean
           +--rw link-up-down-trap-enable?   enumeration {if-mib}?

2. Синтаксис диаграммы дерева

В этом разделе описано значение символов, применяемых в диаграммах деревьев YANG.

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

Модуль указывается тегом module:, за которым следует имя модуля <module-name>. Далее следует один или несколько разделов в соответствии с приведёнными ниже правилами.

  1. Определённые в модуле узлы верхнего уровня смещаются пробелами на две позиции.

  2. Добавления смещаются на две пробельных позиции и указываются ключевым словом augment, за которым следует целевой узел и двоеточие (:).

  3. RPC смещаются на два пробела и указываются тегом rpcs:.

  4. Уведомления смещаются на два пробела и указываются тегом notifications:.

  5. Группировки смещаются на два пробела и указываются ключевым словом grouping, за которым следует имя группировки и двоеточие (:).

  6. Структуры yang-data смещаются на два пробела и указываются ключевым словом yang-data, за которым следует имя структуры yang-data и двоеточие (:).

Относительная организация каждого раздела обеспечивается с использованием текстового формата, который обычно применяется для вывода команд отображения дерева каталогов файловых систем. Каждый узел дерева имеет префикс +—. Узлы схемы, являющиеся потомками другого узла смещаются от родителя на три пробела. Братские узлы схемы указываются с одинаковым смещением и разделяются пустой строкой, а для связывания служит символ |.

Пример полного формата с учётом соглашений о смещениях приведён ниже.

     module: <module-name>
       +--<node>
       |  +--<node>
       |     +--<node>
       +--<node>
          +--<node>
             +--<node>

       augment <target-node>:
         +--<node>
            +--<node>
            +--<node>
               +--<node>
       augment <target-node>:
         +--<node>

       rpcs:
         +--<rpc-node>
         +--<rpc-node>
            +--<node>
            |  +--<node>
            +--<node>

       notifications:
         +--<notification-node>
         +--<notification-node>
            +--<node>
            |  +--<node>
            +--<node>

       grouping <grouping-name>:
         +--<node>
            +--<node>
            |  +--<node>
            +--<node>
       grouping <grouping-name>:
         +--<node>


       yang-data <yang-data-name>:
         +--<node>
            +--<node>
            |  +--<node>
            +--<node>
       yang-data <yang-data-name>:
         +--<node>

2.1. Субмодуль

Субмодули представляются аналогично модулям, но указываются тегом submodule:, за которым следует имя субмодуля <module-name>. Например,

     submodule: <module-name>
       +--<node>
       |  +--<node>
       |     +--<node>

2.2. Группировка

Узлы в используемой группировке обычно раскрываются, как будто они определены в месте расположения оператора uses. Однако можно не раскрывать оператор uses, а вместо этого просто указывать имя группировки.

Например, приведённая ниже диаграмма показывает группировку tls-transport из [RFC7407] без раскрытия

       +--rw tls
          +---u tls-transport

Если группу раскрыть, она пример вид

       +--rw tls
          +--rw port?                 inet:port-number
          +--rw client-fingerprint?   x509c2n:tls-fingerprint
          +--rw server-fingerprint?   x509c2n:tls-fingerprint
          +--rw server-identity?      snmp:admin-string

Группировки могут присутствовать в разделе groupings.

2.3. Структура yang-data

Если модуль определяет структуры yang-data [RFC8040], эти структуры могут присутствовать в разделе yang-data.

2.4. Сжатое представление узлов

Когда состав узлов внутри схемы модуля не имеет значения в контексте представления дерева, братские узлы и их потомки могут быть «сжаты» с использованием трёх точек (…) вместо строк, представляющих реальные узлы.

       +--<node>
       |  ...
       +--<node>
          +--<node>
             +--<node>

2.5. Комментарий

Отдельная строка комментария, начинающегося с символов // (возможно смещённых) и длящегося до конца строки, может использоваться в дереве.

2.6. Представление узла

Каждый узел модуля YANG указывается в форме

     <status>--<flags> <name><opts> <type> <if-features>

       <status> 
         +  текущий
         x  устаревший (deprecated)
         o  отменённый (obsolete)

       <flags>
         rw  для узлов данных конфигурации и узлов выбора
         ro  для узлов неконфигурационных данных и узлов выбора,
             выходных параметров rpc и операций (actions), а 
             также параметров уведомлений
         -w  для входных параметров rpc и операций
         -u  для использования в группировках
         -x  для rpc и операций
         -n  для уведомлений
         mp  для узлов с оператором расширения mount-point

Узлы вариантов (case) не имеют флагов.

       <name> имя узла
         (<name>) означает, что узел задаёт выбор (choice)
        :(<name>) означает, что узел задаёт вариант (case)

Если узел добавлен в дерево из другого модуля, его имя указывается в форме <prefix>:<name>, где <prefix> указывает префикс, заданный в модуле, где определён узел.

Если узел является вариантом (case) перед <name> пробелы не используются.

       <opts> 
         ?  для опционального узла leaf, choice, anydata, anyxml
         !  для контейнера присутствия
         *  для leaf-list или list
         [<keys>] для ключей списка
         /  для узла данных верхнего уровня в смонтированном модуле
         @  для узла данных верхнего уровня модуля, указанного в точке
            монтирования родительской ссылки

       <type> название типа для leaf и leaf-list

Для leafref тип указывается как (1) -> TARGET, где TARGET указывает путь leafref с удалением (по возможности) ссылок или (2) leafref.

       <if-features> список функций (feature) от которых узел зависит,
         указывается в фигурных скобках, за которыми следует символ ? {...}?

Между разделяемыми пробелами полями (например, <opts> и <type>) число пробелов может быть любым. Дополнительные пробелы могут применяться для выравнивания полей (например, в списке или контейнере) с целью удобочитаемости.

3. Рекомендации по использованию в RFC

В этом разделе приводятся рекомендации, связанные с использованием диаграмм деревьев в RFC.

3.1. Разрыв длинных строк

В документах Internet-Draft и RFC размер строки ограничен 72 символами. Когда представление узла требует более длинной строки, следует использовать перевод строки между <opts> и <type> или между <type> и <if-feature>. Новую строку следует дополнить пробельными символами так, чтобы она начиналась под <name> со смещением в 2 символа.

     notifications:
       +---n yang-library-change
          +--ro module-set-id
                  -> /modules-state/module-set-id

Длинные пути (например, пути leafref или цели дополнения) можно разбивать на несколько строк. Например,

     augment /nat:nat/nat:instances/nat:instance/nat:mapping-table
               /nat:mapping-entry:

Упомянутая выше команда pyang помогает в создании нужного форматирования. Например, приведённая выше диаграмма уведомлений создана с помощью команды

     pyang -f tree --tree-line-length 50 ietf-yang-library.yang

При включении диаграмм в качестве рисунков в Internet-Draft или RFC имеет смысл указать —tree-line-length 69.

3.2. Группировки

Если модуль YANG состоит только из группировок, диаграмма дерева должна включать группировки. Можно снова воспользоваться компилятором pyang для создания диаграммы дерева с группировками, используя параметры -f tree —tree-print-groupings.

3.3. Длинные диаграммы

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

Пример разделения диаграммы можно видеть в [RFC7407], где параграф 2.4 включает диаграмму конфигурации engine

       +--rw snmp
          +--rw engine
             // дополнительные параметры из ветви engine

Далее в параграфе 2.5 [RFC7407] показана диаграмма конфигурации target

       +--rw snmp
          +--rw target* [name]
             // дополнительные параметры из ветви target

Уже упомянутая команда pyang будет полезна для создания таких деревьев. Для приведённого выше примера использовалась команда

     pyang -f tree --tree-path /snmp/target ietf-snmp.yang

4. Диаграммы деревьев с точками монтирования YANG

Монтирование схемы YANG, определённое в [SCHEMA-MOUNT], требует некоторого обсуждения. Монтирование схемы является базовым механизмом, который позволяет монтировать один или несколько модулей YANG в заданном месте другой (родительской) схемы. Место установки указывается «точкой монтирования» и любой контейнер или список могут служить в качестве таких точек. Точки монтирования указываются путём включения оператора расширения mount-point в качестве субоператора для узла контейнера или списка. Таким образом узлы точек монтирования напрямую указываются в определении схемы модуля и могут быть указаны в дереве с помощью флага mp.

В следующем примере, заимствованном из [YANG-NIs], контейнер vrf-root включает оператор расширения mount-point в качестве части своего определения.

     module: ietf-network-instance
       +--rw network-instances
          +--rw network-instance* [name]
             +--rw name           string
             +--rw enabled?       boolean
             +--rw description?   string
             +--rw (ni-type)?
             +--rw (root-type)
                +--:(vrf-root)
                |  +--mp vrf-root

4.1. Представление деревьев с точками монтирования

Реальные модули, доступные под точкой монтирования, контролируются сервером и предоставляются клиентам. Эта информация обычно представляется через модуль монтирования схемы (ietf-yang-schema-mount), определённый в [SCHEMA-MOUNT]. Модуль монтирования схемы поддерживает раскрытие (exposure) как смонтированных схем, так и «родительских ссылок». Последние используются для оценки выражений XPath3 в смонтированных модулях и не представляют доступные клиенту пути, указанная ссылкой информация доступна клиентам через родительскую схему. Монтирование схемы также определяет «встроенный» (inline) тип точек монтирования, где смонтированные модули раскрываются через библиотечный модуль YANG.

Хотя доступные под точкой монтирования модули не указываются в модулях YANG с точками монтирования, определяющий модуль документ будет описывать предусмотренное использование модуля и может указывать как модули, которые будут монтироваться, так и родительские модули, на которые монтируемые модули могут ссылаться. Пример такого описания можно найти в [YANG-NIs]. Конкретная реализация модуля с точками монтирования будет также поддерживать список монтируемых и указанных ссылками модулей. В описании предполагаемого использования и фактической реализации полезно указать, как смонтированные модули будут создаваться и указываться под точкой монтирования в диаграмме дерева.

В таких диаграммах точку монтирования следует рассматривать аналогично контейнеру, использующему группировку. Флаги следует устанавливать с учётом листа config, упомянутого выше, а указанные выше опции монтирования должны быть показаны для узлов верхнего уровня в монтируемом или указанном ссылкой модуле. В следующем примере, взятом из [YANG-Nis], представлен предыдущий пример с установленными модулями YANG ietf-routing [YANG-Routing] и ietf-ospf [OSPF-YANG], узлами из модуля YANG ietf-interfaces [RFC8343], доступными через родительскую ссылку, и узел config со значением true.

     module: ietf-network-instance
       +--rw network-instances
          +--rw network-instance* [name]
             +--rw name           string
             +--rw enabled?       boolean
             +--rw description?   string
             +--rw (ni-type)?
             +--rw (root-type)
                +--:(vrf-root)
                   +--mp vrf-root
                      +--ro rt:routing-state/
                      |  +--ro router-id?
                      |  +--ro control-plane-protocols
                      |     +--ro control-plane-protocol* [type name]
                      |        +--ro ospf:ospf
                      |           +--ro instance* [af]
                      |           ...
                      +--rw rt:routing/
                      |  +--rw router-id?
                      |  +--rw control-plane-protocols
                      |     +--rw control-plane-protocol* [type name]
                      |     +--rw ospf:ospf
                      |        +--rw instance* [af]
                      |           ...
                      +--ro if:interfaces@
                      |  ...
                      +--ro if:interfaces-state@
                      |  ...

Следует подчеркнуть, что модуль ietf-ospf дополняет ietf-routing и хотя он не указан в модуле монтирования схемы (или встроенной библиотеке YANG), в диаграмме дерева не используется специального обозначения монтирования.

Определения точки монтирования, самого по себе, не достаточно для указания используются ли смонтированные модули для данных конфигурации или иных данных. Это определяется листом config модуля ietf-yang-schema-mount, связанным с конкретной точкой монтирования и указывается на смонтированных узлах верхнего уровня. Например, в приведённом выше дереве, где лист config для модуля ietf-routing указывает false, узлы в ветви rt:routing будут иметь другие флаги.

                      +--ro rt:routing/
                      |  +--ro router-id?
                      |  +--ro control-plane-protocols
                         ...

5. Взаимодействие с IANA

Этот документ не требует действий IANA.

6. Вопросы безопасности

Описанные здесь диаграммы деревьев не оказывают влияния на безопасность.

7. Литература

[OSPF-YANG] Yeung, D., Qu, Y., Zhang, J., Chen, I., and A. Lindem, «Yang Data Model for OSPF Protocol», Work in Progress4, draft-ietf-ospf-yang-10, March 2018.

[PYANG] «pyang», February 2018, <https://github.com/mbj4668/pyang>.

[RFC7407] Bjorklund, M. and J. Schoenwaelder, «A YANG Data Model for SNMP Configuration», RFC 7407, DOI 10.17487/RFC7407, December 2014, <https://www.rfc-editor.org/info/rfc7407>.

[RFC7950] Bjorklund, M., Ed., «The YANG 1.1 Data Modeling Language», RFC 7950, DOI 10.17487/RFC7950, August 2016, <https://www.rfc-editor.org/info/rfc7950>.

[RFC8040] Bierman, A., Bjorklund, M., and K. Watsen, «RESTCONF Protocol», RFC 8040, DOI 10.17487/RFC8040, January 2017, <https://www.rfc-editor.org/info/rfc8040>.

[RFC8343] Bjorklund, M., «A YANG Data Model for Interface Management», RFC 8343, DOI 10.17487/RFC8343, March 2018, <https://www.rfc-editor.org/info/rfc8343>.

[SCHEMA-MOUNT] Bjorklund, M. and L. Lhotka, «YANG Schema Mount», Work in Progress5, draft-ietf-netmod-schema-mount-08, October 2017.

[YANG-NIs] Berger, L., Hopps, C., Lindem, A., Bogdanovic, D., and X. Liu, «YANG Model for Network Instances», Work in Progress6, draft-ietf-rtgwg-ni-model-11, March 2018.

[YANG-Routing] Lhotka, L., Lindem, A., and Y. Qu, «A YANG Data Model for Routing Management (NMDA Version)», Work in Progress7, draft-ietf-netmod-rfc8022bis-11, January 2018.

Адреса авторов

Martin Bjorklund

Tail-f Systems

Email: mbj@tail-f.com

Lou Berger (редактор)

LabN Consulting, L.L.C.

Email: lberger@labn.net


Перевод на русский язык

Николай Малых

nmalykh@protokols.ru

1Internet Engineering Task Force — комиссия по решению инженерных задач Internet.

2Internet Engineering Steering Group — комиссия по инженерным разработкам Internet.

3XML Path Language — язык путей XML.

4Опубликовано в RFC 9129. Прим. перев.

5Опубликовано в RFC 8528. Прим. перев.

6Опубликовано в RFC 8529. Прим. перев.

7Опубликовано в RFC 8349. Прим. перев.

Рубрика: RFC | Комментарии к записи RFC 8340 YANG Tree Diagrams отключены

RFC 8345 A YANG Data Model for Network Topologies

Internet Engineering Task Force (IETF)                          A. Clemm
Request for Comments: 8345                                        Huawei
Category: Standards Track                                      J. Medved
ISSN: 2070-1721                                                    Cisco
                                                                R. Varga
                                               Pantheon Technologies SRO
                                                              N. Bahadur
                                                       Bracket Computing
                                                      H. Ananthakrishnan
                                                           Packet Design
                                                                  X. Liu
                                                                   Jabil
                                                              March 2018

Модель данных YANG для сетевой топологии

A YANG Data Model for Network Topologies

PDF

Аннотация

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

Статус документа

Документ относится к категории Internet Standards Track.

Документ является результатом работы IETF1 и представляет согласованный взгляд сообщества IETF. Документ прошел открытое обсуждение и был одобрен для публикации IESG2. Дополнительную информацию о стандартах Internet можно найти в разделе 2 в RFC 7841.

Информацию о текущем статусе документа, ошибках и способах обратной связи можно найти по ссылке https://www.rfc-editor.org/info/rfc8345.

Авторские права

Авторские права (Copyright (c) 2018) принадлежат IETF Trust и лицам, указанным в качестве авторов документа. Все права защищены.

К документу применимы права и ограничения, перечисленные в BCP 78 и IETF Trust Legal Provisions и относящиеся к документам IETF (http://trustee.ietf.org/license-info), на момент публикации данного документа. Прочтите упомянутые документы внимательно, поскольку в них описаны права и ограничения, относящиеся к данному документу. Фрагменты программного кода, включённые в этот документ, распространяются в соответствии с упрощённой лицензией BSD, как указано в параграфе 4.e документа Trust Legal Provisions, без каких-либо гарантий (как указано в Simplified BSD License).

1. Введение

В этом документе вводится абстрактная (базовая) модель данных [RFC3444] YANG [RFC7950] для представления сетей и топологии. Модель делится на две части. Первая часть определяет модель данных сети, которая позволяет указать сетевые иерархии или стеки сетей (т. е. наложенные сети) и поддержку кадастра узлов, содержащихся в сети. Вторая часть дополняет базовую модель сети информацией, описывающей сетевую топологию. В частности, она добавляет концепцию каналов (соединений) и точек завершения для описания соединений узлов в сети. Кроме того, модель данных задает вертикальные отношения между сетями, которые могут быть дополнены для охвата как кадастров, так и топологии сети и/или служб.

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

Модель данных может быть дополнена путем задания отдельных типов сетей и топологии. Например, дополнение модели может включать информацию об атрибутах узлов для конкретного типа сети. Примеры дополненных моделей включают модели данных для топологии канального уровня (L2), варианты топологии сетевого уровня (L3), такие как IGP, IS-IS [RFC1195] и OSPF [RFC2328], данные организации трафика (TE3) [RFC3209] и разные варианты топологии транспорта и служб. Информация для конкретного типа сети будут собираться в отдельные модели, зависящие от сетевой технологии.

Базовая модель данных, описанная в этом документе, подходит для множества вариантов топологии сетей и служб, а также для кадастров. Модель данных позволяет приложениям оперировать с кадастром или топологией любой сети на базовом уровне, где специфика конкретных вариантов кадастра или топологии не требуется. В то же время при рассмотрении конкретного типа сети и дополнении модели данных созданные ранее данные сохраняют свою структуру и представляются в согласованном виде. Это также упрощает представление сетевых иерархий и зависимостей между разными сетевыми компонентами и типами сетей.

               +------------------------+
               |                        |
               | Абстрактная модель сети|
               |                        |
               +------------------------+
                            |
                    +-------+-------+
                    |               |
                    V               V
             +------------+  ..............
             | Абстрактная|  :  Модель(и) :
             |   модель   |  :  кадастра  :
             |  топологии |  :            :
             +------------+  ''''''''''''''
                    |
      +-------------+-------------+-------------+
      |             |             |             |
      V             V             V             V
............  ............  ............  ............
:  Модель  :  :  Модель  :  :  Модель  :  :  Модель  :
: топологии:  : топологии:  : топологии:  : топологии:
:    L1    :  :    L2    :  :    L3    :  :  сервиса :
''''''''''''  ''''''''''''  ''''''''''''  ''''''''''''

Рисунок 1. Структура модели данных сети.

Модуль YANG для абстрактной (базовой) сети, определенный в этом документе, называется ietf-network (параграф 6.1) и содержит список узлов абстрактной сети, а также определяет концепцию «иерархии сетей» (стека). Узел абстрактной сети может быть дополнен в моделях данных кадастра или топологии зависящими от этого кадастра или топологии атрибутами. Иерархия (стек) сетей позволяет данной сети иметь одну или множество «поддерживающих сетей». Отношения между моделью данных базовой сети, моделями данных кадастра и топологическими моделями данных показаны на рисунке 1 (линии из точек указывают возможность дополнения моделей, определенных в этом документе).

Модуль YANG для топологии сети, определенный в этом документе, называется ietf-network-topology (параграф 6.2) и задает базовую топологическую модель с максимальным уровнем абстракции. Модуль определяет граф и компоненты топологии — узлы (node), ребра (edge) и точки завершения (termination point). Узлы (из модуля ietf-network) представляют вершины графа, а соединения — его ребра. Узлы также включают точки завершения для привязки соединений. Сеть может содержать множество топологий, например, топологию разных уровней и топологию наложения. Поэтому модель данных поддерживает связь между топологиями, а также зависимости между узлами и точками завершения разных топологий. Пример топологического стека приведен на рисунке 2.

       +---------------------------------------+
      /            _[X1]_          "Service"  /
     /           _/  :   \_                  /
    /          _/     :    \_               /
   /         _/        :     \_            /
  /         /           :      \          /
 /       [X2]__________________[X3]      /
+---------:--------------:------:-------+
           :              :     :
       +----:--------------:----:--------------+
      /      :              :   :        "L3" /
     /        :              :  :            /
    /         :               : :           /
   /         [Y1]_____________[Y2]         /
  /           *               * *         /
 /            *              *  *        /
+--------------*-------------*--*-------+
                *           *   *
       +--------*----------*----*--------------+
      /     [Z1]_______________[Z2] "Optical" /
     /         \_         *   _/             /
    /            \_      *  _/              /
   /               \_   * _/               /
  /                  \ * /                /
 /                    [Z]                /
+---------------------------------------+

Рисунок 2. Пример иерархии (стека) топологий.

На рисунке 2 показаны три уровня топологии. На верхнем уровне Service показаны отношения между элементами служб типа сервисных функций и цепочек услуг. Топология сетевого уровня L3 показывает элементы уровня L3 (IP), а уровень Optical показывает сетевые элементы физического уровня L1. Сервисные функции в топологии Service отображаются на сетевые элементы топологии L3, которые в свою очередь отображаются на элементы физического уровня топологии Optical. Две сервисных функции (X1 и X3) отображаются на один элемент L3 (Y2) — это может происходить, например, при реализации двух функций на одной виртуальной машине VM4 (или сервере) — и сообща используют сетевые интерфейсы. Один сетевой элемент L3 (Y2) отображается на два элемента уровня Optical (Z2 и Z). Это может быть, например, при подключении одного маршрутизатора IP к разным мультиплексорам ROADM5 в оптическом домене.

Другой пример стека топологий служб показан на рисунке 3.

                         VPN1                       VPN2
      +---------------------+    +---------------------+
     /   [Y5]...           /    / [Z5]______[Z3]      /
    /    /  \  :          /    /  : \_       / :     /
   /    /    \  :        /    /   :   \_    /  :    /
  /    /      \  :      /    /   :      \  /   :   /
 /   [Y4]____[Y1] :    /    /   :       [Z2]   :  /
+------:-------:---:--+    +---:---------:-----:-+
       :        :   :         :          :     :
       :         :   :       :           :     :
       :  +-------:---:-----:------------:-----:-----+
       : /       [X1]__:___:___________[X2]   :     /
       :/         / \_  : :       _____/ /   :     /
       :         /    \_ :  _____/      /   :     /
      /:        /       \: /           /   :     /
     / :       /        [X5]          /   :     /
    /   :     /       __/ \__        /   :     /
   /     :   /    ___/       \__    /   :     /
  /       : / ___/              \  /   :     /
 /        [X4]__________________[X3]..:     /
+------------------------------------------+
                                 L3 Topology

Рисунок 3. Пример топологической иерархии (стека).

На рисунке 3 показаны две топологии сервиса VPN (VPN1 и VPN2), организованные на базе общей топологии L3. Каждая топология сервиса VPN отображается на подмножество узлов общей топологии L3.

Существует множество применений для такой модели данных. Например, в контексте интерфейса в систему маршрутизации I2RS6 узлы сети могут использовать модель данных для понимания общей топологии сети и ее представления сетевому контроллеру. Контроллер может использовать данные подтвержденной топологии для сравнения и согласования своего представления топологии сети с ее представлением управляемыми им элементами. В дополнение к этому узлы сети могут распространять свое понимание для сравнения и согласования между собой или с помощью контроллера. Помимо сетевых элементов и непосредственного контекста самого I2RS, сетевой контроллер может использовать модель данных для представления своего взгляда на топологию, которой он управляет, приложениям на своем северном интерфейсе. Другие случаи, где может быть применена описанная модель данных, рассмотрены в [USECASE-REQS].

В этой модели данных сеть классифицируется как управляемая системой или нет. Если сеть управляется системой, модель автоматически заполняется сервером и представляет динамически определяемую (learned) информацию, которая может быть прочитана из хранилища данных операционного состояния. Модель данных может также служить для создания или изменения сетевой топологии, что может быть связано с моделью кадастра или наложенной (overlay) сетью. Такая сеть не контролируется системой, а вместо этого настраивается клиентом.

Модель данных позволяет сети ссылаться на поддерживающую сеть, поддерживающие узлы, каналы и т. п. Модель также позволяет делить на уровни сеть, расположенную «поверх» сети, контролируемой системой. Этот позволяет настраивать наложенные сети поверх тех сетей, которые были обнаружены. В частности, эта модель данных структурирована для поддержки реализации в форме части эфемерного хранилища данных [RFC8342], требования к которому определены в разделе 3 [RFC8242]. Это позволяет записывать данные сетевой топологии, т. е. обеспечивает возможность настройки клиентом без контроля системы для ссылки на динамически получаемые данные, которые контролируются системой, а не настраиваются клиентом. Простой пример использования может включать наложенную сеть, которая поддерживается динамически определяемой сетевой топологией с маршрутизацией IP. Когда реализация помещает записанные данные для этой модели в эфемерное хранилище, такая сеть может указывать на другую сеть, контролируемую системой.

2. Уровни требований

Ключевые слова необходимо (MUST), недопустимо (MUST NOT), требуется (REQUIRED), нужно (SHALL), не нужно (SHALL NOT), следует (SHOULD), не следует (SHOULD NOT), рекомендуется (RECOMMENDED), не рекомендуется (NOT RECOMMENDED), возможно (MAY), необязательно (OPTIONAL) в данном документе интерпретируются в соответствии с BCP 14 [RFC2119] [RFC8174] тогда и только тогда, когда они выделены шрифтом, как показано здесь.

3. Определения и сокращения

Datastore – хранилище данных

Концептуальное место для хранения и доступа к информации. Хранилище может быть реализовано, например, в виде файла, базы данных, flash-памяти или их комбинации. Хранилище отображается на экземпляр дерева данных YANG (определение из [RFC8342]).

Data subtree – ветвь (субдерево) данных

Конкретный узел данных и узлы, иерархические входящие в него.

IGP

Interior Gateway Protocol — протокол внутреннего шлюза.

IS-IS

Intermediate System to Intermediate System — протокол взаимодействия промежуточных систем.

OSPF

Open Shortest Path First — сначала кратчайший путь (протокол маршрутизации по состоянию канала).

SDN

Software-Defined Networking — программно-определяемая сеть.

URI

Uniform Resource Identifier — унифицированный идентификатор ресурса.

VM

Virtual Machine — виртуальная машина.

4. Структура модели

4.1. Базовая модель сети

Модель данных абстрактной (базовой) сети определена в модуле ietf-network. Ее структура показана на рисунке 4. Обозначения на рисунке соответствуют синтаксису, используемому в [RFC8340].

   module: ietf-network
     +--rw networks
        +--rw network* [network-id]
           +--rw network-id            network-id
           +--rw network-types
           +--rw supporting-network* [network-ref]
           |  +--rw network-ref    -> /networks/network/network-id
           +--rw node* [node-id]
              +--rw node-id            node-id
              +--rw supporting-node* [network-ref node-ref]
                 +--rw network-ref
                 |       -> ../../../supporting-network/network-ref
                 +--rw node-ref       -> /networks/network/node/node-id

Рисунок 4. Структура модели данных абстрактной (базовой) сети.

Модель данных содержит контейнер со списком сетей. Каждая сеть фиксируется в своей записи, указанной network-id.

Сеть имеет тот или иной тип (например L2, L3, OSPF, IS-IS) или относится сразу к нескольким типам. Тип или типы указываются под контейнером network-types. В этой модели он служит лишь целью дополнения. Модули для конкретного типа сети будут позднее добавлять новые узлы данных для представления типа ниже этой цели (т. е. ниже network-types) с помощью дополнения YANG (augmentation).

Когда сеть имеет определенный тип, она будет содержать соответствующий узел данных. Узлы всегда следует представлять с использованием контейнеров присутствия, а не листьев типа empty. Это обеспечивает представление иерархий подтипов сетей в информации экземпляра. Например, экземпляр сети OSPF (который в то же время является сетью L3 unicast IGP) будет содержать под network-types другой контейнер присутствия l3-unicast-igp-network, который в свою очередь будет содержать контейнер присутствия ospf-network. Фактические примеры можно найти в [RFC8346].

Сеть может быть частью иерархии сетей, построенной поверх других сетей. Все такие сети фиксируются в списке supporting-network. Поддерживающей сетью является базовая (underlay) сеть.

Кроме того, сеть содержит кадастр узлов, являющихся ее частями. Узлы фиксируются в своих списках. Каждый узел указывается относительно содержащей его сети идентификатором node-id.

Кроме того, сеть содержит кадастр узлов, являющихся её частями. Узлы фиксируются в своих списках. Каждый узел указывается относительно содержащей его сети идентификатором node-id.

Следует отметить, что узел не существует отдельно от сети — он является частью содержащей его сети. В тех случаях, когда устройство или элемент является частью нескольких сетей или нескольких уровней стека сетей, такое устройство или объект будет представлено множеством узлов (по одному для каждой сети). Иными словами, узел представляет абстракцию устройства для отдельной сети, в которую это устройство входит. Для индикации включения одного устройства или элемента в разные топологии или сети можно создать одну «физическую» сеть со списком узлов для каждого из устройств или элементов. Эту (физическую) сеть (сущности) можно называть базовой (underlay) сетью, а её узлы — узлами других (логических) сетей и узлов. Отметим, что модель данных позволяет определить более одной базовой сети (и узла), что даёт возможность одновременно представлять многоуровневые топологии сетей и служб, а также физическое размещение.

Подобно сети, узел может поддерживаться другими узлами и отображаться на один или множество других узлов в базовой сети. Это фиксируется в списке supporting-node (поддерживающий узел). Результирующая иерархия узлов позволяет также представлять стеки устройств, где узлы одного уровня поддерживаются набором узлов нижележащего уровня. Например,

  • узел router может поддерживаться узлом, представляющим процессор маршрутизатора, и отдельными узлами для разных интерфейсных плат и сервисных модулей;

  • виртуальный маршрутизатор может поддерживаться или размещаться на физическом устройстве, представленном отдельным узлом,

и т. д.

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

Для учёта этих возможностей используется пересмотренная архитектура хранилищ данных [RFC8342]. В частности, для каждой сети источник данных указывается аннотацией метаданных origin [RFC7952] (в соответствии с определением [RFC8342]) — intended для данных, настраиваемых клиентским приложением, и learned для раскрываемых данных. Раскрываемые данные автоматически заполняются как часть рабочего хранилища. Настраиваемые данные являются частью конфигурации и предусмотренных хранилищ. Настраиваемые данные, которые действительно применяются, дополнительно отражаются в рабочем хранилище. Данные в этом хранилище всегда будут иметь полную целостность ссылок. Если сконфигурированный элемент данных (например, узел) имеет «оборванную» ссылку на отсутствующий элемент (например, поддерживающий узел), этот сконфигурированный элемент будет автоматически удалён из рабочего хранилища и останется лишь в хранилище предполагаемой конфигурации. Для устранения конфликта клиентское приложение (например, контроллер SDN) должно указать корректные ссылки на поддерживающие ресурсы.

4.2. Базовая модель данных сетевой топологии

Модель данных абстрактной (базовой) сетевой топологии определена в модуле ietf-network-topology. Она построена на основе модели данных сети, определенной в модуле ietf-network и дополненной каналами (соединения между узлами) и точками завершения (содержащиеся в узлах привязки к каналам). Структура модели топологии сети представлена на рисунке 5. Синтаксис обозначений соответствует [RFC8340].

Узел имеет список точек завершения, которые служат для привязки каналов. Примером точки завершения может служить физический или логический порт, более обобщенно — интерфейс.

Подобно узлу, точка завершения может поддерживаться базовой (нижележащей) точкой завершения, содержащейся в узле поддержки базовой сети.

   module: ietf-network-topology
     augment /nw:networks/nw:network:
       +--rw link* [link-id]
          +--rw link-id            link-id
          +--rw source
          |  +--rw source-node?   -> ../../../nw:node/node-id
          |  +--rw source-tp?     leafref
          +--rw destination
          |  +--rw dest-node?   -> ../../../nw:node/node-id
          |  +--rw dest-tp?     leafref
          +--rw supporting-link* [network-ref link-ref]
             +--rw network-ref
             |       -> ../../../nw:supporting-network/network-ref
             +--rw link-ref       leafref
     augment /nw:networks/nw:network/nw:node:
       +--rw termination-point* [tp-id]
          +--rw tp-id                           tp-id
          +--rw supporting-termination-point*
                  [network-ref node-ref tp-ref]
             +--rw network-ref
             |       -> ../../../nw:supporting-node/network-ref
             +--rw node-ref
             |       -> ../../../nw:supporting-node/node-ref
             +--rw tp-ref         leafref

Рисунок 5. Структура модели данных топологии абстрактной (базовой) сети.

Каналы обозначаются идентификаторами link-id, которые однозначно указывают канал в рамках данной топологии. Каналы относятся к типу «точка-точка» и являются односторонними. Следовательно, каждый канал имеет источник и цель. И то и другое указывает соответствующий узел, а также точку завершения на этом узле. Подобно узлу, канал может отображаться на один или множество каналов (которые завершаются в соответствующих базовых точках привязки) базовой топологии. Это отображение фиксируется в списке supporting-link.

4.3. Расширение модели данных

Чтобы создать производную модель данных для конкретного типа сети, базовая модель может быть расширена. Это можно сделать примерно так — создается новый модуль YANG для нового типа сети и в этом модуле определяются дополнения к модулям ietf-network и ietf-network-topology.

Начнем дополнение с модуля ietf-network. Сначала требуется определить новый тип сети — это делается путем определения контейнера присутствия, который представляет новый тип сети. Новый тип добавляется под контейнером базовых типов. Далее определяются узлы данных для всех параметров узла, относящиеся к конкретному типу сети, и добавляются в список узлов. Новые узлы данных могут быть определены как условные (when) по наличию соответствующего типа в содержащей сети. При наличии каких-либо требований или ограничений применительно к иерархии сетей, такие как потребность нового типа в определенной базовой сети, можно задать соответствующие ограничения, а также дополнения к списку поддерживающих сетей. Однако следует соблюдать осторожность и избегать слишком большого числа ограничений.

Далее определяются дополнения для модуля ietf-network-topology с указанием узлов данных для параметров каналов и точек завершения, которые относятся к новому типу сети. Эти узлы добавляются в списки каналов и точек завершения, соответственно. Узлы данных также могут определяться по условию присутствия соответствующего типа сети в содержащей сети с помощью оператора when.

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

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

4.4. Обсуждение и выбор некоторых решений

4.4.1. Структура контейнера

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

4.4.2. Базовые иерархии и отображения

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

Когда требуются более конкретные отображения между верхним и нижним уровнем, эта информация может фиксироваться в модулях дополнения. Например, для указания того, что точка завершения определенного типа сети отображается на интерфейс, может быть добавлен модуль дополнения для точки завершения. Дополнение создает лист типа interface-ref, который указывает на соответствующий интерфейс [RFC8343]. Аналогично, если узел отображается на конкретное устройство или элемент сети, модуль дополнения может дополнять данные узла листом, указывающим на элемент сети.

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

4.4.3. Изменения в базовых сетях

В сети возможны разные флуктуации, даже если эта сеть является базовой для других сетей. При удалении поддерживающего узла, канала или точки завершения поддерживающие leafref в наложении становятся «подвешенными». Для таких случаев в модели данных используется конструкция require-instance языка YANG 1.1 [RFC7950].

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

Приложение, поддерживающее наложение, отвечает за возможность оттока в базовую сеть. Когда сервер получает запрос на настройку наложенной сети, ему следует проверить, указывают ли поддерживающие узлы/каналы/точки завершения на реально существующие узлы базовой сети, т. е. убедиться в том, что узлы отражены в хранилище рабочего состояния. Запросы конфигурации, в которых узлы/каналы/точки завершения указывают на несуществующие узлы, следует отвергать. Приложение отвечает за обновление наложений при последующем удалении узла/канала/точки завершения. По этой причине приложение может подписаться на обновления в базовой сети, например, с помощью механизмов, определенных в [YANG-Push].

4.4.4. Использование группировок

Модель данных использует группировки вместо простого определения узлов данных как встроенных (inline). Это упрощает включение соответствующих узлов данных в уведомления, не требуя заново определять каждый включаемый узел данных. Однако это усложняет задание ограничений, поскольку ограничения, вовлекающие узлы данных вне группировки требуется задавать вместе с оператором uses при использовании группировки. Это также означает, что ограничения и операторы языка описания путей (XPath7) требуется указывать так, чтобы они сначала перемещались «вниз» и выбирали весь набор узлов, а не просто задавать их для отдельных узлов данных.

4.4.5. Тип и направленность соединений

Модель топологических данных включает каналы, которые относятся к типу «точка-точка» и являются односторонними. Модель не поддерживает напрямую многоточечные и двухсторонние каналы. Это может показаться ограничением, однако такое решение делает модель простой и универсальной, позволяя использовать ее в приложениях, применяющих алгоритмы на основе графов. Двухстороннее соединение можно представить парой однонаправленных. Многоточечные сети можно представить псевдоузлами (например, как в IS-IS). Внедрение иерархии, в которой узлы одного уровня отображаются на множество узлов другого уровня и внедрение соединений на этом уровне позволяет представить топологии, не являющиеся соединениями «точка-точка».

4.4.6. Многодомность и агрегирование каналов

Канал завершается одной точкой, а не множеством их. Соединения, включающие многодомность и агрегирование, необходимо представлять множеством соединений «точка-точка», определяя на более высоком уровне канал, образуемых этими отдельными соединениями.

4.4.7. Избыточность отображения

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

4.4.8. Типизация

Типы сетей представляются с использованием контейнера, который содержит узел данных для каждого из имеющихся в сети типов. Сеть может включать одновременно несколько типов сетей, поэтому применяется контейнер вместо конструкции case, причем каждый тип сети в свою очередь представляется выделенным контейнером присутствия. Причина того, чтобы не использовать просто пустой лист или (что еще проще) отказаться от контейнера network и просто использовать взамен лист-список (leaf-list) network-type, заключается в возможности представления иерархий классов для типов сетей, где один тип уточняет другой. Контейнеры для конкретного типа сети определяются в связанных с сетью модулях, дополняющих контейнер network-types.

4.4.9. Представление одного устройства в нескольких сетях

Одно общее требование связано с возможностью указать, что одно и то же устройство может входить в разные сети и топологии. Однако модель данных определяет узел относительно сети, в которой он расположен. Один узел не может быть частью разных топологий. Во многих случаях узел будет абстракцией отдельного устройства в сети. Для отражения принадлежности одного устройства множеству топологий можно выбрать специальную модель — вводится новый тип сети для представления физической сети («устройства») с узлами, представляющими устройства. Эта сеть формирует базовую сеть для расположенной выше логической сети с отображением узлов логической сети на узлы физической сети.

Этот сценарий представлен на рисунке 6, где приведены 3 сети с двумя узлами в каждой. Физическая сеть (P на рисунке) состоит из двух узлов (D1 и D2), являющихся устройствами. Вторая сеть (X) имеет третью (Y) в качестве базовой и для обеих сетей X и Y физическая сеть P служит базовой. X1 имеет базовые узлы Y1 и D1, а для Y1 узел D1 является базовым. Аналогично, X2 имеет базовые узлы Y2 и D2, а Y2 имеет базовый узел D2. Размещение X1 и Y1 на одном физическом узле (D1) легко увидеть на рисунке.

                  +---------------------+
                 /   [X1]____[X2]      /  X (наложенный сервис)
                +----:--:----:--------+
                  ..:    :..: :
         ........:     ....: : :....
  +-----:-------------:--+    :     :...
 /   [Y1]____[Y2]....:  /      :..      :
+------|-------|-------+          :..    :...
 Y(L3) |       +---------------------:-----+ :
       |                         +----:----|-:----------+
       +------------------------/---[D1]  [D2]         /
                               +----------------------+
                                 P (физическая сеть)

Рисунок 6. Пример иерархии топологий — множественное наложение.

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

4.4.10. Поддержка настроенной клиентом и управляемой системой топологии

YANG требует обозначать узлы данных как конфигурационные (config true) или операционные (config false), но не оба сразу и важно, чтобы вся информация о сети, включая вертикальные зависимости между сетями, была записана в одну согласованную модель данных. В большинстве случаев топологическая информация о сети получается пуем обнаружения и топология является свойством сети, которое отражается в модели данных. Тем не менее, некоторые типы топологии должны быть также настраиваемыми приложением (например, в случае наложенной топологии).

В модели данных YANG для сетевой топологии все данные обозначаются как config true. Различие между данными, которые реально настраиваются, и данными которые действуют (включая обнаруженные данные), обеспечивается через хранилища, представленные как часть архитектуры NMDA8 [RFC8342]. Обнаруженные данные сетевой топологии автоматически становятся частью хранилища рабочего состояния (<operational>). Они «управляются системой». Настраиваемая топология сети создается как часть хранилища предполагаемой конфигурации (<intended>) и только после ее вступления в силу эта топология становится частью рабочего хранилища (<operational>).

В общем случае настроенная сетевая топология будет указывать базовую топологию и включать информацию об уровнях, такую как поддерживающие узлы, каналы и точки завершения. Поддерживающие объекты должны быть созданы в рабочем хранилище. Если настроенный элемент данных (такой как узел) имеет «подвешенную» ссылку на несуществующий элемент данных (например, поддерживающий узел), настроенный элемент будет автоматически удален из хранилища <operational> и отобразиться лишь в <intended>. Клиентское приложение может разрешить такую проблему и убедиться в корректности настройки ссылок на поддерживающие ресурсы.

Для каждой сети источник данных указывается в аннотации метаданных origin [RFC7952], определенной в [RFC8342]. В общем случае источником обнаруженных сетевых данных является обучение (learned), а настроенных — intended.

4.4.11. Идентификаторы в виде строк и URI

Текущая модель данных определяет идентификаторы узлов, сетей, каналов и точек завершения как URI. Дополнительно их можно определить в форме строк.

Дело в том, что строки проще реализовать. Причина выбора URI состоит в том, что топология, узел, точка завершения существует в более широком контексте, поэтому полезна возможность сопоставлять идентификаторы в разных системах. Хотя строки являются универсальным типом данных и проще для понимания человеком, с ними связано много путаницы. Обычно строки имеют некую структуру, которая задается «магически» и эту «магию» нужно передать каждой системе, работающей с данными. URI делают структуру явной и обеспечивают дополнительную семантику, поскольку, в отличие от строк в свободной форме, могут быть переданы анализатору URI (resolver), который может указать дополнительные ресурсы, связанные с URI. Это свойство важно при интеграции топологических данных в более крупную и сложную систему.

5. Взаимодействие с другими модулями YANG

Эта модель использует типы данных, определенные в [RFC6991].

Это независимая от протоколов модель данных YANG с топологической информацией. Она отделена и не связана с моделями данных, служащими для настройки протоколов маршрутизации или маршрутной информации (например, модуль YANG ietf-routing [RFC8022]).

Модель данных соответствует требованиям к эфемерному состоянию, заданным в [RFC8242]. Для контролируемых системой эфемерных данных топологии процесс, которому поручено поддерживать топологическую информацию, будет загружать данные из процесса маршрутизации (например, OSPF) в рабочее хранилище данных, не полагаясь на конфигурационное хранилище.

6. Модули YANG

6.1. Определение абстрактной сети — ietf-network

   <CODE BEGINS> file "ietf-network@2018-02-26.yang"

   module ietf-network {
     yang-version 1.1;
     namespace "urn:ietf:params:xml:ns:yang:ietf-network";
     prefix nw;

     import ietf-inet-types {
       prefix inet;
       reference
         "RFC 6991: Common YANG Data Types";
     }

     organization
       "Рабочая группа IETF I2RS (интерфейс в систему маршрутизации)";

     contact
       "WG Web:    <https://datatracker.ietf.org/wg/i2rs/> 
        WG List:   <mailto:i2rs@ietf.org> 

        Editor:    Alexander Clemm
                   <mailto:ludwig@clemm.org> 

        Editor:    Jan Medved
                   <mailto:jmedved@cisco.com> 

        Editor:    Robert Varga
                   <mailto:robert.varga@pantheon.tech> 

        Editor:    Nitin Bahadur
                   <mailto:nitin_bahadur@yahoo.com> 

        Editor:    Hariharan Ananthakrishnan
                   <mailto:hari@packetdesign.com> 

        Editor:    Xufeng Liu
                   <mailto:xufeng.liu.ietf@gmail.com>"; 

     description
       "Этот модуль определяет базовую модель данных общего назначения 
        для набора узлов в сети. Определения узлов потом применяются в 
        топологии и кадастре сети.

        Авторские права (Copyright (c) 2018) принадлежат IETF Trust 
        и указанным авторам. Все права защищены.

        Распространение и использование с исходной или двоичной форме
        с внесением изменений или без них определяется лицензией
        Simplified BSD License, изложенной в разделе 4.c IETF Trust
        Legal Provisions для документов IETF
        (https://trustee.ietf.org/license-info). 

        Данная версия модуля YANG является частью RFC 8345, где указаны
        дополнительные правовые аспекты.";

     revision 2018-02-26 {
       description
         "Первоначальный вариант.";
       reference
         "RFC 8345: A YANG Data Model for Network Topologies";
     }

     typedef node-id {
       type inet:uri;
       description
         "Идентификатор узла. Точная структура node-id будет
          определяться реализацией. Например, некоторые 
          реализации МОГУТ выбрать URI с включением network-id
          как части пути. Идентификаторы СЛЕДУЕТ выбирать так,
          чтобы один и тот же узел в реальной сетевой топологии
          всегда указывался одним и тем же идентификатором, даже при
          размещении модели данных в раздельных хранилищах. Реализация
          МОЖЕТ фиксировать в идентификаторах некую семантику, например,
          для указания типа узла.";
     }

     typedef network-id {
       type inet:uri;
       description
	  "Идентификатор сети. Точная структура network-id будет зависеть от
	   реализации. Идентификаторы СЛЕДУЕТ выбирать так, чтобы одна и та же
          сеть всегда указывалась одним идентификатором даже при создании
          экземпляров модели в разных хранилищах данных. Реализация МОЖЕТ
          зафиксировать семантику идентификаторов (например, для указания
          типа сети).";
     }

     grouping network-ref {
       description
         "Содержит информацию, требуемую для ссылки на сеть (например, базовая сеть).";
       leaf network-ref {
         type leafref {
           path "/nw:networks/nw:network/nw:network-id";
         require-instance false;
         }
         description
           "Служит для ссылки на сеть (например, базовая сеть).";
       }
     }

     grouping node-ref {
       description
         "Содержит информацию, требуемую для ссылки на узел.";
       leaf node-ref {
         type leafref {
           path "/nw:networks/nw:network[nw:network-id=current()/../"+
             "network-ref]/nw:node/nw:node-id";
           require-instance false;
         }
         description
           "Служит для ссылки на узел. Узлы идентифицируются относительно сети,
            которая их содержит.";
       }
       uses network-ref;
     }

     container networks {
       description
         "Служит контейнером верхнего уровня для списка сетей.";
       list network {
         key "network-id";
         description
           "Описывает сеть. Сеть обычно содержит опись узлов, топологическую
            информацию (дополненную моделью данных топологии сети) и сведения
            об уровнях.";
         leaf network-id {
           type network-id;
           description
             "Идентифицирует сеть.";
         }
         container network-types {
           description
             "Служит в качестве цели дополнения. Тип сети указывается
              соответствующими контейнерами присутствия, добавленными
              в этот контейнер.";
         }
         list supporting-network {
           key "network-ref";
           description
             "Базовая сеть, используемая для представления многоуровневой топологии.";
           leaf network-ref {
             type leafref {
               path "/nw:networks/nw:network/nw:network-id";
             require-instance false;
             }
             description
               "Ссылка на базовую сеть.";
           }
         }
         list node {
           key "node-id";
           description
             "Опись узлов сети.";
           leaf node-id {
             type node-id;
             description
               "Уникальный идентификатор узла в сети.";
           }
           list supporting-node {
             key "network-ref node-ref";
             description
               "Представляет другой узел, находящийся в базовой сети и 
                поддерживающий данный узел. Служит для представления 
                многоуровневой структуры.";
             leaf network-ref {
               type leafref {
                 path "../../../nw:supporting-network/nw:network-ref";
               require-instance false;
               }
               description
                 "Указывает базовую сеть, к которой относится базовый узел.";
             }
             leaf node-ref {
               type leafref {
                 path "/nw:networks/nw:network/nw:node/nw:node-id";
               require-instance false;
               }
               description
                 "Указывает сам базовый узел.";
             }
           }
         }
       }
     }
   }

   <CODE ENDS>

6.2. Определение топологии абстрактной сети — ietf-network-topology

   <CODE BEGINS> file "ietf-network-topology@2018-02-26.yang"

   module ietf-network-topology {
     yang-version 1.1;
     namespace "urn:ietf:params:xml:ns:yang:ietf-network-topology";
     prefix nt;

     import ietf-inet-types {
       prefix inet;
       reference
         "RFC 6991: Common YANG Data Types";
     }
     import ietf-network {
       prefix nw;
       reference
         "RFC 8345: A YANG Data Model for Network Topologies";
     }

     organization
       "Рабочая группа IETF I2RS (интерфейс в систему маршрутизации)";

     contact
       "WG Web:    <https://datatracker.ietf.org/wg/i2rs/> 
        WG List:   <mailto:i2rs@ietf.org> 

        Editor:    Alexander Clemm
                   <mailto:ludwig@clemm.org> 

        Editor:    Jan Medved
                   <mailto:jmedved@cisco.com> 

        Editor:    Robert Varga
                   <mailto:robert.varga@pantheon.tech> 

        Editor:    Nitin Bahadur
                   <mailto:nitin_bahadur@yahoo.com> 

        Editor:    Hariharan Ananthakrishnan
                   <mailto:hari@packetdesign.com> 

        Editor:    Xufeng Liu
                   <mailto:xufeng.liu.ietf@gmail.com>"; 

     description
       "Этот модуль определяет общую базовую модель сетевой топологии,
        дополняющую базовую модель данных сети каналами для подключения
        узлов, а также точками завершения каналов на узлах сети.

        Авторские права (Copyright (c) 2018) принадлежат IETF Trust 
        и указанным авторам. Все права защищены.

        Распространение и использование с исходной или двоичной форме
        с внесением изменений или без них определяется лицензией
        Simplified BSD License, изложенной в разделе 4.c IETF Trust
        Legal Provisions для документов IETF
        (https://trustee.ietf.org/license-info). 

        Данная версия модуля YANG является частью RFC 8345, где указаны
        дополнительные правовые аспекты.";

     revision 2018-02-26 {
       description
         "Первый выпуск.";
       reference
         "RFC 8345: A YANG Data Model for Network Topologies";
     }

     typedef link-id {
       type inet:uri;
       description
         "Идентификатор канала в топологии. Точная структура link-id
          зависит от реализации. Идентификаторы СЛЕДУЕТ выбирать так,
          что данный канал в реальной сетевой топологии всегда будет
          указываться одним идентификатором даже при размещении модели
          данных в раздельных хранилищах. Реализация МОЖЕТ выбрать
          семантику идентификаторов, например, указывать тип канала 
          и/или тип топологии, к которой относится канал.";
     }

     typedef tp-id {
       type inet:uri;
       description
         "Идентификатор точки завершения на узле. Точная структура tp-id
          зависит от реализации. Идентификаторы СЛЕДУЕТ выбирать так,
          данная точка завершения всегда будет указываться одним
          идентификатором даже при размещении модели данных в
          раздельных хранилищах. Реализация МОЖЕТ выбрать семантику
          идентификаторов, например, указывать тип точки завершения 
          и/или тип узла, к которому относится точка завершения.";
     }

     grouping link-ref {
       description
         "Эта группировка может служить для указания канала в
          конкретной сети. Хотя этот модуль не применяет группировку,
          она определена для удобства дополнения модулей.";
       leaf link-ref {
         type leafref {
           path "/nw:networks/nw:network[nw:network-id=current()/../"+
             "network-ref]/nt:link/nt:link-id";
           require-instance false;
         }
         description
           "Тип для абсолютной ссылки на экземпляр канала (этот тип не
            следует применять для относительных ссылок, используя для
            них относительный путь.)";
       }
       uses nw:network-ref;
     }

     grouping tp-ref {
       description
         "Эта группировка может служить для указания точки завершения
          на конкретном узле. Хотя этот модуль не применяет группировку,
          она определена для удобства дополнения модулей.";
       leaf tp-ref {
         type leafref {
           path "/nw:networks/nw:network[nw:network-id=current()/../"+
             "network-ref]/nw:node[nw:node-id=current()/../"+
             "node-ref]/nt:termination-point/nt:tp-id";
           require-instance false;
         }
         description
           "Тип для абсолютной ссылки на точку завершения
            (этот тип не следует применять для относительных ссылок,
            используя для них относительный путь.)";
       }
       uses nw:node-ref;
     }

     augment "/nw:networks/nw:network" {
       description
         "Добавить каналы в модель данных сети.";
       list link {
         key "link-id";
         description
           "Сетевой канал соединяет локальный (источник) и удаленный
            (получатель) узел через набор точек завершения 
            соответствующих узлов. Можно иметь несколько каналов между
            парой узлов. Канал можно перемещать между точками
            завершения. Поэтому для однозначного различения каналов
            каждый из них указывается выделенным идентификатором.
            Отметим, что канал моделирует соединение «точка-точка», а
            не многоточечное.";
         leaf link-id {
           type link-id;
           description
             "Идентификатор канала в топологии. Канал связан с
              топологией, к которой он относится.";
         }
         container source {
           description
             "Контейнер, содержащий логический источник конкретного канала.";
           leaf source-node {
             type leafref {
               path "../../../nw:node/nw:node-id";
               require-instance false;
             }
             description
               "Идентификатор узла-источника. Должен быть в той же топологии.";
           }
           leaf source-tp {
             type leafref {
               path "../../../nw:node[nw:node-id=current()/../"+
                 "source-node]/termination-point/tp-id";
               require-instance false;
             }
             description
               "Эта точка завершения размещается внутри узла-источника и
                завершает канал.";
           }
         }

         container destination {
           description
             "Контейнер, содержащий логического получателя конкретного канала.";
           leaf dest-node {
             type leafref {
               path "../../../nw:node/nw:node-id";
             require-instance false;
             }
             description
               "Идентификатор узла-получателя. Должен быть в той же топологии.";
           }
           leaf dest-tp {
             type leafref {
               path "../../../nw:node[nw:node-id=current()/../"+
                 "dest-node]/termination-point/tp-id";
               require-instance false;
             }
             description
               "Эта точка завершения размещается внутри узла-получателя и
                завершает канал.";
           }
         }
         list supporting-link {
           key "network-ref link-ref";
           description
             "Указывает канал или каналы, от которых данный канал зависит.";
           leaf network-ref {
             type leafref {
               path "../../../nw:supporting-network/nw:network-ref";
             require-instance false;
             }
             description
               "Этот лист указывает топологию, в которой присутствует 
                поддерживающий канал.";
           }

           leaf link-ref {
             type leafref {
               path "/nw:networks/nw:network[nw:network-id=current()/"+
                 "../network-ref]/link/link-id";
               require-instance false;
             }
             description
               "Этот лист указывает канал, который является базы поддержки
                данного канала. Петли ссылок, где канал указывает себя
                как свою же базу напрямую или опосредованно, не разрешены.";
           }
         }
       }
     }
     augment "/nw:networks/nw:network/nw:node" {
       description
         "Дополняет точки завершения каналов. Эти точки могут в конце
          концов отображаться на интерфейсы.";
       list termination-point {
         key "tp-id";
         description
           "Точка завершения может быть окончанием канала. В зависимости
            от топологии такая точка может указывать, например, на порт
            или интерфейс.";
         leaf tp-id {
           type tp-id;
           description
             "Идентификатор точки завершения.";
         }
         list supporting-termination-point {
           key "network-ref node-ref tp-ref";
           description
             "Этот список указывает все точки завершения, от которых 
              данная точка завершения зависит или куда отображается.
              Эти точки будут сами по себе содержаться в поддерживающем
              узле. Эта информация о зависимостях может быть выведена из
              зависимостей между каналами. Поэтому не требуется отдельно
              настраивать этот элемент и нет необходимости формулировать
              соответствующее ограничение. Нужная информация просто
              обеспечивается реализующей системой.";
           leaf network-ref {
             type leafref {
               path "../../../nw:supporting-node/nw:network-ref";
             require-instance false;
             }
             description
               "Этот лист указывает, в какой топологии присутствует
                поддерживающая точка завершения.";
           }
           leaf node-ref {
             type leafref {
               path "../../../nw:supporting-node/nw:node-ref";
             require-instance false;
             }
             description
               "Этот лист указывает, в каком узле присутствует
                поддерживающая точка завершения.";
           }
           leaf tp-ref {
             type leafref {
               path "/nw:networks/nw:network[nw:network-id=current()/"+
                 "../network-ref]/nw:node[nw:node-id=current()/../"+
                 "node-ref]/termination-point/tp-id";
               require-instance false;
             }
             description
               "Ссылка на базовый узел (этот узел должен находиться
                в другой топологии).";
           }
         }
       }
     }
   }

   <CODE ENDS>

7. Взаимодействие с IANA

Этот документ регистрирует перечисленные ниже URI пространств имен в реестре IETF XML Registry [RFC3688].

   URI: urn:ietf:params:xml:ns:yang:ietf-network
   Registrant Contact: The IESG.
   XML: N/A; the requested URI is an XML namespace.

   URI: urn:ietf:params:xml:ns:yang:ietf-network-topology
   Registrant Contact: The IESG.
   XML: N/A; the requested URI is an XML namespace.

   URI: urn:ietf:params:xml:ns:yang:ietf-network-state
   Registrant Contact: The IESG.
   XML: N/A; the requested URI is an XML namespace.

   URI: urn:ietf:params:xml:ns:yang:ietf-network-topology-state
   Registrant Contact: The IESG.
   XML: N/A; the requested URI is an XML namespace.

Этот документ регистрирует в реестре  YANG Module Names [RFC6020] указанные ниже модули YANG.

   Name:      ietf-network
   Namespace: urn:ietf:params:xml:ns:yang:ietf-network
   Prefix:    nw
   Reference: RFC 8345

   Name:      ietf-network-topology
   Namespace: urn:ietf:params:xml:ns:yang:ietf-network-topology
   Prefix:    nt
   Reference: RFC 8345

   Name:      ietf-network-state
   Namespace: urn:ietf:params:xml:ns:yang:ietf-network-state
   Prefix:    nw-s
   Reference: RFC 8345

   Name:      ietf-network-topology-state
   Namespace: urn:ietf:params:xml:ns:yang:ietf-network-topology-state
   Prefix:    nt-s
   Reference: RFC 8345

8. Вопросы безопасности

Заданные в этом документе модули YANG определяют схему для данных, которые предназначены для доступа через сеть с помощью протоколов управления, таких как NETCONF [RFC6241] или RESTCONF [RFC8040]. Нижним уровнем NETCONF является защищенный сетевой транспорт с обязательной реализацией протокола SSH9 [RFC6242], а нижним уровнем RESTCONF — протокол HTTPS с обязательной реализацией TLS [RFC5246].

Модель контроля доступа NETCONF [RFC8341] позволяет разрешать доступ к заданному подмножеству доступных операций и содержимого NETCONF или RESTCONF лишь уполномоченным пользователям NETCONF или RESTCONF.

Топология и опись сети, создаваемые этими модулями, раскрывают информацию о структуре сети, которая может помочь атакующим. Хотя в модулях и не определяется персональная информация, некоторые идентификаторы узлов могут быть связаны с конкретными устройствами, а те — с конечными пользователями, поэтому конфиденциальность также имеет значение.

Модули YANG определяют информацию, которая в некоторых экземплярах может быть настраиваемой, например для наложенных топологий, создаваемых клиентскими приложениями. В таких случаях вредоносный клиент может внести нежелательную топологию. В частности, он может попытаться удалить или добавить узел, канал или точку завершения путем создания нужных ему элементов в узле, канале или точке завершения, соответственно. В случае изучения топологии сервер будет автоматически предотвращать такие попытки. В случае настраиваемой топологии, т. е. когда источником данных является хранилище intended, нежелательная конфигурация может быть приведена в действие и отражена в рабочем хранилище, что приведет к нарушению обслуживания. Например, топология может быть «обрезана» (cut) или сделана неоптимальной, что ведет к росту потребления ресурсов базовой сети в результате неэффективности маршрутизации и использования пропускной способности. Кроме того, этом может вести к снижению качества обслуживания и даже полному его прекращению. По этим причинам важно активно применять модель контроля доступа NETCONF для предотвращения изменений топологии несанкционированными клиентами.

В этих модулях данных YANG имеется множество узлов, для которых возможна запись, создание и/или удаление (значение config true, которое устанавливается по умолчанию). Такие узлы могут считаться деликатными или уязвимыми в некоторых сетевых средах. Операции записи (например, edit-config) в такие узлы без подобающей защиты могут оказать негативное влияние на работу сети. Ниже перечислены узлы и субдеревья, которые могут быть уязвимы.

В модуле ietf-network:

  • network — вредоносный клиент может пытаться удалить или добавить сеть в целью удаления наложенной топологии или добавления ненужного наложения;

  • supporting network — вредоносный клиент может пытаться нарушить логическую структуру модели, что приведет к отсутствию общей целостности и затруднит, например, поиск неполадок в многоуровневой топологии;

  • node — вредоносный клиент может пытаться удалить или добавить узел, например, для нарушения работы наложенной топологии;

  • supporting node — вредоносный клиент может пытаться сменить поддерживающий узел для нарушения уровней наложения.

В модуле ietf-network-topology:

  • link — вредоносный клиент может пытаться удалить канал из топологии, добавить новый канал, манипулировать уровнем канала в поддерживающих каналах, а также менять источник или получателя канала; в любом случае структура топологии может быть нарушена и это может приводить к неоптимальной топологии наложения;

  • termination point — вредоносный клиент может пытаться удалить точки завершения с узла, добавить «фантомные» точки на узел или изменить зависимости уровней для точек завершения с целью нарушения целостности топологии и возможного нарушения порядка операций наложения.

9. Литература

9.1. Нормативные документы

[RFC2119] Bradner, S., «Key words for use in RFCs to Indicate Requirement Levels», BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.

[RFC3688] Mealling, M., «The IETF XML Registry», BCP 81, RFC 3688, DOI 10.17487/RFC3688, January 2004, <https://www.rfc-editor.org/info/rfc3688>.

[RFC5246] Dierks, T. and E. Rescorla, «The Transport Layer Security (TLS) Protocol Version 1.2», RFC 5246, DOI 10.17487/RFC5246, August 2008, <https://www.rfc-editor.org/info/rfc5246>.

[RFC6020] Bjorklund, M., Ed., «YANG — A Data Modeling Language for the Network Configuration Protocol (NETCONF)», RFC 6020, DOI 10.17487/RFC6020, October 2010, <https://www.rfc-editor.org/info/rfc6020>.

[RFC6241] Enns, R., Ed., Bjorklund, M., Ed., Schoenwaelder, J., Ed., and A. Bierman, Ed., «Network Configuration Protocol (NETCONF)», RFC 6241, DOI 10.17487/RFC6241, June 2011, <https://www.rfc-editor.org/info/rfc6241>.

[RFC6242] Wasserman, M., «Using the NETCONF Protocol over Secure Shell (SSH)», RFC 6242, DOI 10.17487/RFC6242, June 2011, <https://www.rfc-editor.org/info/rfc6242>.

[RFC6991] Schoenwaelder, J., Ed., «Common YANG Data Types», RFC 6991, DOI 10.17487/RFC6991, July 2013, <https://www.rfc-editor.org/info/rfc6991>.

[RFC7950] Bjorklund, M., Ed., «The YANG 1.1 Data Modeling Language», RFC 7950, DOI 10.17487/RFC7950, August 2016, <https://www.rfc-editor.org/info/rfc7950>.

[RFC8040] Bierman, A., Bjorklund, M., and K. Watsen, «RESTCONF Protocol», RFC 8040, DOI 10.17487/RFC8040, January 2017, <https://www.rfc-editor.org/info/rfc8040>.

[RFC8174] Leiba, B., «Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words», BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.

[RFC8341] Bierman, A. and M. Bjorklund, «Network Configuration Access Control Model», STD 91, RFC 8341, DOI 10.17487/RFC8341, March 2018, <https://www.rfc-editor.org/info/rfc8341>.

[RFC8342] Bjorklund, M., Schoenwaelder, J., Shafer, P., Watsen, K., and R. Wilton, «Network Management Datastore Architecture (NMDA)», RFC 8342, DOI 10.17487/RFC8342, March 2018, <https://www.rfc-editor.org/info/rfc8342>.

9.2. Дополнительная литература

[RFC1195] Callon, R., «Use of OSI IS-IS for routing in TCP/IP and dual environments», RFC 1195, DOI 10.17487/RFC1195, December 1990, <https://www.rfc-editor.org/info/rfc1195>.

[RFC2328] Moy, J., «OSPF Version 2», STD 54, RFC 2328, DOI 10.17487/RFC2328, April 1998, <https://www.rfc-editor.org/info/rfc2328>.

[RFC3209] Awduche, D., Berger, L., Gan, D., Li, T., Srinivasan, V., and G. Swallow, «RSVP-TE: Extensions to RSVP for LSP Tunnels», RFC 3209, DOI 10.17487/RFC3209, December 2001, <https://www.rfc-editor.org/info/rfc3209>.

[RFC3444] Pras, A. and J. Schoenwaelder, «On the Difference between Information Models and Data Models», RFC 3444, DOI 10.17487/RFC3444, January 2003, <https://www.rfc-editor.org/info/rfc3444>.

[RFC7951] Lhotka, L., «JSON Encoding of Data Modeled with YANG», RFC 7951, DOI 10.17487/RFC7951, August 2016, <https://www.rfc-editor.org/info/rfc7951>.

[RFC7952] Lhotka, L., «Defining and Using Metadata with YANG», RFC 7952, DOI 10.17487/RFC7952, August 2016, <https://www.rfc-editor.org/info/rfc7952>.

[RFC8022] Lhotka, L. and A. Lindem, «A YANG Data Model for Routing Management», RFC 8022, DOI 10.17487/RFC8022, November 2016, <https://www.rfc-editor.org/info/rfc8022>.

[RFC8242] Haas, J. and S. Hares, «Interface to the Routing System (I2RS) Ephemeral State Requirements», RFC 8242, DOI 10.17487/RFC8242, September 2017, <https://www.rfc-editor.org/info/rfc8242>.

[RFC8340] Bjorklund, M. and L. Berger, Ed., «YANG Tree Diagrams», BCP 215, RFC 8340, DOI 10.17487/RFC8340, March 2018, <https://www.rfc-editor.org/info/rfc8340>.

[RFC8343] Bjorklund, M., «A YANG Data Model for Interface Management», RFC 8343, DOI 10.17487/RFC8343, March 2018, <https://www.rfc-editor.org/info/rfc8343>.

[RFC8346] Clemm, A., Medved, J., Varga, R., Liu, X., Ananthakrishnan, H., and N. Bahadur, «A YANG Data Model for Layer 3 Topologies», RFC 8346, DOI 10.17487/RFC8346, March 2018, <https://www.rfc-editor.org/info/rfc8346>.

[USECASE-REQS] Hares, S. and M. Chen, «Summary of I2RS Use Case Requirements», Work in Progress, draft-ietf-i2rs-usecase-reqs-summary-03, November 2016.

[YANG-Push] Clemm, A., Voit, E., Gonzalez Prieto, A., Tripathy, A., Nilsen-Nygaard, E., Bierman, A., and B. Lengyel, «YANG Datastore Subscription», Work in Progress, draft-ietf-netconf-yang-push-1510, February 2018.

Приложение A. Примеры использования модели

A.1. Излечение топологии из элемента сети

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

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

Следует отметить, что контроллеры могут создаваться на основе других контроллеров для создания топологии, включающей все домены сети.

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

A.2. Изменение топологии TE, импортированной из оптического контроллера

Рассмотрим случай, где Optical Controller представляет свою топологию клиентскому контроллеру пакетов в абстрактных терминах TE. Эта настраиваемая топология (которая объединяется с собственной топологией клиента) содержит достаточно данных, чтобы рассчитывающий пути клиент мог выбрать путь через оптический домен в соответствии с его политикой. Если клиент считает, что в данный момент) импортированная топология не соответствует в точности его требованиям, он может запросить изменение топологии. Такой запрос настройки может включать добавление или удаление элементов топологии или изменение атрибутов имеющихся элементов. Для оптического контроллера эти запросы транслируются в изменение настроек экспортируемой абстрактной топологии.

A.3. Аннотирование топологии для локальных расчетов

В некоторых вариантах определенную контроллером топологию нужно дополнить атрибутами до начала ее использования алгоритмами расчета. Рассмотрим случай, где рассчитывающему путь приложению на контроллере нужны географические координаты узлов при расчете пути. Если в определенной топологии нет этих координат, эти дополнительные атрибуты должны быть настроены в соответствующих элементах топологии.

A.4. Основанная на контроллере SDN конфигурация наложенной сети

В этом варианте контроллер SDN (например, Open Daylight) поддерживает представление топологии, которой он управляет, на основе информации, обнаруженной в сети. Кроме того, он обеспечивает приложение, в котором настраивается и поддерживается топология наложения.

Таким образом, контроллер SDN должен поддерживать две роли:

  • клиент сети;

  • сервер для своих северных приложений и клиентов, например системы поддержки операций OSS12.

Т. е. клиент одной системы (в данном случае контроллер) может быть сервером (системой управления) другой.

В этом варианте контроллер SDN поддерживает модель консолидированных данных нескольких уровней топологии. Это включает нижние уровни, построенные на базе информации, полученной из сети, а также верхние уровни топологии наложения, настраиваемые клиентом в контроллер, т. е. OSS. Для OSS нижние уровни предоставляют информацию, доступную лишь для чтения, а верхние должны разрешать чтение и запись.

Приложение B. Модели YANG для реализаций без поддержки NMDA

Определенные в этом документе модули YANG предназначены для применения с реализациями, поддерживающими архитектуру NMDA, определенную в [RFC8342]. Чтобы реализации могли использовать модель данных даже без поддержки NMDA, определены два используемых совместно модуля ietf-network-state и ietf-network-topology-state, представляющие рабочее состояние и топологию сети, соответственно. Эти модули «отражают» модули ietf-network и ietf-network-topology (параграфы 6.1 и 6.2), однако все их узлы не поддерживают настройку. Они представляют состояние, которое возникает при (1) получении топологической информации из сети или применения конфигурации из «отраженных» модулей.

Парные модули ietf-network-state и ietf-network-topology-state являются избыточными и их не следует включать в реализации с поддержкой NMDA, поэтому они определены в приложениях B.1 и B.2 (ниже), а не в основном тексте.

Структура этих модулей отражает структуру базовых модулей, поэтому дерево YANG для них не приводится.

B.1. Модуль YANG для состояния сети

<CODE BEGINS> file "ietf-network-state@2018-02-26.yang"

module ietf-network-state {
  yang-version 1.1;
  namespace "urn:ietf:params:xml:ns:yang:ietf-network-state";
  prefix nw-s;

  import ietf-network {
    prefix nw;
    reference
      "RFC 8345: A YANG Data Model for Network Topologies";
  }

  organization
    "Рабочая группа IETF I2RS (интерфейс в систему маршрутизации)";

  contact
    "WG Web:    <https://datatracker.ietf.org/wg/i2rs/> 
     WG List:   <mailto:i2rs@ietf.org> 

     Editor:    Alexander Clemm
                <mailto:ludwig@clemm.org> 

     Editor:    Jan Medved
                <mailto:jmedved@cisco.com> 

     Editor:    Robert Varga
                <mailto:robert.varga@pantheon.tech> 

     Editor:    Nitin Bahadur
                <mailto:nitin_bahadur@yahoo.com> 

     Editor:    Hariharan Ananthakrishnan
                <mailto:hari@packetdesign.com> 

     Editor:    Xufeng Liu
                <mailto:xufeng.liu.ietf@gmail.com>"; 

  description
    "Этот модуль определяет общую базовую модель данных для
     набора узлов в сети. Определения узлов затем применяются
     в сетевой топологии и описях (inventorY). Модуль представляет
     информацию, которая (1) изучается автоматически и заполняется
     или (2) является результатом применения сетевой информации,
     заданной в соответствии с моделью данных ietf-network,
     отражающей соответствующие узлы этой модели.

     Модель данных отражает ietf-network, но содержит лишь данные
     состояния, доступные только для чтения. Модель данных не 
     требуется, когда инфраструктура базовой реализации
     поддерживает архитектуру NMDA.

     Авторские права (Copyright (c) 2018) принадлежат IETF Trust 
     и указанным авторам. Все права защищены.

     Распространение и использование с исходной или двоичной форме
     с внесением изменений или без них определяется лицензией
     Simplified BSD License, изложенной в разделе 4.c IETF Trust
     Legal Provisions для документов IETF
     (https://trustee.ietf.org/license-info). 

     Данная версия модуля YANG является частью RFC 8345, где указаны
     дополнительные правовые аспекты.";

  revision 2018-02-26 {
    description
      "Первый выпуск.";
    reference
      "RFC 8345: A YANG Data Model for Network Topologies";
  }

  grouping network-ref {
    description
      "Содержит информацию, необходимую для указания сети, например,
       указывает базовую сеть.";
    leaf network-ref {
      type leafref {
        path "/nw-s:networks/nw-s:network/nw-s:network-id";
      require-instance false;
      }
      description
        "Служит для указания сети, например, базовой.";
    }
  }

  grouping node-ref {
    description
      " Содержит информацию, необходимую для указания узла.";
    leaf node-ref {
      type leafref {
        path "/nw-s:networks/nw-s:network[nw-s:network-id=current()"+
          "/../network-ref]/nw-s:node/nw-s:node-id";
        require-instance false;
      }
      description
        "Служит для указания узла относительно содержащей его сети.";
    }
    uses network-ref;
  }

  container networks {
    config false;
    description
      "Служит контейнером верхнего уровня для списка сетей.";
    list network {
      key "network-id";
      description
        "Описывает сеть. Обычно сеть содержит опись узлов, 
         топологическую информацию (дополненную моделью данных
         сетевой топологии) и информацию об уровнях.";
      container network-types {
        description
          "Служит целью дополнения. Тип сети указывается через
           соответствующие контейнеры присутствия, добавленные
           в этот контейнер.";
      }
      leaf network-id {
        type nw:network-id;
        description
          "Указывает сеть.";
      }
      list supporting-network {
        key "network-ref";
        description
          "Базовая сеть, используемая для представления 
           многоуровневой сетевой топологии.";
        leaf network-ref {
          type leafref {
            path "/nw-s:networks/nw-s:network/nw-s:network-id";
          require-instance false;
          }
          description
            "Указывает базовую сеть.";
        }
      }

      list node {
        key "node-id";
        description
          "Опись узлов данной сети.";
        leaf node-id {
          type nw:node-id;
          description
            "Однозначно указывает узел в содержащей его сети.";
        }
        list supporting-node {
          key "network-ref node-ref";
          description
            "Представляет другой узел, находящийся в базовой сети и
             поддерживающий данный узел. Служит для представления
             многоуровневой структуры.";
          leaf network-ref {
            type leafref {
              path "../../../nw-s:supporting-network/nw-s:network-ref";
            require-instance false;
            }
            description
              "Указывает базовую сеть, в которой находится базовый узел.";
          }
          leaf node-ref {
            type leafref {
              path "/nw-s:networks/nw-s:network/nw-s:node/nw-s:node-id";
            require-instance false;
            }
            description
              "Указывает сам базовый узел.";
          }
        }
      }
    }
  }
}

<CODE ENDS>

B.2. Модуль YANG для состояния сетевой топологии

  <CODE BEGINS> file "ietf-network-topology-state@2018-02-26.yang"

  module ietf-network-topology-state {
    yang-version 1.1;
    namespace "urn:ietf:params:xml:ns:yang:ietf-network-topology-state";
    prefix nt-s;

    import ietf-network-state {
      prefix nw-s;
      reference
        "RFC 8345: A YANG Data Model for Network Topologies";
    }
    import ietf-network-topology {
      prefix nt;
      reference
        "RFC 8345: A YANG Data Model for Network Topologies";
    }

    organization
       "Рабочая группа IETF I2RS (интерфейс в систему маршрутизации)";

    contact
      "WG Web:    <https://datatracker.ietf.org/wg/i2rs/> 
       WG List:   <mailto:i2rs@ietf.org> 

       Editor:    Alexander Clemm
                  <mailto:ludwig@clemm.org> 

       Editor:    Jan Medved
                  <mailto:jmedved@cisco.com> 

       Editor:    Robert Varga
                  <mailto:robert.varga@pantheon.tech> 

       Editor:    Nitin Bahadur
                  <mailto:nitin_bahadur@yahoo.com> 

       Editor:    Hariharan Ananthakrishnan
                  <mailto:hari@packetdesign.com> 

       Editor:    Xufeng Liu
                  <mailto:xufeng.liu.ietf@gmail.com>"; 

    description
      "Эта модель определяет общую базовую модель данных для состояния
       сетевой топологии, представляющую топологию, полученную (1) путем
       обучения или (2) в результате применения топологии, настроенной в
       модели данных ietf-network-topology и отражающей соответствующие
       узлы данных в этой модели. Она дополняет базовую модель данных 
       состояния сети каналами для подключения узлов и точками 
       завершения каналов на узлах.

       Модель данных отражает ietf-network-topology, но содержит лишь 
       данные состояния, доступные только для чтения. Модель данных не 
       требуется, когда инфраструктура базовой реализации
       поддерживает архитектуру NMDA.

       Авторские права (Copyright (c) 2018) принадлежат IETF Trust 
       и указанным авторам. Все права защищены.

       Распространение и использование с исходной или двоичной форме
       с внесением изменений или без них определяется лицензией
       Simplified BSD License, изложенной в разделе 4.c IETF Trust
       Legal Provisions для документов IETF
       (https://trustee.ietf.org/license-info). 

       Данная версия модуля YANG является частью RFC 8345, где указаны
       дополнительные правовые аспекты.";

    revision 2018-02-26 {
      description
        "Первый выпуск.";
      reference
        "RFC 8345: A YANG Data Model for Network Topologies";
    }

    grouping link-ref {
      description
        "Указывает канал в конкретной сети. Эта группировка не 
         применяется в модуле и определена для удобства его дополнения.";
      leaf link-ref {
        type leafref {
          path "/nw-s:networks/nw-s:network[nw-s:network-id=current()"+
            "/../network-ref]/nt-s:link/nt-s:link-id";
          require-instance false;
        }
        description
          "Тип для абсолютной ссылки на экземпляр канала
           (этот тип не следует применять для относительных ссылок,
           используя для них относительный путь.)";
      }
      uses nw-s:network-ref;
    }

    grouping tp-ref {
      description
        "Указывает точку завершения в конкретном узле. Эта группировка не 
         применяется в модуле и определена для удобства его дополнения.";
      leaf tp-ref {
        type leafref {
          path "/nw-s:networks/nw-s:network[nw-s:network-id=current()"+
            "/../network-ref]/nw-s:node[nw-s:node-id=current()/../"+
            "node-ref]/nt-s:termination-point/nt-s:tp-id";
          require-instance false;
        }
        description
          "Тип для абсолютной ссылки на точку завершения
           (этот тип не следует применять для относительных ссылок,
           используя для них относительный путь.)";
      }
      uses nw-s:node-ref;
    }
    augment "/nw-s:networks/nw-s:network" {
      description
        "Добавляет каналы в модель данных сети.";
      list link {
        key "link-id";
        description
          "Сетевой канал соединяет локальный (источник) и удаленный
           (получатель) узел через набор точек завершения 
           соответствующих узлов. Можно иметь несколько каналов между
           парой узлов. Канал можно перемещать между точками
           завершения. Поэтому для однозначного различения каналов
           каждый из них указывается выделенным идентификатором.
           Отметим, что канал моделирует соединение «точка-точка», а
           не многоточечное.";
        container source {
          description
            "Контейнер, содержащий логический источник конкретного канала.";
          leaf source-node {
            type leafref {
              path "../../../nw-s:node/nw-s:node-id";
              require-instance false;
            }
            description
              "Идентификатор узла-источника. Должен быть в той же топологии.";
          }
          leaf source-tp {
            type leafref {
              path "../../../nw-s:node[nw-s:node-id=current()/../"+
                "source-node]/termination-point/tp-id";
              require-instance false;
            }
            description
               "Эта точка завершения размещается внутри узла-источника и
                завершает канал.";
          }
        }
        container destination {
          description
             "Контейнер, содержащий логического получателя конкретного канала.";
          leaf dest-node {
            type leafref {
              path "../../../nw-s:node/nw-s:node-id";
            require-instance false;
            }
            description
               "Идентификатор узла-получателя. Должен быть в той же сети.";
          }
          leaf dest-tp {
            type leafref {
              path "../../../nw-s:node[nw-s:node-id=current()/../"+
                "dest-node]/termination-point/tp-id";
              require-instance false;
            }
            description
               "Эта точка завершения размещается внутри узла-получателя и
                завершает канал.";
          }
        }
        leaf link-id {
          type nt:link-id;
          description
            "Идентификатор канала в топологии, к которой канал относится.";
        }
        list supporting-link {
          key "network-ref link-ref";
          description
            "Идентификатор канала или каналов, от которых данный канал зависит.";
          leaf network-ref {
            type leafref {
              path "../../../nw-s:supporting-network/nw-s:network-ref";
            require-instance false;
            }
            description
              "Этот лист указывает, в какой базовой топологии присутствует
               поддерживающий канал.";
          }
          leaf link-ref {
            type leafref {
              path "/nw-s:networks/nw-s:network[nw-s:network-id="+
                "current()/../network-ref]/link/link-id";
              require-instance false;
            }
            description
              "Это лист указывает канал, который является частью базы для
               данного канала. Петли, в которых канал указывает себя в
               качестве своей основы, напрямую или косвенно, не разрешены.";
          }
        }
      }
    }

    augment "/nw-s:networks/nw-s:network/nw-s:node" {
      description
        "Дополняет точки завершения каналов. Точки завершения в
         конце концов указывают на интерфейсы.";
      list termination-point {
        key "tp-id";
        description
          "Точка завершения может заканчивать канал. В зависимости от типа
           топологии точка завершения может указывать, например, на порт 
           или интерфейс.";
        leaf tp-id {
          type nt:tp-id;
          description
            "Идентификатор точки завершения.";
        }
        list supporting-termination-point {
          key "network-ref node-ref tp-ref";
          description
            "Этот список указывает все точки завершения, от которых 
             данная точка завершения зависит или куда отображается.
             Эти точки будут сами по себе содержаться в поддерживающем
             узле. Эта информация о зависимостях может быть выведена из
             зависимостей между каналами. Поэтому не требуется отдельно
             настраивать этот элемент и нет необходимости формулировать
             соответствующее ограничение. Нужная информация просто
             обеспечивается реализующей системой.";
          leaf network-ref {
            type leafref {
              path "../../../nw-s:supporting-node/nw-s:network-ref";
            require-instance false;
            }
            description
              "Этот лист указывает, в какой топологии присутствует
               поддерживающая точка завершения.";
          }
          leaf node-ref {
            type leafref {
              path "../../../nw-s:supporting-node/nw-s:node-ref";
            require-instance false;
            }
            description
              "Этот лист указывает, в каком узле присутствует
               поддерживающая точка завершения.";
          }
          leaf tp-ref {
            type leafref {
              path "/nw-s:networks/nw-s:network[nw-s:network-id="+
                "current()/../network-ref]/nw-s:node[nw-s:node-id="+
                "current()/../node-ref]/termination-point/tp-id";
              require-instance false;
            }
            description
              "Ссылка на базовый узел (этот узел должен находиться
               в другой топологии).";
          }
        }
      }
    }
  }
  <CODE ENDS>

Приложение C. Пример

В этом приложении представлен пример экземпляра дерева данные в коде JSON [RFC7951]. Пример создает экземпляр ietf-network-topology (и дополняемого им ietf-network) для топологии, показанной на рисунке 7. Имеется три узла — D1, D2, и D3. У D1 имеется 3 точки завершения (1-0-1, 1-2-1, 1-3-1), у D2 — тоже три (2-1-1, 2-0-1, 2-3-1), а у D3 — две (3-1-1 и 3-2-1). Кроме того, имеется 6 односторонних каналов, по паре разнонаправленных между каждыми двумя узлами.

 +------------+                   +------------+
 |     D1     |                   |     D2     |
/-\          /-\                 /-\          /-\
| | 1-0-1    | |---------------->| | 2-1-1    | |
| |    1-2-1 | |<----------------| |    2-0-1 | |
\-/  1-3-1   \-/                 \-/  2-3-1   \-/
 |   /----\   |                   |   /----\   |
 +---|    |---+                   +---|    |---+
     \----/                           \----/
      A  |                             A  |
      |  |                             |  |
      |  |                             |  |
      |  |       +------------+        |  |
      |  |       |     D3     |        |  |
      |  |      /-\          /-\       |  |
      |  +----->| | 3-1-1    | |-------+  |
      +---------| |    3-2-1 | |<---------+
                \-/          \-/
                 |            |
                 +------------+

Рисунок 7. Пример топологии сети.


Соответствующий экземпляр дерева данных представлен на рисунке 8.

   {
     "ietf-network:networks": {
       "network": [
         {
           "network-types": {
           },
           "network-id": "foo:otn-hc",
           "node": [
             {
               "node-id": "D1",
               "termination-point": [
                 {
                   "tp-id": "1-0-1"
                 },
                 {
                   "tp-id": "1-2-1"
                 },
                 {
                   "tp-id": "1-3-1"
                 }
               ]
             },
             {
               "node-id": "D2",
               "termination-point": [
                 {
                   "tp-id": "2-0-1"
                 },
                 {
                   "tp-id": "2-1-1"
                 },
                 {
                   "tp-id": "2-3-1"
                 }
               ]
             },
             {
               "node-id": "D3",
               "termination-point": [
                 {
                   "tp-id": "3-1-1"
                 },
                 {
                   "tp-id": "3-2-1"
                 }
               ]
             }
           ],
           "ietf-network-topology:link": [
             {
               "link-id": "D1,1-2-1,D2,2-1-1",
               "source": {
                 "source-node": "D1",
                 "source-tp": "1-2-1"
               }
               "destination": {
                 "dest-node": "D2",
                 "dest-tp": "2-1-1"
               }
             },
             {
               "link-id": "D2,2-1-1,D1,1-2-1",
               "source": {
                 "source-node": "D2",
                 "source-tp": "2-1-1"
               }
               "destination": {
                 "dest-node": "D1",
                 "dest-tp": "1-2-1"
               }
             },
             {
               "link-id": "D1,1-3-1,D3,3-1-1",
               "source": {
                 "source-node": "D1",
                 "source-tp": "1-3-1"
               }
               "destination": {
                 "dest-node": "D3",
                 "dest-tp": "3-1-1"
               }
             },
             {
               "link-id": "D3,3-1-1,D1,1-3-1",
               "source": {
                 "source-node": "D3",
                 "source-tp": "3-1-1"
               }
               "destination": {
                 "dest-node": "D1",
                 "dest-tp": "1-3-1"
               }
             },
             {
               "link-id": "D2,2-3-1,D3,3-2-1",
               "source": {
                 "source-node": "D2",
                 "source-tp": "2-3-1"
               }
               "destination": {
                 "dest-node": "D3",
                 "dest-tp": "3-2-1"
               }
             },
             {
               "link-id": "D3,3-2-1,D2,2-3-1",
               "source": {
                 "source-node": "D3",
                 "source-tp": "3-2-1"
               }
               "destination": {
                 "dest-node": "D2",
                 "dest-tp": "2-3-1"
               }
             }
           ]
         }
       ]
     }
   }

Рисунок 8. Экземпляр дерева данных.

Благодарности

Мы хотели бы выразить признательность за полезный вклад в работу, комментарии и предложения Alia Atlas, Andy Bierman, Martin Bjorklund, Igor Bryskin, Benoit Claise, Susan Hares, Ladislav Lhotka, Carlos Pignataro, Juergen Schoenwaelder, Robert Wilton, Qin Wu и Xian Zhang.

Участники работы

Многие люди в дополнение к перечисленным в разделе «Адреса авторов» внесли свой вклад в модель данных, представленную в этом документе. В число таких людей входят:

  • Vishnu Pavan Beeram, Juniper;

  • Ken Gray, Cisco;

  • Tom Nadeau, Brocade;

  • Tony Tkacik;

  • Kent Watsen, Juniper;

  • Aleksandr Zhdankin, Cisco.

Адреса авторов

Alexander Clemm

Huawei USA — Futurewei Technologies Inc.

Santa Clara, CA

United States of America

Email: ludwig@clemm.org, alexander.clemm@huawei.com

Jan Medved

Cisco

Email: jmedved@cisco.com

Robert Varga

Pantheon Technologies SRO

Email: robert.varga@pantheon.tech

Nitin Bahadur

Bracket Computing

Email: nitin_bahadur@yahoo.com

Hariharan Ananthakrishnan

Packet Design

Email: hari@packetdesign.com

Xufeng Liu

Jabil

Email: xufeng.liu.ietf@gmail.com


Перевод на русский язык

Николай Малых

nmalykh@gmail.com

1Internet Engineering Task Force.

2Internet Engineering Steering Group.

3Traffic engineering.

4Virtual Machine.

5Reconfigurable Optical Add/Drop Multiplexer — перенастраиваемый оптический мультиплексор ввода-вывода.

6Interface to the Routing System.

7XML Path Language.

8Network Management Datastore Architecture — архитектура хранилища данных сетевого управления.

9Secure Shell — защищенная оболочка.

10Документ завершен и опубликован в RFC 8641. Прим. перев.

11Network Management System.

12Operations Support System.

Рубрика: RFC | Комментарии к записи RFC 8345 A YANG Data Model for Network Topologies отключены

RFC 8343 A YANG Data Model for Interface Management

Internet Engineering Task Force (IETF)                      M. Bjorklund
Request for Comments: 8343                                Tail-f Systems
Obsoletes: 7223                                               March 2018
Category: Standards Track
ISSN: 2070-1721

A YANG Data Model for Interface Management
Модель данных YANG для управления интерфейсами

PDF

Аннотация

В этом документе определена модель данных YANG для управления сетевыми интерфейсами. Предполагается добавление (augment) расширений для конкретных типов интерфейсов к определённой здесь базовой модели. Модель включает данные конфигурации и состояния (информация о состоянии и счётчики статистики) и соответствует архитектуре хранилищ конфигурации NMDA1 в RFC 8342.

Данный документ отменяет RFC 7223.

Статус документа

Документ относится к категории Internet Standards Track.

Документ является результатом работы IETF2 и представляет согласованный взгляд сообщества IETF. Документ прошёл открытое обсуждение и был одобрен для публикации IESG3. Дополнительную информацию о стандартах Internet можно найти в разделе 2 в RFC 7841.

Информацию о текущем статусе документа, ошибках и способах обратной связи можно найти по ссылке https://www.rfc-editor.org/info/rfc8343.

Авторские права

Авторские права (Copyright (c) 2018) принадлежат IETF Trust и лицам, указанным в качестве авторов документа. Все права защищены.

К документу применимы права и ограничения, указанные в BCP 78 и IETF Trust Legal Provisions и относящиеся к документам IETF (http://trustee.ietf.org/license-info), на момент публикации данного документа. Прочтите упомянутые документы внимательно. Фрагменты программного кода, включённые в этот документ, распространяются в соответствии с упрощённой лицензией BSD, как указано в параграфе 4.e документа IETF Trust Legal Provisions, без каких-либо гарантий (как указано в Simplified BSD License).

1. Введение

Этот документ определяет модель данных YANG [RFC7950] для управления сетевыми интерфейсами. Предполагается, что базовая модель будет дополнена (augment) моделями данных для конкретных типов интерфейсов.

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

Модель включает данные конфигурации и состояния (информация о состоянии и счётчики статистики).

Эта версия модели данных интерфейсов поддерживает архитектуру хранилищ конфигурации NMDA [RFC8342].

1.1. Отличия от RFC 7223

Ветвь «/interfaces-state» с узлами данных config false была исключена и все узлы config false сейчас представлены в ветви «/interfaces».

Серверы, не реализующие NMDA или желающие поддерживать клиентов, не реализующих NMDA, могут реализовать отменённое дерево «/interfaces-state».

1.2. Терминология

Ключевые слова необходимо (MUST), недопустимо (MUST NOT), требуется (REQUIRED), нужно (SHALL), не следует (SHALL NOT), следует (SHOULD), не нужно (SHOULD NOT), рекомендуется (RECOMMENDED), не рекомендуется (NOT RECOMMENDED), возможно (MAY), необязательно (OPTIONAL) в данном документе интерпретируются в соответствии с BCP 14 [RFC2119] [RFC8174] тогда и только тогда, когда они выделены шрифтом, как показано здесь.

Ниже приведены определения используемых в документе терминов.

system-controlled interface — управляемый системой интерфейс

Интерфейс называют управляемым системой (system-controlled), если система создаёт или удаляет интерфейс независимо от того, был ли он явно настроен. Примерами являются интерфейсы, представляющие физические компоненты, которые могут добавляться в систему или удаляться из неё (например, линейные платы или подключаемые в процессе работы беспроводные интерфейсы). Управляемые системой интерфейсы могут появляться при включении той или иной функциональности (например, loopback-интерфейс при включении стека IP).

user-controlled interface — управляемый пользователем интерфейс

Интерфейс называют управляемым пользователем (user-controlled), если создание интерфейса определяется его явной настройкой в хранилище рабочей конфигурации, а удаление — явным удалением конфигурации интерфейса из этого хранилища. Примерами являются интерфейсы VLAN, настроенные на управляемых системой интерфейсах Ethernet.

Ниже приведён список используемых терминов из [RFC8342].

  • client — клиент;

  • server — сервер;

  • configuration — конфигурация;

  • system state — состояние системы;

  • operational state — операционное (рабочее) состояние;

  • intended configuration — предполагаемая конфигурация;

  • running configuration datastore — хранилище рабочей конфигурации;

  • operational state datastore — хранилище данных операционного состояния.

Ниже приведён список используемых терминов из [RFC7950].

  • augment — дополнение;

  • data model — модель данных;

  • data node — узел данных.

1.3. Диаграммы деревьев

Диаграммы деревьев в этом документе используют нотацию, определённую в [RFC8340].

2. Цели

В этом разделе описаны некоторые из целей разработки модели, представленной в разделе 5.

  • Ясно, что существующие реализации будут отображать описанную здесь модель на свои фирменные, естественные модели данных. Для облегчения этого модель данных должна быть простой.

  • Модель данных должна подходить для новых реализаций без отображения на естественную модель.

  • Ссылки на интерфейсы должны быть как можно проще, предпочтительно с использованием одного leafref.

  • Отображение на ifIndex [RFC2863], применяемое протоколом SNMP4 для указания интерфейсов, должно быть чётким.

  • Модель должна поддерживать уровни интерфейсов — как (1) простые, где один интерфейс работает поверх единственного другого, так и (2) более сложные, где один интерфейс может быть результатом агрегирования N других интерфейсов или N интерфейсов могут мультиплексироваться в один.

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

  • Модель данных должна поддерживать как физические, так и логические интерфейсы.

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

3. Модель данных интерфейса

А документе задан модуль YANG ietf-interfaces, который имеет показанную ниже структуру без ветви /interfaces-state.

   module: ietf-interfaces
     +--rw interfaces
        +--rw interface* [name]
           +--rw name                        string
           +--rw description?                string
           +--rw type                        identityref
           +--rw enabled?                    boolean
           +--rw link-up-down-trap-enable?   enumeration {if-mib}?
           +--ro admin-status                enumeration {if-mib}?
           +--ro oper-status                 enumeration
           +--ro last-change?                yang:date-and-time
           +--ro if-index                    int32 {if-mib}?
           +--ro phys-address?               yang:phys-address
           +--ro higher-layer-if*            interface-ref
           +--ro lower-layer-if*             interface-ref
           +--ro speed?                      yang:gauge64
           +--ro statistics
              +--ro discontinuity-time    yang:date-and-time
              +--ro in-octets?            yang:counter64
              +--ro in-unicast-pkts?      yang:counter64
              +--ro in-broadcast-pkts?    yang:counter64
              +--ro in-multicast-pkts?    yang:counter64
              +--ro in-discards?          yang:counter32
              +--ro in-errors?            yang:counter32
              +--ro in-unknown-protos?    yang:counter32
              +--ro out-octets?           yang:counter64
              +--ro out-unicast-pkts?     yang:counter64
              +--ro out-broadcast-pkts?   yang:counter64
              +--ro out-multicast-pkts?   yang:counter64
              +--ro out-discards?         yang:counter32
              +--ro out-errors?           yang:counter32

3.1. Списки интерфейсов

Представленная в документе модель данных использует плоский список интерфейсов (/interfaces/interface), каждый из которых указывается именем. Кроме того, каждый интерфейс имеет обязательный лист type.

Модуль iana-if-type [RFC7224] определяет отождествления YANG для типов интерфейсов в поддерживаемом IANA реестре ifType definitions.

Имеется один список настроенных интерфейсов (/interfaces/interface) и отдельный список рабочих состояний интерфейсов (/interfaces-state/interface).

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

В качестве примера возможного дополнения для конкретного типа интерфейса рассмотрим приведённый ниже фрагмент кода YANG. Более полный пример приведён в Приложении A.

     import interfaces {
         prefix "if";
     }
     import iana-if-type {
       prefix ianaift;
     }

     augment "/if:interfaces/if:interface" {
         when "if:type = 'ianaift:ethernetCsmacd'";

         container ethernet {
             leaf duplex {
                 ...
             }
         }
     }

Для управляемых системой интерфейсов name является зависимым от устройства именем интерфейса.

Если устройство поддерживает произвольное именование управляемых пользователем интерфейсов, сервер анонсирует свойство arbitrary-names. Если сервер не анонсирует это свойство, имена управляемых пользователем интерфейсов должны соответствовать схеме именования для устройства. Способ получения клиентом информации о схемах именования таких устройств выходит за рамки документа. Примеры приведены в приложениях E.1 и E.2.

Когда системно-управляемый интерфейс создаётся системой в рабочем состоянии, она пытается применить предполагаемую конфигурацию с именем нового интерфейса. Если такой конфигурации не найдено или заданный в конфигурации тип не соответствует типу реального интерфейса, система создаёт интерфейс без применения явной конфигурации.

При создании управляемого пользователем интерфейса имя этого интерфейса определяет конфигурация.

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

3.2. Указание интерфейсов

Интерфейс указывается именем, которое уникально в рамках сервера. Это свойство фиксируется в определениях типа (typedef) interface-ref, которые другим модулям YANG следует применять, когда нужно указать интерфейс.

3.3. Уровни интерфейсов

Не существует общего механизма настройки интерфейса так, чтобы он размещался «поверх» некого другого интерфейса. Предполагается, что модели для конкретных типов интерфейсов будут определять свои узлы данных для уровней интерфейсов с помощью типов interface-ref для указания нижележащих уровней.

Ниже приведён пример модели с такими узлами. Более полный пример представлен в Приложении B.

     import interfaces {
         prefix "if";
     }
     import iana-if-type {
       prefix ianaift;
     }

     augment "/if:interfaces/if:interface" {
         when "if:type = 'ianaift:ieee8023adLag'";

         leaf-list slave-if {
             type if:interface-ref;
             must "/if:interfaces/if:interface[if:name = current()]"
                + "/if:type = 'ianaift:ethernetCsmacd'" {
                 description
                     "Ведомый (slave) интерфейс должен иметь тип 
                      'ethernetCsmacd'.";
             }
         }
         // Другие параметры настройки связки, время восстановления и пр.
     }

Хотя параметры уровней настраиваются в моделях для конкретных типов интерфейсов, два базовых leaf-list (higher-layer-if и lower-layer-if) представляют доступную только для чтения иерархию уровней интерфейсов.

4. Связь с IF-MIB

Если устройство реализует IF-MIB [RFC2863], каждая запись списка /interfaces/interface обычно отображается на один элемент ifEntry. Лист if-index должен содержать ifIndex соответствующего элемента ifEntry.

В большинстве случаев name из записи /interfaces/interface отображается на ifName. База IF-MIB позволяет двум разным ifEntry иметь общее имя ifName. Поддерживающие такую возможность устройства, которые соответствуют также определённой в этом документе модели данных, не могут иметь взаимно-однозначного сопоставления между листом name и ifName.

Указанное в конфигурации описание (description) интерфейса традиционно отображается некоторыми реализациями в ifAlias. Этот документ разрешает такие отображения, но рекомендует разработчикам учитывать различия в пространстве значений и постоянстве этих объектов. Подробности приведены в определении листа description представленного в разделе 5 модуля YANG.

IF-MIB определяет также открытый для записи объект ifPromiscuousMode. Поскольку агенты SNMP обычно не реализуют этот объект как конфигурационный, он не отображается в модуль ietf-interfaces.

Объект ifMtu из IF-MIB не отображается в модуль ietf-interfaces. Предполагается, что модули YANG для конкретных типов интерфейсов будут представлять MTU с помощью дополнения модели ietf-interfaces.

В IF-MIB имеется множество счётчиков, существующих в 32-битовом и 64-битовом варианте. 64-битовые счётчики были добавлены для поддержки интерфейсов со скоростью выше 20000000 бит/с. Современные реализации обычно поддерживают такие интерфейсы, поэтому модель данных включает лишь 64-битовые счётчики. Отметим, что NETCONF и SNMP могут различаться по частоте обращения к этим счётчикам. Например, многие реализации SNMP кэшируют значения счётчиков в течение некоторого времени.

Объекты ifDescr и ifConnectorPresent из IF-MIB не отображаются в модуль ietf-interfaces.

Ниже приведены таблицы сопоставления узлов данных YANG и объектов IF-MIB.

Узлы YANG и соответствующие объекты IF-MIB.

 

Узел данных YANG в /interfaces/interface

Объект IF-MIB

name

ifName

type

ifType

description

ifAlias

admin-status

ifAdminStatus

oper-status

ifOperStatus

last-change

ifLastChange

if-index

ifIndex

link-up-down-trap-enable

ifLinkUpDownTrapEnable

phys-address

ifPhysAddress

higher-layer-if и lower-layer-if

ifStackTable

speed

ifSpeed и ififHighSpeed

discontinuity-time

ifCounterDiscontinuityTime

in-octets

ifHCInOctets

in-unicast-pkts

ifHCInUcastPkts

in-broadcast-pkts

ifHCInBroadcastPkts

in-multicast-pkts

ifHCInMulticastPkts

in-discards

ifInDiscards

in-errors

ifInErrors

in-unknown-protos

ifInUnknownProtos

out-octets

ifHCOutOctets

out-unicast-pkts

ifHCOutUcastPkts

out-broadcast-pkts

ifHCOutBroadcastPkts

out-multicast-pkts

ifHCOutMulticastPkts

out-discards

ifOutDiscards

out-errors

ifOutErrors

 

5. Модуль YANG

Этот модуль YANG импортирует определения типов (typedef) из [RFC6991].

   <CODE BEGINS> file "ietf-interfaces@2018-02-20.yang"

   module ietf-interfaces {
     yang-version 1.1;
     namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces";
     prefix if;

     import ietf-yang-types {
       prefix yang;
     }

     organization
       "IETF NETMOD (Network Modeling) Working Group";

     contact
       "WG Web:   <https://datatracker.ietf.org/wg/netmod/> 
        WG List:  <mailto:netmod@ietf.org> 

        Editor:   Martin Bjorklund
                  <mailto:mbj@tail-f.com>"; 

     description
       "Этот модуль содержит набор определений YANG для управления
        сетевыми интерфейсами.

        Авторские права (Copyright (c) 2018) принадлежат IETF Trust 
        и лицам, указанным как авторы кода. Все права защищены.

        Распространение и использование в исходной и двоичной форме
        с изменениями или без них разрешается в соответствии с условиями,
        указанными в упрощённой лицензии BSD, изложенной в разделе 4.c
        Правового положения IETF Trust применительно к документам IETF
        (http://trustee.ietf.org/license-info). 

        Эта версия модуля YANG является частью RFC 8343, где
        правовые аспекты выражены более полно.";

     revision 2018-02-20 {
       description
         "Обновление для поддержки NMDA.";
       reference
         "RFC 8343: A YANG Data Model for Interface Management";
     }

     revision 2014-05-08 {
       description
         "Исходный выпуск.";
       reference
         "RFC 7223: A YANG Data Model for Interface Management";
     }

     /*
      * Определения типов
      */

     typedef interface-ref {
       type leafref {
         path "/if:interfaces/if:interface/if:name";
       }
       description
         "Этот тип применяется моделями данных, которым нужно 
          указывать настроенные интерфейсы.";
     }

     /*
      * Отождествления (идентификаторы)
      */

     identity interface-type {
       description
         "Базовый идентификатор, из которого выводятся конкретные типы";
     }

     /*
      * Возможности
      */

     feature arbitrary-names {
       description
         "Это свойство показывает, что управляемые пользователем 
          интерфейсы могут иметь произвольные имена.";
     }
     feature pre-provisioning {
       description
         "Это свойство показывает, что устройство поддерживает 
          подготовленные заранее конфигурации интерфейсов, т. е. можно 
          настроить интерфейс, которого ещё нет в системе.";
     }
     feature if-mib {
       description
         "Это свойство указывает поддержку устройством IF-MIB.";
       reference
         "RFC 2863: The Interfaces Group MIB";
     }

     /*
      * Узлы данных
      */

     container interfaces {
       description
         "Параметры интерфейса.";

       list interface {
         key "name";

         description
           "Список интерфейсов устройства.

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

            Системно-управляемые интерфейсы, созданные системой, всегда
            присутствуют в этом списке рабочего состояния, даже
            если они не настроены.";

        leaf name {
           type string;
           description
             "Имя интерфейса .

              Устройство МОЖЕТ ограничивать разрешённые для этого листа
              значения, возможно в зависимости от типа интерфейса.
              Для управляемых системой интерфейсов этот лист содержит
              зависимое от устройства имя интерфейса. 

              При попытке клиента создать конфигурацию для управляемого
              системой интерфейса, которого нет в рабочем состоянии,
              сервер МОЖЕТ отклонить запрос, если система не позволяет 
              предварительно настраивать интерфейсы или имя указывает 
              интерфейс, который не может присутствовать в системе. 
              Сервер NETCONF ДОЛЖЕН вернуть отклик rpc-error с
              error-tag invalid-value.

              Если устройство поддерживает предварительную настройку
              интерфейсов, анонсируется свойство pre-provisioning.

              Если устройство разрешает произвольные имена для 
              управляемых пользователем интерфейсов, анонсируется 
              свойство arbitrary-names.

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

              Реализация сервера МОЖЕТ отображать этот лист на объект 
              MIB ifName. Такая реализация должна использовать тот или 
              иной механизм обработки различий в размере и разрешённых 
              символах для этого листа и ifName. Определение таких 
              механизмов выходит за рамки этого документа.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifName";
         }

         leaf description {
           type string;
           description
             "Текстовое описание интерфейса.

              Реализация сервера МОЖЕТ отображать этот лист на объект 
              MIB ifAlias. Такие реализации должны использовать тот или 
              иной механизм обработки различий в размере и разрешённых
              символах для этого листа и ifAlias. Определение таких
              механизмов выходит за рамки документа.

              Поскольку ifAlias определяется для сохранения в
              энергонезависимой памяти, реализация MIB ДОЛЖНА отображать 
              ifAlias на значение description в постоянном хранилище.
           reference
             "RFC 2863: The Interfaces Group MIB - ifAlias";
         }

         leaf type {
           type identityref {
             base interface-type;
           }
           mandatory true;
           description
             "Тип интерфейса.

              При создании записи для интерфейса сервер МОЖЕТ 
              инициализировать type действующим значением, например, 
              если можно вывести тип из имени интерфейса.

              При попытке клиента установить тип интерфейса значение,
              который никогда не используется системой (например, тип не
              поддерживается или не соответствует имени), сервер ДОЛЖЕН
              отвергнуть запрос. Сервер NETCONF ДОЛЖЕН возвратить отклик
              rpc-error с error-tag invalid-value в таком случае.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifType";
         }

         leaf enabled {
           type boolean;
           default "true";
           description
             "Настроенное желаемое состояние интерфейса.

              Системы, реализующие IF-MIB, используют значение листа в
              хранилище running для установки в IF-MIB.ifAdminStatus 
              значения up или down после инициализации ifEntry как 
              описано в RFC 2863.

              Изменение этого листа в хранилище running отражается в
              ifAdminStatus, но при изменении ifAdminStatus с помощью 
              SNMP этот лист не меняется.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
         }

         leaf link-up-down-trap-enable {
           if-feature if-mib;
           type enumeration {
             enum enabled {
               value 1;
               description
                 "Указывает следует ли генерировать уведомления SNMP 
              linkUp/linkDown для этого интерфейса.";
             }
             enum disabled {
               value 2;
               description
                 "Указывает следует ли генерировать уведомления SNMP 
                  linkUp/linkDown для этого интерфейса.";
             }
           }
           description
             "Указывает следует ли генерировать уведомления SNMP 
              linkUp/linkDown для этого интерфейса.

              Если этот узел не настроен, значение enabled применяется
              сервером для интерфейсов, которые не работают поверх 
              другого интерфейса (т. е. нет записей lower-layer-if), и 
              disabled в остальных случаях.";
           reference
             "RFC 2863: The Interfaces Group MIB -
                        ifLinkUpDownTrapEnable";
         }

         leaf admin-status {
           if-feature if-mib;
           type enumeration {
             enum up {
               value 1;
               description
                 "Готов пропускать пакеты.";
             }
             enum down {
               value 2;
               description
                 "Не готов пропускать пакеты и не находится в режиме 
                  тестирования.";
             }
             enum testing {
               value 3;
               description
                 "Находится в режиме тестирования.";
             }
           }
           config false;
           mandatory true;
           description
             "Желаемое состояние интерфейса.

              Этот лист имеет такую же семантику, как ifAdminStatus.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
         }

         leaf oper-status {
           type enumeration {
             enum up {
               value 1;
               description
                 "Готов пропускать пакеты.";
             }
             enum down {
               value 2;
               description
                 "Интерфейс не пропускает никаких пакетов.";
             }
             enum testing {
               value 3;
               description
                 "В режиме тестирования. Не может пропускать рабочих 
                  пакетов.";
             }
             enum unknown {
               value 4;
               description
                 "Состояние невозможно определить по каким-то причинам";
             }
             enum dormant {
               value 5;
               description
                 "Ожидание внешнего события.";
             }
             enum not-present {
               value 6;
               description
                 "Отсутствует тот или иной (обычно аппаратный) компонент.";
             }
             enum lower-layer-down {
               value 7;
               description
                 "Не работает вследствие состояния базового интерфейса";
             }
           }
           config false;
           mandatory true;
           description
             "Текущее операционное состояние интерфейса.

              Этот лист имеет такую же семантику, как ifOperStatus.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifOperStatus";
         }

         leaf last-change {
           type yang:date-and-time;
           config false;
           description
             "Время перехода интерфейса в текущее рабочее состояние.
              Если текущее состояние началось до предыдущей 
              реинициализации локальной подсистемы сетевого управления, 
              этого узла не будет.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifLastChange";
         }

         leaf if-index {
           if-feature if-mib;
           type int32 {
             range "1..2147483647";
           }
           config false;
           mandatory true;
           description
             "Значение ifIndex для ifEntry, представленной интерфейсом";
           reference
             "RFC 2863: The Interfaces Group MIB - ifIndex";
         }

         leaf phys-address {
           type yang:phys-address;
           config false;
           description
             "Адрес интерфейса на его протокольном подуровне. Например,
              для интерфейса 802.x этот объект обычно содержит MAC5-адрес
              Зависящие от среды модули должны определять порядок битов
              и байтов, а также формат значения для этого объекта. Для
              интерфейсов, не имеющих такого адреса (например, 
              последовательная линия), этого узла не будет.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifPhysAddress";
         }

         leaf-list higher-layer-if {
           type interface-ref;
           config false;
           description
             "Список ссылок на интерфейсы, работающие поверх этого
              интерфейса.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifStackTable";
         }

         leaf-list lower-layer-if {
           type interface-ref;
           config false;
           description
             "Список ссылок на интерфейсы под этим интерфейсом.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifStackTable";
         }

         leaf speed {
           type yang:gauge64;
           units "bits/second";
           config false;
           description
               "Оценка текущей пропускной способности интерфейса (бит/с)
                Для интерфейсов, не меняющих пропускной способности или
                не позволяющих точно оценить её, в этом узле следует
                указывать номинальную пропускную способность. Для 
                интерфейсов, не использующих концепцию пропускной 
                способности, этот узел не присутствует.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifSpeed, ifHighSpeed";
         }

         container statistics {
           config false;
           description
             "Набор связанных с интерфейсом объектов статистики.";

           leaf discontinuity-time {
             type yang:date-and-time;
             mandatory true;
             description
               "Время последнего события, когда один или несколько 
                счётчиков интерфейса подверглись разрыву. Если таких
                событий не было с момента последней реинициализации
                локальной подсистемы управления, указывается время с
                момента этой реинициализации.";
           }

           leaf in-octets {
             type yang:counter64;
             description
               "Общее число октетов, принятых на интерфейсе с учётом
                символов кадрирования.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifHCInOctets";
           }

           leaf in-unicast-pkts {
             type yang:counter64;
             description
               "Число пакетов, доставленных этим подуровнем на 
                вышележащий (под)уровень, которые не были направлены по 
                широковещательному или групповому адресу этого подуровня

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts";
           }

           leaf in-broadcast-pkts {
             type yang:counter64;
             description
               "Число пакетов, доставленных этим подуровнем на 
                вышележащий (под)уровень, которые были направлены по 
                широковещательному адресу этого подуровня.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB -
                          ifHCInBroadcastPkts";
           }

           leaf in-multicast-pkts {
             type yang:counter64;
             description
               "Число пакетов, доставленных этим подуровнем на 
                вышележащий (под)уровень, которые были направлены по 
                групповому адресу этого подуровня. Для протокола
                уровня MAC учитываются групповые (Group) и
                функциональные (Functional) адреса.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB -
                          ifHCInMulticastPkts";
           }

           leaf in-discards {
             type yang:counter32;
             description
               "Число входящих пакетов, которые несмотря на отсутствие
                ошибок были выбраны для отбрасывания,чтобы предотвратить
                их доставку протоколу вышележащего уровня. Одной из 
                возможных причин отбрасывания является очистка буферов.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifInDiscards";
           }

           leaf in-errors {
             type yang:counter32;
             description
               "Для ориентированных на пакеты интерфейсов — это число 
                входящих пакетов с ошибками, препятствующими передаче 
                протоколу вышележащего уровня. Для ориентированных на 
                символы интерфейсов и интерфейсов с фиксированным 
                размером блоков - число входящих блоков передачи с 
                ошибками, препятствующими доставке вышележащему уровню.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifInErrors";
           }

           leaf in-unknown-protos {
             type yang:counter32;
             description
               "Для ориентированных на пакеты интерфейсов - число 
                принятых интерфейсом пакетов, которые были отброшены по 
                причине неизвестного или неподдерживаемого протокола. 
                Для символьно-ориентированных интерфейсов и интерфейсов
                с фиксированным размером блока и поддержкой 
                мультиплексирования протоколов - число принятых через 
                интерфейс блоков передачи, которые были отброшены по 
                причине неизвестного или неподдерживаемого протокола. 
                Для интерфейсов, не поддерживающих мультиплексирование 
                протоколов, этот счётчик не присутствует.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos";
           }


           leaf out-octets {
             type yang:counter64;
             description
               "Общее число переданных интерфейсом октетов, включая 
                символы кадрирования.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifHCOutOctets";
           }

           leaf out-unicast-pkts {
             type yang:counter64;
             description
               "Общее число пакетов, для которых протокол вышележащего
                уровня запросил передачу по адресу, не являющемуся
                широковещательным или групповым адресом для этого 
                подуровня, включая отброшенные и непереданные пакеты.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts";
           }

           leaf out-broadcast-pkts {
             type yang:counter64;
             description
               "Общее число пакетов, для которых протокол вышележащего
                уровня запросил передачу, направленных по
                широковещательным адресам для этого подуровня,
                включая отброшенные и не переданные пакеты.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";

             reference
               "RFC 2863: The Interfaces Group MIB -
                          ifHCOutBroadcastPkts";
           }

           leaf out-multicast-pkts {
             type yang:counter64;
             description
               "Общее число пакетов, для которых протокол вышележащего
                уровня запросил передачу, направленных по
                групповым адресам для этого подуровня, включая
                отброшенные и не переданные пакеты. Для протокола уровня
                MAC это включает групповые и функциональные адреса.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB -
                          ifHCOutMulticastPkts";
           }

           leaf out-discards {
             type yang:counter32;
             description
               "Число исходящих пакетов, которые были выбраны для 
                отбрасывания, несмотря на отсутствие препятствующих
                передаче ошибок. Возможной причиной такого отбрасывания
                является освобождение буферного пространства.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifOutDiscards";
           }

           leaf out-errors {
               "Для пакетно-ориентированных интерфейсов - число 
                исходящих пакетов, которые не были переданы по причине 
                ошибок. Для символьных интерфейсов и интерфейсов с 
                постоянным размером блока - число блоков, не переданных 
                из-за ошибок.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifOutErrors";
           }
         }

       }
     }

     /*
      * Устаревшие определения типов
      */
     typedef interface-state-ref {
       type leafref {
         path "/if:interfaces-state/if:interface/if:name";
       }
       status deprecated;
       description
         "Этот тип применяется моделями данных, которым нужно 
          указывать работающие интерфейсы.";
     }

     /*
      * Устаревшие узлы данных состояния
      */

     container interfaces-state {
       config false;
       status deprecated;
       description
         "Узлы данных для рабочих состояний интерфейсов.";
       list interface {
         key "name";
         status deprecated;

         description
           "Список интерфейсов в устройстве.
            
            Системно-управляемые интерфейсы, созданные системой, всегда
            присутствуют в этом списке, независимо от их конфигурации.";

         leaf name {
           type string;
           status deprecated;
           description
             "Имя интерфейса.

              Реализация сервера МОЖЕТ отображать этот лист на объект 
              MIB ifName. Такие реализации должны использовать тот или
              иной механизм обработки различий в размере и разрешённых
              символах для этого листа и ifName. Определение механизмов
              выходит за рамки этого документа.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifName";
         }

         leaf type {
           type identityref {
             base interface-type;
           }
           mandatory true;
           status deprecated;
           description
              "Тип интерфейса.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifType";
         }

         leaf admin-status {
           if-feature if-mib;
           type enumeration {
             enum up {
               value 1;
               description
                 "Готов пропускать пакеты.";
             }
             enum down {
               value 2;
               description
                 "Не готов пропускать пакеты и не находится в режиме
                  тестирования.";
             }
             enum testing {
               value 3;
               description
                 "Находится в режиме тестирования.";
             }
           }
           mandatory true;
           status deprecated;
           description
             "Желаемое состояние интерфейса.

              Этот лист имеет такую же семантику, как ifAdminStatus.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
         }

         leaf oper-status {
           type enumeration {
             enum up {
               value 1;
               description
                 "Готов пропускать пакеты.";
             }
             enum down {
               value 2;
               description
                 "Интерфейс не пропускает никаких пакетов.";
             }
             enum testing {
               value 3;
               description
                 "В режиме тестирования. Не может пропускать рабочих 
                  пакетов.";
             }
             enum unknown {
               value 4;
               description
                 "Состояние невозможно определить по каким-то причинам";
             }
             enum dormant {
               value 5;
               description
                 "Ожидание внешнего события.";
             }
             enum not-present {
               value 6;
               description
                 "Отсутствует тот или иной (обычно аппаратный) компонент.";
             }
             enum lower-layer-down {
               value 7;
               description
                 "Не работает вследствие состояния базового интерфейса.";
             }
           }
           mandatory true;
           status deprecated;
           description
             "Текущее рабочее состояние интерфейса.

              Этот лист имеет такую же семантику, как ifOperStatus.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifOperStatus";
         }

         leaf last-change {
           type yang:date-and-time;
           status deprecated;
           description
             "Время перехода интерфейса в текущее рабочее состояние.
              Если текущее состояние началось до прежней реинициализации
              локальной подсистемы управления, этого узла не будет.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifLastChange";
         }

         leaf if-index {
           if-feature if-mib;
           type int32 {
             range "1..2147483647";
           }
           mandatory true;
           status deprecated;
           description
             "Значение ifIndex для ifEntry, представленной интерфейсом";
           reference
             "RFC 2863: The Interfaces Group MIB - ifIndex";
         }

         leaf phys-address {
           type yang:phys-address;
           status deprecated;
           description
             "Адрес интерфейса на его протокольном подуровне. Например,
              для интерфейса 802.x этот объект обычно содержит MAC-адрес
              Зависящие от среды модули должны определять порядок битов
              и байтов, а также формат значения для этого объекта. Для
              интерфейсов, не имеющих такого адреса (например, 
              последовательная линия), этого узла не будет.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifPhysAddress";
         }

         leaf-list higher-layer-if {
           type interface-state-ref;
           status deprecated;
           description
             "Список ссылок на интерфейсы, работающие поверх этого
              интерфейса.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifStackTable";
         }

         leaf-list lower-layer-if {
           type interface-state-ref;
           status deprecated;
           description
             "Список ссылок на интерфейсы под этим интерфейсом.";
           reference
             "RFC 2863: The Interfaces Group MIB - ifStackTable";
         }

         leaf speed {
           type yang:gauge64;
           units "bits/second";
           status deprecated;
           description
               "Оценка текущей пропускной способности интерфейса (бит/с)
                Для интерфейсов, не меняющих пропускной способности или
                не позволяющих точно оценить её, в этом узле следует
                указывать номинальную пропускную способность. Для 
                интерфейсов, не использующих концепцию пропускной 
                способности, этот узел не присутствует.";
           reference
             "RFC 2863: The Interfaces Group MIB -
                        ifSpeed, ifHighSpeed";
         }

         container statistics {
           status deprecated;
           description
             "Набор связанных с интерфейсом объектов статистики.";

           leaf discontinuity-time {
             type yang:date-and-time;
             mandatory true;
             status deprecated;
             description
               "Время последнего события, когда один или несколько 
                счётчиков интерфейса подверглись разрыву. Если таких
                событий не было с момента последней реинициализации
                локальной подсистемы управления, указывается время с
                момента этой реинициализации.";
           }

           leaf in-octets {
             type yang:counter64;
             status deprecated;
             description
               "Общее число октетов, принятых на интерфейсе с учётом
                символов кадрирования.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifHCInOctets";
           }

           leaf in-unicast-pkts {
             type yang:counter64;
             status deprecated;
             description
               "Число пакетов, доставленных этим подуровнем на 
                вышележащий (под)уровень, которые не были направлены по 
                широковещательному или групповому адресу этого подуровня.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts";
           }

           leaf in-broadcast-pkts {
             type yang:counter64;
             status deprecated;
             description
               "Число пакетов, доставленных этим подуровнем на 
                вышележащий (под)уровень, которые были направлены по 
                широковещательному адресу этого подуровня.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB -
                          ifHCInBroadcastPkts";
           }

           leaf in-multicast-pkts {
             type yang:counter64;
             status deprecated;
             description
               "Число пакетов, доставленных этим подуровнем на 
                вышележащий (под)уровень, которые были направлены по 
                групповому адресу этого подуровня. Для протокола
                уровня MAC учитываются групповые (Group) и
                функциональные (Functional) адреса.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB -
                          ifHCInMulticastPkts";
           }

           leaf in-discards {
             type yang:counter32;
             status deprecated;
             description
               "Число входящих пакетов, которые несмотря на отсутствие
                ошибок были выбраны для отбрасывания, чтобы не
                доставлять их протоколу вышележащего уровня. Одной из 
                возможных причин отбрасывания является очистка буферов.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifInDiscards";
           }

           leaf in-errors {
             type yang:counter32;
             status deprecated;
             description
               "Для ориентированных на пакеты интерфейсов - число 
                входящих пакетов с ошибками, препятствующими передаче 
                протоколу вышележащего уровня. Для ориентированных на 
                символы интерфейсов и интерфейсов с фиксированным 
                размером блоков - число входящих блоков передачи с 
                ошибками, препятствующими доставке вышележащему уровню.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifInErrors";
           }

           leaf in-unknown-protos {
             type yang:counter32;
             status deprecated;
             description
               "Для ориентированных на пакеты интерфейсов - число 
                принятых интерфейсом пакетов, которые были отброшены по 
                причине неизвестного или неподдерживаемого протокола. 
                Для символьно-ориентированных интерфейсов и интерфейсов 
                с фиксированным размером блока и поддержкой 
                мультиплексирования протоколов - число принятых через 
                интерфейс блоков передачи, которые были отброшены по 
                причине неизвестного или неподдерживаемого протокола. 
                Для интерфейсов, не поддерживающих мультиплексирование
                протоколов, этот счётчик не присутствует.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos";
           }


           leaf out-octets {
             type yang:counter64;
             status deprecated;
             description
               "Общее число переданных интерфейсом октетов, включая 
                символы кадрирования.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifHCOutOctets";
           }

           leaf out-unicast-pkts {
             type yang:counter64;
             status deprecated;
             description
               "Общее число пакетов, для которых протокол вышележащего
                уровня запросил передачу по адресу, не являющемуся
                широковещательным или групповым адресам для этого 
                подуровня, включая отброшенные и не переданные пакеты.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts";
           }

           leaf out-broadcast-pkts {
             type yang:counter64;
             status deprecated;
             description
               "Общее число пакетов, для которых протокол вышележащего
                уровня запросил передачу, направленных по
                широковещательным адресам для этого подуровня,
                включая отброшенные и не переданные пакеты.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB -
                          ifHCOutBroadcastPkts";
           }

           leaf out-multicast-pkts {
             type yang:counter64;
             status deprecated;
             description
                "Общее число пакетов, для которых протокол вышележащего
                уровня запросил передачу, направленных по
                групповым адресам для этого подуровня, включая
                отброшенные и не переданные пакеты. Для протокола уровня
                MAC это включает групповые и функциональные адреса.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB -
                          ifHCOutMulticastPkts";
           }

           leaf out-discards {
             type yang:counter32;
             status deprecated;
             description
               "Число исходящих пакетов, которые были выбраны для 
                отбрасывания, несмотря на отсутствие препятствующих
                передаче ошибок. Возможной причиной такого отбрасывания
                является освобождение буферного пространства.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifOutDiscards";
           }

           leaf out-errors {
             type yang:counter32;
             status deprecated;
             description
               "Для пакетно-ориентированных интерфейсов - число 
                исходящих пакетов, которые не были переданы по причине
                ошибок. Для символьных интерфейсов и интерфейсов с 
                постоянным размером блока - число блоков, не переданных
                из-за ошибок.

                Разрыв значения этого счётчика может происходить при
                реинициализации системы управления или иных событиях,
                указанных временем discontinuity-time.";
             reference
               "RFC 2863: The Interfaces Group MIB - ifOutErrors";
           }
         }
       }
     }
   }

   <CODE ENDS>

6. Взаимодействие с IANA

Этот документ регистрирует URI в реестре IETF XML Registry [RFC3688] в соответствии с форматом RFC 3688.

      URI: urn:ietf:params:xml:ns:yang:ietf-interfaces
      Registrant Contact: The IESG.
      XML: N/A, запрошенный URI является пространством имён XML.

Документ регистрирует модуль YANG в реестре YANG Module Names [RFC6020].

      name:         ietf-interfaces
      namespace:    urn:ietf:params:xml:ns:yang:ietf-interfaces
      prefix:       if
      reference:    RFC 8343

7. Вопросы безопасности

Описанный в этом документе модуль YANG определяет схему данных, которая предназначения для доступа по протоколам сетевого управления, таким как NETCONF [RFC6241] или RESTCONF [RFC8040]. Нижним уровнем NETCONF является защищённый транспорт с обязательной поддержкой SSH6 [RFC6242]. Нижним уровнем RESTCONF является HTTPS с обязательной поддержкой TLS [RFC5246].

Модель управления доступом NETCONF [RFC8341] обеспечивает способы ограничения доступа отдельным пользователям NETCONF или RESTCONF заданным подмножеством всех доступных операций и содержимого NETCONF или RESTCONF.

В модуле YANG имеется множество узлов данных, доступных для чтения, создания и удаления (например, с принятым по умолчанию config true). Эти узлы могут быть конфиденциальными или уязвимыми в некоторых сетевых средах. Операции записи (например, <edit-config>) в эти узлы без подобающей защиты могут оказывать негативное влияние на работу сети. Ниже перечислены субдеревья и узлы данных с указанием уязвимости.

/interfaces/interface

Этот список указывает настроенные в устройстве интерфейсы. Несанкционированный доступ к списку может вынуждать устройство игнорировать пакеты, которые оно должно принимать и обрабатывать.

/interfaces/interface/enabled

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

8. Литература

8.1. Нормативные документы

[RFC2119] Bradner, S., «Key words for use in RFCs to Indicate Requirement Levels», BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.

[RFC2863] McCloghrie, K. and F. Kastenholz, «The Interfaces Group MIB», RFC 2863, DOI 10.17487/RFC2863, June 2000, <https://www.rfc-editor.org/info/rfc2863>.

[RFC3688] Mealling, M., «The IETF XML Registry», BCP 81, RFC 3688, DOI 10.17487/RFC3688, January 2004, <https://www.rfc-editor.org/info/rfc3688>.

[RFC5246] Dierks, T. and E. Rescorla, «The Transport Layer Security (TLS) Protocol Version 1.2», RFC 5246, DOI 10.17487/RFC5246, August 2008, <https://www.rfc-editor.org/info/rfc5246>.

[RFC6020] Bjorklund, M., Ed., «YANG — A Data Modeling Language for the Network Configuration Protocol (NETCONF)», RFC 6020, DOI 10.17487/RFC6020, October 2010, <https://www.rfc-editor.org/info/rfc6020>.

[RFC6241] Enns, R., Ed., Bjorklund, M., Ed., Schoenwaelder, J., Ed., and A. Bierman, Ed., «Network Configuration Protocol (NETCONF)», RFC 6241, DOI 10.17487/RFC6241, June 2011, <https://www.rfc-editor.org/info/rfc6241>.

[RFC6242] Wasserman, M., «Using the NETCONF Protocol over Secure Shell (SSH)», RFC 6242, DOI 10.17487/RFC6242, June 2011, <https://www.rfc-editor.org/info/rfc6242>.

[RFC6991] Schoenwaelder, J., Ed., «Common YANG Data Types», RFC 6991, DOI 10.17487/RFC6991, July 2013, <https://www.rfc-editor.org/info/rfc6991>.

[RFC7950] Bjorklund, M., Ed., «The YANG 1.1 Data Modeling Language», RFC 7950, DOI 10.17487/RFC7950, August 2016, <https://www.rfc-editor.org/info/rfc7950>.

[RFC8040] Bierman, A., Bjorklund, M., and K. Watsen, «RESTCONF Protocol», RFC 8040, DOI 10.17487/RFC8040, January 2017, <https://www.rfc-editor.org/info/rfc8040>.

[RFC8174] Leiba, B., «Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words», BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.

[RFC8341] Bierman, A. and M. Bjorklund, «Network Configuration Access Control Model», STD 91, RFC 8341, DOI 10.17487/RFC8341, March 2018, <https://www.rfc-editor.org/info/rfc8341>.

[RFC8342] Bjorklund, M., Schoenwaelder, J., Shafer, P., Watsen, K., and R. Wilton, «Network Management Datastore Architecture (NMDA)», RFC 8342, DOI 10.17487/RFC8342, March 2018, <https://www.rfc-editor.org/info/rfc8342>.

8.2. Дополнительная литература

[RFC7224] Bjorklund, M., «IANA Interface Type YANG Module», RFC 7224, DOI 10.17487/RFC7224, May 2014, <https://www.rfc-editor.org/info/rfc7224>.

[RFC8340] Bjorklund, M. and L. Berger, Ed., «YANG Tree Diagrams», BCP 215, RFC 8340, DOI 10.17487/RFC8340, March 2018, <https://www.rfc-editor.org/info/rfc8340>.

Приложение A. Пример модуля для интерфейса Ethernet

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

   module example-ethernet {
     namespace "http://example.com/ethernet";
     prefix "eth";

     import ietf-interfaces {
       prefix if;
     }
     import iana-if-type {
       prefix ianaift;
     }

     // Конфигурационные параметры для интерфейсов Ethernet 
     augment "/if:interfaces/if:interface" {
       when "if:type = 'ianaift:ethernetCsmacd'";

       container ethernet {
         container transmission {
           choice transmission-params {
             case auto {
               leaf auto-negotiate {
                 type empty;
               }
             }
             case manual {
               container manual {
                 leaf duplex {
                   type enumeration {
                     enum "half";
                     enum "full";
                   }
                 }
                 leaf speed {
                   type enumeration {
                     enum "10Mb";
                     enum "100Mb";
                     enum "1Gb";
                     enum "10Gb";
                   }
                 }
               }
             }
           }
           leaf duplex {
             type enumeration {
               enum "half";
               enum "full";
             }
             config false;
           }
         }
         // Другие параметры, относящиеся к Ethernet ...
       }
     }
   }

Приложение B. Пример модуля для связки интерфейсов Ethernet

В этом приложении представлен пример определения уровней интерфейсов. Определён интерфейс связки Ethernet, объединяющей несколько интерфейсов Ethernet в один логический интерфейс.

   module example-ethernet-bonding {
     namespace "http://example.com/ethernet-bonding";
     prefix "bond";

     import ietf-interfaces {
       prefix if;
     }
     import iana-if-type {
       prefix ianaift;
     }

     augment "/if:interfaces/if:interface" {
       when "if:type = 'ianaift:ieee8023adLag'";

       leaf-list slave-if {
         type if:interface-ref;
         must "/if:interfaces/if:interface[if:name = current()]"
            + "/if:type = 'ianaift:ethernetCsmacd'" {
           description
             "Ведомый интерфейс должен иметь тип ethernetCsmacd.";
         }
       }
       leaf bonding-mode {
         type enumeration {
           enum round-robin;
           enum active-backup;
           enum broadcast;
         }
       }
       // Другие параметры конфигурации, время восстановления и пр.
     }
   }

Приложение C. Пример модуля для интерфейса VLAN

В этом приложении представлен пример определения интерфейса VLAN.

   module example-vlan {
     namespace "http://example.com/vlan";
     prefix "vlan";

     import ietf-interfaces {
       prefix if;
     }
     import iana-if-type {
       prefix ianaift;
     }

     augment "/if:interfaces/if:interface" {
       when "if:type = 'ianaift:ethernetCsmacd' or
             if:type = 'ianaift:ieee8023adLag'";
       leaf vlan-tagging {
         type boolean;
         default false;
       }
     }

     augment "/if:interfaces/if:interface" {
       when "if:type = 'ianaift:l2vlan'";

       leaf base-interface {
         type if:interface-ref;
         must "/if:interfaces/if:interface[if:name = current()]"
            + "/vlan:vlan-tagging = 'true'" {
           description
             "На базовом интерфейсе должны быть разрешены теги VLAN.";
         }
       }
       leaf vlan-id {
         type uint16 {
           range "1..4094";
         }
         must "../base-interface" {
           description
             "При определении vlan-id нужно указать базовый интерфейс.";
         }
       }
     }
   }

Приложение D. Пример отклика NETCONF <get-config>

В этом приложении дан пример отклика на запрос NETCONF <get-config> для хранилища рабочей конфигурации на устройстве, реализующем приведённый выше пример модели данных.

   <rpc-reply
       xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
       message-id="101">
     <data>
       <interfaces
           xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"
           xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type"
           xmlns:vlan="http://example.com/vlan">

         <interface>
           <name>eth0</name>
           <type>ianaift:ethernetCsmacd</type>
           <enabled>false</enabled>
         </interface>

         <interface>
           <name>eth1</name>
           <type>ianaift:ethernetCsmacd</type>
           <enabled>true</enabled>
           <vlan:vlan-tagging>true</vlan:vlan-tagging>
         </interface>

         <interface>
           <name>eth1.10</name>
           <type>ianaift:l2vlan</type>
           <enabled>true</enabled>
           <vlan:base-interface>eth1</vlan:base-interface>
           <vlan:vlan-id>10</vlan:vlan-id>
         </interface>

         <interface>
           <name>lo1</name>
           <type>ianaift:softwareLoopback</type>
           <enabled>true</enabled>
         </interface>

       </interfaces>
     </data>
   </rpc-reply>

Приложение E. Пример отклика NETCONF <get-data>

В этом приложении дан пример отклика на запрос NETCONF <get-data> для хранилища рабочего состояния на устройстве, реализующем приведённый выше пример модели данных.

В этом примере используется аннотация origin, определённая в модуле ietf-origin [RFC8342].

   <rpc-reply
       xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
       message-id="101">
     <data xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-datastores">
       <interfaces
           xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"
           xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type"
           xmlns:vlan="http://example.com/vlan"
           xmlns:or="urn:ietf:params:xml:ns:yang:ietf-origin">

         <interface or:origin="or:intended">
           <name>eth0</name>
           <type>ianaift:ethernetCsmacd</type>
           <enabled>false</enabled>
           <admin-status>down</admin-status>
           <oper-status>down</oper-status>
           <if-index>2</if-index>
           <phys-address>00:01:02:03:04:05</phys-address>
           <statistics>
             <discontinuity-time>
               2013-04-01T03:00:00+00:00
             </discontinuity-time>
             <!-- Здесь показаны счётчики -->
           </statistics>
         </interface>

         <interface or:origin="or:intended">
           <name>eth1</name>
           <type>ianaift:ethernetCsmacd</type>
           <enabled>true</enabled>
           <admin-status>up</admin-status>
           <oper-status>up</oper-status>
           <if-index>7</if-index>
           <phys-address>00:01:02:03:04:06</phys-address>
           <higher-layer-if>eth1.10</higher-layer-if>
           <statistics>
             <discontinuity-time>
               2013-04-01T03:00:00+00:00
             </discontinuity-time>
             <!-- Здесь показаны счётчики -->
           </statistics>
           <vlan:vlan-tagging>true</vlan:vlan-tagging>
         </interface>

         <interface or:origin="or:intended">
           <name>eth1.10</name>
           <type>ianaift:l2vlan</type>
           <enabled>true</enabled>
           <admin-status>up</admin-status>
           <oper-status>up</oper-status>
           <if-index>9</if-index>
           <lower-layer-if>eth1</lower-layer-if>
           <statistics>
             <discontinuity-time>
               2013-04-01T03:00:00+00:00
             </discontinuity-time>
             <!-- Здесь показаны счётчики -->
           </statistics>
           <vlan:base-interface>eth1</vlan:base-interface>
           <vlan:vlan-id>10</vlan:vlan-id>
         </interface>

         <!-- Этот интерфейс не настроен -->
         <interface or:origin="or:system">
           <name>eth2</name>
           <type>ianaift:ethernetCsmacd</type>
           <admin-status>down</admin-status>
           <oper-status>down</oper-status>
           <if-index>8</if-index>
           <phys-address>00:01:02:03:04:07</phys-address>
           <statistics>
             <discontinuity-time>
               2013-04-01T03:00:00+00:00
             </discontinuity-time>
             <!-- Здесь показаны счётчики -->
           </statistics>
         </interface>

         <interface or:origin="or:intended">
           <name>lo1</name>
           <type>ianaift:softwareLoopback</type>
           <enabled>true</enabled>
           <admin-status>up</admin-status>
           <oper-status>up</oper-status>
           <if-index>1</if-index>
           <statistics>
             <discontinuity-time>
               2013-04-01T03:00:00+00:00
             </discontinuity-time>
             <!-- Здесь показаны счётчики -->
           </statistics>
         </interface>

       </interfaces>
     </data>
   </rpc-reply>

Приложение F. Примеры схем именования интерфейсов

В этом приложении приведены примеры стратегии развёртывания.

В примерах применятся модель данных ex-vlan (Приложение C) для настройки управляемых пользователем интерфейсов.

F.1. Маршрутизатор с ограничениями для имён интерфейсов

В этом примере маршрутизатор имеет 4 линейных платы с восемью портами на каждой. Гнезда для плат имеют физические номера от 0 до 3, а порты на каждой плате — от 0 до 7. Каждая плата имеет порты Fast Ethernet или Gigabit Ethernet.

Обусловленными устройством именами физических интерфейсов будут fastethernet-N/M или gigabitethernet-N/M.

Имена интерфейсов VLAN ограничены формой <physical-interface-name>.<subinterface-number>.

Предполагается, что схема именования известна оператору. Реализация автоматически инициализирует значение type в соответствии с именем интерфейса.

Сервер NETCONF не анонсирует возможность arbitrary-names в сообщении <hello>.

Оператор может настроить физический интерфейс, передавая команду <edit-config>, содержащую

     <interface nc:operation="create">
       <name>fastethernet-1/0</name>
     </interface>

При получении сервером такого запроса он будет устанавливать для листа type значение ianaift:ethernetCsmacd. Если клиент передаст команду <get-config> после приведённой выше команды <edit-config>, он получит

     <interface>
       <name>fastethernet-1/0</name>
       <type>ianaift:ethernetCsmacd</type>
     </interface>

Клиент может настроить интерфейс VLAN с помощью команды <edit-config>, содержащей

     <interface nc:operation="create">
       <name>fastethernet-1/0.10005</name>
       <type>ianaift:l2vlan</type>
       <vlan:base-interface>fastethernet-1/0</vlan:base-interface>
       <vlan:vlan-id>5</vlan:vlan-id>
     </interface>

Если клиент попытается изменить тип физического интерфейса командой <edit-config>, содержащей

     <interface nc:operation="merge">
       <name>fastethernet-1/0</name>
       <type>ianaift:tunnel</type>
     </interface>

сервер вернёт ошибку invalid-value, поскольку новый тип не соответствует имени.

F.2. Маршрутизатор с произвольными именами интерфейсов

В этом примере маршрутизатор имеет 4 линейных платы с восемью портами на каждой. Гнезда для плат имеют физические номера от 0 до 3, а порты на каждой плате — от 0 до 7. Каждая плата имеет порты Fast Ethernet или Gigabit Ethernet.

Обусловленными устройством именами физических интерфейсов будут fastethernet-N/M или gigabitethernet-N/M.

Реализация не ограничивает управляемые пользователем имена. Это позволяет оператору более гибко применять конфигурации к разным интерфейсам. Однако это несколько усложняет отображение имён интерфейсов, найденных в других протоколах, на записи конфигурации.

Сервер NETCONF анонсирует возможность arbitrary-names в сообщении <hello>.

Физические интерфейсы настраиваются в соответствии с приложением F.1.

Оператор может настроить интерфейс VLAN с помощью команды <edit-config>, содержащей

     <interface nc:operation="create">
       <name>acme-interface</name>
       <type>ianaift:l2vlan</type>
       <vlan:base-interface>fastethernet-1/0</vlan:base-interface>
       <vlan:vlan-id>5</vlan:vlan-id>
     </interface>

При необходимости оператор может перенести конфигурацию с именем acme-interface на другой физический интерфейс с помощью команды <edit-config>, содержащей

     <interface nc:operation="merge">
       <name>acme-interface</name>
       <vlan:base-interface>fastethernet-1/1</vlan:base-interface>
     </interface>

F.3. Коммутатор Ethernet с ограничениями для имён интерфейсов

В этом примере коммутатор Ethernet имеет множество портов, каждый из которых указывается простым номером.

Зависящие от устройства имена физических интерфейсов являются номерами, соответствующими номерам физических портов.

Оператор может настроить физический интерфейс с помощью команды <edit-config>, содержащей

     <interface nc:operation="create">
       <name>6</name>
     </interface>

Когда сервер получает такой запрос, он устанавливает для листа type значение ianaift:ethernetCsmacd. Если клиент выполнит команду <get-config> после показанной выше команды <edit-config>, он получит

     <interface>
       <name>6</name>
       <type>ianaift:ethernetCsmacd</type>
     </interface>

F.4. Типовой хост с ограничениями для имён интерфейсов

В этом примере обычный хост имеет интерфейсы, именуемые ядром. Система идентифицирует физические интерфейсы по именам, назначенным операционной системой.

Имена интерфейсов VLAN ограничены формой <physical-interface-name>:<vlan-number>.

Сервер NETCONF не анонсирует возможность arbitrary-names в сообщении <hello>.

Оператор может настроить интерфейс с помощью команды <edit-config>, содержащей

     <interface nc:operation="create">
       <name>eth8</name>
     </interface>

Когда сервер получает такой запрос, он устанавливает для листа type значение ianaift:ethernetCsmacd. Если клиент выполнит команду <get-config> после показанной выше команды <edit-config>, он получит

     <interface>
       <name>eth8</name>
       <type>ianaift:ethernetCsmacd</type>
     </interface>

Клиент может настроить интерфейс VLAN с помощью команды <edit-config>, содержащей

     <interface nc:operation="create">
       <name>eth8:5</name>
       <type>ianaift:l2vlan</type>
       <vlan:base-interface>eth8</vlan:base-interface>
       <vlan:vlan-id>5</vlan:vlan-id>
     </interface>

F.5. Типовой хост с произвольными именами интерфейсов

В этом примере обычный хост имеет интерфейсы, именуемые ядром. Система идентифицирует физические интерфейсы по именам, назначенным операционной системой.

Реализация не ограничивает управляемые пользователем имена. Это позволяет оператору более гибко применять конфигурации к разным интерфейсам. Однако это несколько усложняет отображение имён интерфейсов, найденных в других протоколах, на записи конфигурации.

Сервер NETCONF анонсирует возможность arbitrary-names в сообщении <hello>.

Физические интерфейсы настраиваются как в приложении F.4.

Оператор может настроить интерфейс VLAN с помощью команды <edit-config>, содержащей

     <interface nc:operation="create">
       <name>acme-interface</name>
       <type>ianaift:l2vlan</type>
       <vlan:base-interface>eth8</vlan:base-interface>
       <vlan:vlan-id>5</vlan:vlan-id>
     </interface>

При необходимости оператор может перенести конфигурацию с именем acme-interface на другой физический интерфейс с помощью команды <edit-config>, содержащей

     <interface nc:operation="merge">
       <name>acme-interface</name>
       <vlan:base-interface>eth3</vlan:base-interface>
     </interface>

Благодарности

Автор благодарен Alexander Clemm, Per Hedeland, Ladislav Lhotka и Juergen Schoenwaelder за ценные замечания.

Адрес автора

Martin Bjorklund

Tail-f Systems

Email: mbj@tail-f.com

Перевод на русский язык

Николай Малых

nmalykh@protokols.ru

1Network Management Datastore Architecture — архитектура хранилища данных сетевого управления.

2Internet Engineering Task Force.

3Internet Engineering Steering Group.

4Simple Network Management Protocol — простой протокол сетевого управления.

5Media Access Control — управление доступом к среде передачи.

6Secure Shell — защищённая оболочка.

Рубрика: RFC | Комментарии к записи RFC 8343 A YANG Data Model for Interface Management отключены

RFC 8303 On the Usage of Transport Features Provided by IETF Transport Protocols

Internet Engineering Task Force (IETF)                          M. Welzl
Request for Comments: 8303                            University of Oslo
Category: Informational                                        M. Tuexen
ISSN: 2070-1721                         Muenster Univ. of Appl. Sciences
                                                              N. Khademi
                                                      University of Oslo
                                                           February 2018

On the Usage of Transport Features Provided by IETF Transport Protocols

Использование транспортных свойств, предоставляемых транспортными протоколами IETF

PDF

Аннотация

В этом документе описано, как транспортные протоколы TCP1, MPTCP2, SCTP3, UDP4 и UDP-Lite5 раскрывают свои услуги приложениям и как приложение может настроить и использовать функции, составляющие эти услуги. Рассматриваются также услуги, предоставляемые механизмом контроля перегрузок LEDBAT6. Описание задает набор транспортных абстракций, которые можно экспортировать в API транспортных служб (TAPS).

Статус документа

Документ не содержит какой-либо спецификации (Internet Standards Track) и публикуется с информационными целями.

Документ является результатом работы IETF7 и представляет согласованный взгляд сообщества IETF. Документ прошел открытое обсуждение и был одобрен для публикации IESG8. Дополнительную информацию о стандартах Internet можно найти в разделе 2 в RFC 7841.

Информация о текущем статусе документа, найденных ошибках и способах обратной связи доступна по ссылке https://www.rfc-editor.org/info/rfc8303.

Авторские права

Copyright (c) 2018. Авторские права принадлежат IETF Trust и лицам, указанным в качестве авторов документа. Все права защищены.

К документу применимы права и ограничения, указанные в BCP 78 и IETF Trust Legal Provisions и относящиеся к документам IETF (http://trustee.ietf.org/license-info), на момент публикации данного документа. Прочтите упомянутые документы внимательно. Фрагменты программного кода, включённые в этот документ, распространяются в соответствии с упрощённой лицензией BSD, как указано в параграфе 4.e документа IETF Trust Legal Provisions, без каких-либо гарантий (как указано в Simplified BSD License).

1. Введение

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

Процесс разработки начался с обзора услуг, предоставляемых транспортными протоколами IETF и механизмами контроля перегрузок [RFC8095]. Данный документ вместе с [RFC8304] дополняют указанный обзор подробным описанием взаимодействия приложений с транспортными протоколами TCP, MPTCP, SCTP, UDP и UDP-Lite. Определены также примитивы для включения, выключения и настройки механизма контроля перегрузок для индивидуального (unicast) трафика LEDBAT. Для UDP и UDP-Lite первым шагом анализа служит обсуждение текста связанных с ними RFC, приведенное в [RFC8304].

Это «временной срез» транспортных протоколов IETF опубликован как RFC для документирования анализа авторами и членами рабочей группы TAPS. Он содержит набор транспортных абстракций, которые можно экспортировать в TAPS API. Документ обеспечивает базу для определения минимального набора транспортных служб, которые следует реализовать конечной системе, поддерживающей TAPS [TAPS-MINSET].

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

Те части протоколов, которые явно означены необязательными, не рассматриваются здесь. Не обсуждаются также взаимодействия между приложением и транспортным протоколом, не связанные напрямую с работой протокола. Например, приложение может разными способами использовать опции сокета для индикации своей заинтересованности в получении тех или иных уведомлений [RFC6458]. Однако для идентификации примитивов, событий и транспортных функций способность включать и отключать получение уведомлений интереса не представляет. Точно так же, сокеты в форме «один со многими» [RFC6458] просто влияют на стиль программирования приложений и не рассматриваются здесь. То же самое относится и к способности получить неизменное значение параметра, ранее установленного приложением (например, с помощью операции get [RFC6458]).

Этот документ представляет трехэтапный процесс получения списка транспортных функций. На первом этапе обсуждается текст соответствующих RFC по протоколам. На втором этапе предыдущее обсуждение служит для выведения списка примитивов и событий, которые единообразно классифицируются по протоколам. Здесь предпринята попытка представить или (при отсутствии теста, представляющего примитивы и события) сконструировать примитивы и события в несколько обобщенной форме для выделений сходства. Это достигается, например, переименованием протокольных примитивов и событий или исключения строго сопоставления (1:1) между примитивами и событиями в спецификации протокола и списке. Финальный этап 3 представляет транспортные функции (свойства) на основе этапа 2, указывая реализующие их протоколы.

В списке, полученном на этапе 2, некоторые транспортные функции отсутствуют, поскольку они являются неявными в некоторых протоколах и становятся явными лишь при рассмотрении множества транспортных функций, предоставляемых всеми протоколами. Например, TCP всегда поддерживает контроль перегрузок, но нужно рассмотреть его вместе с такими протоколами как UDP (нет контроля перегрузок) до того, как включить контроль перегрузок в число транспортных функций. Поэтому полный список транспортных свойств для всех протоколов доступен лишь после этапа 3.

Некоторые протоколы ориентированы на соединения и в них часто применяется начальный вызов определенного примитива для организации соединения до начала обмена данными и требуется явный разрыв соединения путем вызова другого примитива (обычно он называется Close). Соединение — это общее состояние, к которому относятся некоторые транспортные примитивы, например, настройка общих параметров конфигурации. Организация, поддержка и разрыв соединений поэтому служат для классификации транспортных примитивов ориентированных на соединения протоколов на этапах 2 и 3. Поэтому предполагается, что UDP используется с «подключенными» сокетами, т. е. сокетами, которые привязаны к конкретной паре адресов и портов [RFC8304].

2. Терминология

Transport Feature — транспортная функция (свойство)

Сквозная функция обеспечиваемая приложению транспортным уровнем. Примерами являются защита конфиденциальности, надежная или упорядоченная доставка, работа с потоком или сообщениями и т. п.

Transport Service — транспортный сервис (служба)

Набор транспортных функций, обеспечивающих приложению полное обслуживание, без привязки к протоколу кадрирования.

Transport Protocol — транспортный протокол

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

Transport Protocol Component — компонент транспортного протокола

Реализация транспортной функции в протоколе.

Transport Service Instance — экземпляр транспортного сервиса

Набор транспортных протоколов и выбранными свойствами и параметрами конфигурации, реализующий один транспортный сервис, например, стек протоколов RTP на основе UDP.

Application — приложение

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

End точка — конечная точка

Сущность (элемент), взаимодействующая с одной или несколькими другими конечными точками с помощью протокола транспортного уровня.

Connection — соединение

Общее состояние двух или более конечных точек, сохраняемое в сообщениях между этими точками.

Primitive — примитив

Функция, вызов которой служит для взаимодействия между приложением и транспортной конечной точкой. Примитив связан с одной или несколькими транспортными функциями.

Event — событие

Примитив, вызываемый транспортной конечной точкой.

Parameter — параметр

Значение, передаваемое примитивом между приложением и транспортным протоколом.

Socket — сокет

Комбинация IP-адреса и номера порта у получателя.

Transport Address — транспортный адрес

Комбинация адреса IP, транспортного протокола и номера порта, используемая транспортным протоколом.

3. Этап 1

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

3.1. Примитивы, предоставляемые TCP

В исходной спецификации TCP [RFC0793] сказано:

Протокол TCP предназначен для надежной и гарантированной доставки данных между хостами в компьютерных сетях с коммутацией пакетов и между такими сетями через промежуточные системы.

В параграфе 3.8 [RFC0793] определено взаимодействие с приложением через несколько транспортных примитивов. Предполагается также, что операционная система обеспечивает для TCP возможности асинхронной передачи сигналов приложению. Примитивы, представляющие такие сигналы, в этом разделе называются событиями (event).

Open

Вызов может быть активным для организации соединения или пассивным для прослушивания входящих вызовов. Все другие примитивы связаны с конкретным соединением, которое предполагается созданным заранее. Активный вызов open содержит сокет, пассивный вызов с сокетом ожидает входящего соединения. Возможно при пассивном вызове не задавать сокет для восприятия любых входящих соединений. Полностью заданный пассивный вызов можно перевести в активное состояние с помощью Send. Можно при желании указать тайм-аут, по истечении которого TCP будет разрывать соединение, если через него получателю не были доставлены данные (если тайм-аут не задан, используется принятый по умолчанию). Процедура разрыва соединения служит для предотвращения избыточных повторов передачи и приложение может контролировать порог, определяющий условия разрыва. Порог может указываться временем ожидания или числом повторов передачи [RFC1122]. Тайм-аут также можно указать числом повторов.

Для многодомных хостов можно указать локальный адрес IP [RFC1122]. Если адрес не задан, при активном вызове будет использован принятый по умолчанию. Пассивный вызов будет ждать входящих соединений по всем локальным адресам и использовать адрес IP, на который поступил вызов. Параметр options позволяет приложению указать опции IP, такие как Source Route, Record Route, Timestamp [RFC1122]. Не указано, к каким сегментам соединения этот параметр следует применять (вероятно, ко всем, поскольку это задано для опции IP Source Route в параграфе 4.2.3.8 в [RFC1122]). Опция Source Route является единственной опцией IP в этом параметре, которая не указана не обязательной (non-optional), и позволяет приложению указать заданный отправителем маршрут при активной организации соединения TCP.

Кортежи первичных ключей (Master Key Tuple или MKT) для аутентификации можно задать при вызове Open (параграф 7.1 в [RFC5925]). При использовании аутентификации защищаются полные сегменты TCP, включая псевдозаголовок IPv4, заголовок и данные TCP.

TCP Fast Open (TFO) [RFC7413] позволяет приложению незамедлительно передать сообщение при активном вызове open на пассивную сторону соединения TCP вместе с первым пакетом организации соединения (SYN). Это может быть полезно для приложений, чувствительных к задержке при организации соединения TCP. В [RFC7413] сказано: «реализациям TCP недопустимо использовать TFO по умолчанию и можно применять TFO лишь при явном запросе приложения или настройке на уровне сервисного порта». Размер сообщения, передаваемого с TFO, не может быть больше максимального сегмента TCP (за вычетом опций, применяемых в SYN). Для активной стороны рекомендуется изменить или заменить вызов () для поддержки аргумента, задающего пользовательский буфер [RFC7413]. Для пассивной стороны приложение должно включить прием запросов Fast Open, например, через новую опцию сокета TCP_FASTOPEN setsockopt() перед listen(). Принимающее приложение должно быть готово воспринимать дубликаты сообщения TFO, поскольку первые данные, записанные в сокет, могут доставляться приложению на удаленном хосте в нескольких экземплярах.

Send

Этот примитив приложение использует для передачи локальной транспортной конечной точке TCP числа байтов, которые протоколу TCP нужно гарантированно передать на другую сторону соединения. Флаг важности (urgent) при его установке говорит, что данные этого вызова являются срочными (важными) и эту важность следует указать принимающему процессу, если он еще не воспринял всех полученных данных без такого флага. Необязательный параметр может задавать тайм-аут для соединения (см. Open). Кроме того, необязательные параметры позволяют указать предпочтительный исходящий MKT (current_key) и/или предпочтительный входящий MKT (rnext_key) для соединения (параграф 7.1 в [RFC5925]).

Receive

Этот примитив выделяет приемный буфер для представленного числа байтов. Примитив возвращает число принятых байтов, сообщенное буфером, когда они были приняты и записаны в буфер протоколом TCP. Приложение информируется о важных данных через флаг urgent, устанавливаемый при получении важных (срочных) данных. Сброшенный флаг говорит об отсутствии важных данных, т. е. они не были получены или данный вызов Receive вернул все срочные данные. Приложению также сообщается current_key и rnext_key из недавно принятого сегмента через необязательный параметр (параграф 7.1 в [RFC5925]).

Close

Этот примитив закрывает соединение с одной стороны. Семантически это говорит об отсутствии данных для передачи, но не закрывает прием поступающих данных, которые могут еще быть у другой стороны. Этот вызов гарантированно доставляет все уже полученные данные TCP (при отказе Close превращается в abort).

Abort

Этот примитив прерывает все ожидающие Send и Receive. Конечной точке TCP на другой стороне соединения передается сообщение TCP RESET [RFC0793].

Событие Close

TCP использует этот примитив для информирования приложения о вызове примитива Close приложением на удаленной стороне, чтобы локальное приложение также могло применить Close и аккуратно завершить соединение (параграф 3.5 в [RFC0793]).

Событие Abort

Когда TCP получает от партнера RESET, протокол: «уведомляет пользователя, и переходит в состояние CLOSED» (параграф 3.4 в [RFC0793]).

Событие User Timeout

Это событие выполняется при достижении заданного пользователем времени ожидания (параграф 3.9 в [RFC0793]), см. Open. Очищаются все очереди и приложение информируется о разрыве соединения по тайм-ауту.

Событие Error_Report

Это событие информирует приложение о «мягкой ошибке» (soft error), которую можно игнорировать [RFC5461], включая прием сообщения ICMP об ошибке или избыточные повторы (порог, который ниже порога прерывания), см. параграф 4.2.4.1 в [RFC1122].

Type-of-Service

В параграфе 4.2.4.2 [RFC1122] сказано: «Прикладному уровню должна быть обеспечена возможность задавать тип обслуживания TOS для сегментов, передаваемых через соединение». Приложению следует поддерживать возможность смены TOS в течение срока действия соединения и значение TOS следует без изменений передавать на уровень IP. Позднее поле TOS было переопределено и модель дифференцированного обслуживания (Differentiated Services или Diffserv) [RFC2475] [RFC3260] заменила это поле в заголовке IP, назначив 6 старших битов для передачи кода DSCP (Differentiated Services Code Point) [RFC2474].

Nagle

Алгоритм Nagle на некоторое время задерживает передачу данных для увеличения вероятности отправки полноразмерного сегмента (параграф 4.2.3.4 в [RFC1122]). Приложение может отключить алгоритм Nagle для отдельного соединения.

User Timeout Option

Опция пользовательского тайм-аута (User Timeout Option или UTO) [RFC5482] позволяет одной стороне соединения TCP аносировать свое значение заданного пользователем времени ожидания, чтобы друга сторона могла соответственно скорректировать свое значение. В дополнение к настройке тайм-аута (см. Send) имеется 3 переменных состояния на уровне соединения, которые приложение может настроить для работы UTO — adv_uto задает значение UTO, анонсируемое удаленному партнеру TCP (по умолчанию системное значение пользовательского тайм-аута), флаг enabled (по умолчанию false) для включения и отключения UTO на соединении и флаг changeable (по умолчанию true), определяющий возможность смены тайм-аута на основании опции UTO, полученной от удаленного партнера. Первые 2 переменных работают для приема и передачи, changeable принимает значение false, когда приложение явно задает пользовательский тайм-аут (см. Send).

Set/Get Authentication Parameters

Предпочтительный исходящий кортеж MKT (current_key) и/или предпочтительный входящий MKT (rnext_key) можно настроить для соединения. Можно извлечь информацию о текущих значениях current_key и rnext_key из последнего принятого сегмента (параграф 7.1 в [RFC5925]).

3.1.1. Исключенные примитивы и параметры

Примитиву Open можно передать информацию о предпочтении или защите (разделении — compartment) [RFC0793], но это не рассматривается здесь, поскольку в настоящее время в основно неактуально [RFC7414].

Примитив Status не включен в документ, поскольку в исходной спецификации TCP он указан как «зависящий от реализации» и сказано, что он: «может быть исключен без негативного влияния» [RFC0793]. Более того, хотя описан блок данных, содержащий конкретную информацию, сказано, что вся эта информация может быть доступна не всегда. Хотя в [RFC5925] сказано: «STATUS следует дополнить для обеспечения возможности читать MKT текущего или ожидающего соединения (для подтверждения)», та же самая информация доступна через примитив Receive, который в соответствии с [RFC5925] «должен быть дополнен» этой функциональностью. Примитив Send включает необязательный флаг push, установка которого требует немедленной передачи данных получателю [RFC0793]. Описанный там же примитив Receive может (в некоторых обстоятельствах) устанавливать флаг push. Поскольку функциональность push необязательная для примитивов Send и Receive [RFC1122], она не рассматривается здесь. В [RFC1122] введены сообщения keep-alive для TCP, но они не обязательны для реализации и не включены в документ. Там же сказано: «Некоторые реализации TCP включают вызовы FLUSH», что говорит о необязательности данного вызова и он не рассматривается здесь.

3.2. Примитивы, предоставляемые MPTCP

MPTCP является расширением TCP, позволяющим применять несколько путей для одного потока данных. Это достигается созданием так называемых субпотоков TCP для каждого из интерфейсов и планированием трафика через эти субпотоки. Сервис, предоставляемый MPTCP описан в [RFC6182]:

Протокол Multipath TCP должен следовать модели сервиса, используемой TCP [1] — упорядоченная, надежная, ориентированная на байты доставка. Кроме того, соединению Multipath TCP следует предоставлять приложению пропускную способность не хуже ожидаемой при работе через одно соединение TCP по любому из доступных путей.

Кроме того, имеются некоторые ограничения на API, раскрываемый MPTCP, как отмечено в [RFC6182]:

Поддерживающий множество путей эквивалент TCP должен сохранять некоторый уровень совместимости с имеющимися TCP API, чтобы существующие приложения могли использовать новый транспорт, просто обновив операционные системы конечных хостов.

Таким образом, предоставляемые MPTCP примитивы эквивалентны примитивам TCP. Тем не менее, в MPTCP RFC [RFC6824] и [RFC6897] разъяснены некоторые детали примитивов TCP в отношении MPTCP и добавлены расширения для лучшего управления субпотоками MPTCP. Ниже приведен список уточнений и расширений, которые упомянутые RFC содержат для примитивов TCP.

Open

«Приложению следует иметь возможность включать и выключать применение MPTCP» [RFC6897]. Эта функциональность может быть обеспечена опцией сокета tcp_multipath_enable. Кроме того протокол MPTCP должен отключаться, если приложение привязано к конкретному адресу [RFC6897].

Send/Receive

Передача и прием данных не требуют менять приложение для использования MPTCP [RFC6824]. Уровень MPTCP принимает от приложения один поток данных и делит его на субпотоки с достаточным объемом данных управления, позволяющих гарантированно доставить данные с сохранением порядка и собрать их на приемной стороне.

Использование указателя важности (Urgent Pointer) отличается MPTCP и в [RFC6824] сказано: «Субпотоку TCP недопустимо использовать Urgent Pointer для прерывания имеющегося сопоставления».

Управление адресами и субпотоками

MPTCP использует разные адреса и позволяет хосту анонсировать их как часть протокола. В [RFC6897] сказано: «Приложению следует обеспечивать возможность ограничить MPTCP привязкой к данному набору адресов», что позволяет приложению указать ограниченный набор адресов для использования протоколом MPTCP. Там же сказано: «Приложению следует иметь возможность получить список пар адресов, используемых субпотоками MPTCP».

3.3. Примитивы, предоставляемые SCTP

TCP имеет множество ограничений, которые устранены в SCTP (параграф 1.1 в [RFC4960]). Три снятых ограничения напрямую отражаются в транспортных возможностях, которые видит использующее SCTP приложение — 1) возможность сохранить разграничители сообщений, 2) отсутствие гарантий доставки и сохранения порядка без запроса приложения, 3) поддержка многодомности. В SCTP соединения называются ассоциациями (association) и могут создаваться не только между парой (как в TCP), но и между множеством адресов на каждой конечной точке.

Раздел 10 базовой спецификации SCTP [RFC4960] задает взаимодействие с приложением, которое в SCTP называется протоколом вышележащего уровня (Upper-Layer Protocol или ULP). Предполагается, что операционная система обеспечивает SCTP возможность асинхронной передачи сигналов приложению, примитивы такой сигнализации называются здесь событиями (event) и описаны ниже. В дополнение к абстрактному API, заданному в разделе 10 [RFC4960], были описаны расширения для API сокетов [RFC6458], включающие функциональность базового протокола [RFC4960] и некоторые расширения [RFC3758] [RFC4895] [RFC5061]. Для других расширений протокола ([RFC6525] [RFC6951] [RFC7053] [RFC7496] [RFC7829] [RFC8260]) соответствующие расширения API сокетов описаны в спецификациях. Функциональность, раскрываемая ULP через все эти API описана здесь.

Абстрактный API содержит примитив SetProtocolParameters, позволяющий настраивать элементы списка параметров [RFC4960]. В спецификации протокола сказано: «Реализация SCTP может разрешать ULP изменение некоторых параметров протокола». Это указывает, что ни один из элементов этого списка параметров не является обязательным для настройки ULP. Поэтому здесь рассматриваются лишь параметры в абстрактном API, которые также указаны в одном из других RFC, упомянутых выше, что привело к исключению параметров RTO.Alpha, RTO.Beta, HB.Max.Burst. Для четкости сам примитив SetProtocolParameters здесь заменен примитивами настройки параметров или групп параметров.

Initialize

Примитив Initialize создает локальный экземпляр SCTP, связывая его с набором локальных адресов и номером порта (если он задан) [RFC4960]. Примитив нужно вызывать лишь один раз для набора локальных адресов. При создании ассоциации может использоваться множество параметров для нее, это нужно сделать до соединения (с помощью описанного ниже примитива Associate). Можно указать максимальное число входных потоков, которые приложение готово поддерживать, максимальное число попыток передачи INIT (первое сообщение при создании ассоциации) и максимальный тайм-аут повторной передачи (maximum retransmission timeout — RTO) для попыток INIT [RFC6458]. С этого момента (до соединения) приложение может также включить инкапсуляцию UDP путем настройки номера удаленного порта для нее [RFC6951].

Associate

Создает ассоциацию (эквивалент SCTP для соединения), связывающую локальный и удаленный экземпляр SCTP. Для идентификации удаленной конечной точки можно ей может быть назначен один или несколько (с помощью connectx) сокетов (параграф 9.9 в [RFC6458]). Большинство примитивов связано с конкретной ассоциацией, которая предполагается созданной первой. Associate может возвращать список транспортных адресов получателя, что позволяет использовать затем множество путей. Один из возвращенных сокетов выбирается локальной конечной точкой как используемый по умолчанию основной путь для передачи партнеру пакетов SCTP, но этот выбор приложение может изменить, используя список адресов получателя. Associate также получает число исходящих потоков для запроса и может возвращать число согласованных исходящих потоков. Может быть представлен необязательный 32-битовый параметр индикации уровня адаптации [RFC5061]. При использовании аутентифицированных блоков (chunk) могут быть представлены типы блоков, которые требуется передавать с проверкой подлинности [RFC4895]. Уведомление SCTP_Cant_Str_Assoc служит для информирования приложения об отказе при создании ассоциации [RFC6458]. Приложение может использовать sendto() или sendmsg() для неявного создания ассоциации, передавая тем самым сообщение, что SCTP может передавать на этапе создания ассоциации [RFC6458]. Отметим, что этот механизм отличается от TCP TFO и сообщение будет приходить лишь один раз по истечении по меньшей мере одного интервала RTT, поскольку передается вместе с третьим сообщением организации ассоциации (блок COOKIE-ECHO).

Send

Примитив передает через ассоциацию сообщение с неким числом байтов. Сообщению может быть назначен номер для последующего обращения к корректному сообщению при возникновении ошибки, а также предоставляется идентификатор для указания потока, используемого внутри ассоциации (этот параметр для простоты считается обязательным, а при его отсутствии просто используется 0). Могут быть заданы условия отказа от сообщения (например, ограничение числа повторных попыток передачи или срока действия пользовательского сообщения). Это позволяет управлять расширением частичных гарантий [RFC3758] [RFC7496]. Необязательный срок действия сообщения позволяет отбросить устаревшее сообщение без его отправки. Можно указать предпочтительный путь (выбор его не гарантирован) путем задания сокета, а также возможность неупорядоченной доставки с помощью флага unordered. Рекомендательный флаг указывает, что партнеру не следует задерживать подтверждение пользовательского сообщения [RFC7053], а другой рекомендательный флаг управляет предпочтениями приложения в части группировки данных пользователя с другими исходящими блоками DATA в один пакет. Идентификатор протокола для данных (payload) может предоставляться для партнеру передачи значения, указывающего тип данных. При использовании аутентификации блоков может предоставляться идентификатор ключа для проверки подлинности блоков DATA [RFC4895].

Receive

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

Shutdown

Примитив аккуратно закрывает ассоциацию, гарантированно доставляя данные, которые уже переданы SCTP. Параметр предоставляет приложению контроль над операциями дальнейшего приема или отправки. Код возврата информирует приложение об выполнении или отказе процедуры.

Abort

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

Change Heartbeat/Request Heartbeat

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

Configure Max. Retransmissions of an Association

Параметр Association.Max.Retrans [RFC4960] (sasoc_maxrxt расширении API сокетов [RFC6458]) позволяет задать число неудачных повторов, после которого принимается решение об отказе ассоциации в целом, кода следует вызывать уведомление Communication Lost.

Set Primary

Позволяет изменить принятый по умолчанию путь для ассоциации предоставлением сокета. Можно также указать используемый по умолчанию адрес для дейтаграмм IP.

Change Local Address/Set Peer Primary

Позволяет конечной точке добавлять и удалять локальные адреса для ассоциации. Кроме того, партнеру можно посоветовать адрес для использования в качестве первичного [RFC5061].

Configure Path Switchover

Этот абстрактный API содержит примитив Set Failure Threshold [RFC4960], задающий параметр Path.Max.Retrans, который определяет число повторов, после которого определенный транспортный адрес считается недоступным. Если в ассоциации еще остаются транспортные адреса, достижение этого предела ведет к переключению пути. Расширение SCTP-PF добавляет в этот метод концепцию «ненадежных путей» (Potentially Failed или PF) [RFC7829]. SCTP не отказывается полностью от передачи по путям со статусом PF, но будет предпочитать другие активные пути, если они доступны. Переход в состояние PF происходит при превышении заданного максимума повторов передачи. Таким образом, для всех путей, гже применяется этот механизм, имеется два настраиваемых порога ошибок, один из которых определяет переход в состояние PF, а другой — принятие решения о недоступности адреса.

Set/Get Authentication Parameters

Позволяет приложению добавлять и удалять ключевой материал для ассоциации. Кроме того, можно запросить типы блоков, которые нужно аутентифицировать [RFC4895].

Add/Reset Streams, Reset Association

Позволяет конечной точке добавлять потоки в имеющуюся ассоциацию или сбрасывать потоки индивидуально. Кроме того, можно сбросить ассоциацию [RFC6525].

Status

Примитив возвращает блок данных с информацией об указанной ассоциации, списком транспортных адресов получателей, состояниях доступности этих транспортных адресов, текущих размерах локального и удаленного окна, текущем размере локального окна перегрузок, числе неподтвержденных блоков DATA; основном пути, последнем сглаженном времени кругового обхода (Smoothed Round-Trip Time или SRTT) на основном пути, RTO на основном пути, SRTT и RTO для других адресов получателя [RFC4960] и MTU на путях [RFC6458].

Enable/Disable Interleaving

Позволяет включить или выключить согласование поддержки чередования пользовательских сообщений для будущих ассоциаций. Для имеющихся ассоциаций можно запросить, было ли согласовано чередование пользовательских сообщений в конкретной ассоциации [RFC8260].

Set Stream Scheduler

Позволяет выбрать планировщик потоков для ассоциации из числа First-Come-First-Served (обслуживание в порядке очередности поступления), Round-Robin (циклический перебор), Round-Robin per Packet (циклический перебор на уровне пакетов), Priority-Based (по приоритету), Fair Bandwidth (беспристрастное распределение пропускной способности, Weighted Fair Queuing (взвешенная беспристрастная очередь) [RFC8260].

Configure Stream Scheduler

Позволяет менять параметры планировщика на уровне потока — приоритет для планировщика Priority-Based и вес для Weighted Fair Queuing.

Enable/Disable NoDelay

Включает и отключает использование любого алгоритма в стиле Nagle для ассоциации [RFC6458].

Configure Send Buffer Size

Управляет объемом данных, которые могут ожидать передачи (включая повтор) во внутренних буферах SCTP [RFC6458].

Configure Receive Buffer Size

Задает размер приемного буфера в октетах, управляя тем самым окном приема для ассоциации [RFC6458].

Configure Message Fragmentation

Если пользовательское сообщение создает пакет SCTP, размер которого превышает допустимый для передачи (задается приложением или определяется Path MTU), протокол SCTP фрагментирует его. Запрет фрагментации в таких случаях будет приводить к ошибке вместо фрагментирования сообщения [RFC6458].

Configure Path MTU Discovery

Механизм Path MTU Discovery (PMTUD) можно включить и выключить для каждого адреса партнера в ассоциации (параграф 8.1.12 в [RFC6458]). При включенном механизме определяется текущее значение Path MTU, при выключенном значение Path MTU контролируется приложением.

Configure Delayed SACK Timer

Время задержки отправки SACK может быть настроено, вплоть до запрета задержки. Можно задать также число пакетов, которые должны быть получены перед отправкой SACK без ожидания таймера задержки [RFC6458].

Set Cookie Life Value

Значение Cookie life может быть настроено (параграф 8.1.2 в [RFC6458]). Valid.Cookie.Life является одним из параметров, которые могут настраиваться с помощью SetProtocolParameters [RFC4960].

Set Maximum Burst

Максимальный пик пакетов, которые могут быть отправлены конкретной ассоциацией (по умолчанию 4 и большие значения поддерживать не требуется), см. параграф 8.1.2 в [RFC6458]). Max.Burst является одним из параметров, которые могут настраиваться с помощью SetProtocolParameters [RFC4960].

Configure RTO Calculation

Абстрактный API содержащий настраиваемые параметры RTO.Initial, RTO.Min, RTO.Max, RTO.Alpha. RTO.Beta. Лишь начальное, минимальное и максимальное значение RTO указаны как настраиваемые в расширении API сокетов SCTP [RFC6458].

Set DSCP Value

Значение DSCP можно установить для адреса партнера в ассоциации (параграф 8.1.12 в [RFC6458]).

Set IPv6 Flow Label

Метку потока можно установить для адреса партнера в ассоциации (параграф 8.1.12 в [RFC6458]).

Set Partial Delivery Point

Позволяет задать размер сообщения, при котором будет использоваться частичная доставка. Снижение порога приводит к более частому применению частичной доставки [RFC6458].

Уведомление Communication Up

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

Уведомление Restart

Передается на вышележащий уровень, когда конечная точка SCTP детектирует перезапуск партнера [RFC6458].

Data Arrive

Информирует приложение о готовности к приему сообщения с помощью примитива Receive.

Уведомление Send Failure Notification/Receive Unsent Message/Receive Unacknowledged Message

При невозможности доставить сообщение через ассоциацию отправителя можно информировать об этом и он узнает, было ли сообщение просто не подтверждено или не было доставлено (например, по истечении срока жизни). Это может также информировать отправителя о доставке части сообщения.

Уведомление Network Status Change

Информирует приложение о смене состояния активности сокета [RFC4960] или состоянии PF [RFC7829].

Уведомление Communication Lost

Когда SCTP теряет связь с конечной точкой (определяется по heartbeat или числу повторов передачи) или узнает о прерывании, это уведомление информирует прикладной процесс о затронутой ассоциации, типе события (отказ или прерывание в ответ на запрос shutdown или abort).

Уведомление Shutdown Complete

Когда SCTP завершает процедуры shutdown, это уведомление передается на вышележащий уровень для информирования о затронутой ассоциации.

Уведомление Authentication

Передается на вышележащий уровень, когда SCTP хочет уведомить его об управлении ключами, связанном с аутентифицируемыми блоками [RFC4895].

Уведомление Adaptation Layer Indication

Передается на вышележащий уровень, когда SCTP завершает создание ассоциации и партнер узнает уровень адаптации [RFC5061] [RFC6458].

Уведомление Stream Reset

Информирует вышележащий уровень о результате, когда SCTP завершает процедуры сброса потоков [RFC6525].

Уведомление Association Reset

Информирует вышележащий уровень о результате, когда SCTP завершает процедуру сброса [RFC6525],.

Уведомление Stream Change

Информирует вышележащий уровень о результате, когда SCTP завершает процедуру, используемую для увеличения числа потоков [RFC6525].

Уведомление Sender Dry

Передается на вышележащий уровень, когда SCTP больше нет пользовательских данных для передачи или повтора [RFC6458].

Уведомление Partial Delivery Aborted

Передается на вышележащий уровень, когда получатель начал принимать части пользовательского сообщения, но доставка сообщения затем была прервана (параграф 6.1.7 of [RFC6458]).

3.3.1. Исключенные примитивы и параметры

Примитив Receive может возвращать дополнительную информацию, но она не обязательна для реализации и не рассматривается здесь. С уведомлением Communication Lost также может предоставляться дополнительная информация (например, идентификация для извлечения не отправленных или не подтвержденных данных). SCTP «может вызывать» уведомление Communication Error и «передавать» уведомление Restart, которые не обязательны для реализации. Список, для примитива Status включает «и т. п.», что говорит о дополнительной информации. Примитив Get SRTT Report возвращает информацию, предоставляемую примитивом Status, поэтому здесь не рассматривается. Функция API Destroy SCTP Instance была исключена — она удаляет экземпляр SCTP, созданный Initialize, но не является примитивом в смысле данного документа, поскольку не относится к транспортным свойствам. Событие Shutdown информирует приложение о передаче партнером SHUTDOWN, поэтому данные больше не следует передавать в сокет (параграф 6.1 в [RFC6458]). Однако при попытке приложения передать данные в сокет оно будет получать сообщение об ошибке, поэтому данное событие классифицировано лишь как воздействие на стиль программирования приложений и не включено в этот документ.

3.4. Примитивы, предоставляемые UDP и UDP-Lite

Набор примитивов этапа 1 для UDP и UDP-Lite приведен в [RFC8304].

3.5. Служба LEDBAT

Описание сервиса механизма контроля перегрузок LEDBAT приведено ниже.

LEDBAT предназначен для использования фоновыми процессами с передачей больших объемов данных и быть не более активным, нежели контроль перегрузок в стандартном TCP (RFC 5681), уступая в присутствии конкурирующих потоков, что ограничивает негативное влияние одновременных потоков на производительность сети [RFC6817].

LEDBAT не имеет своих примитивов и не является транспортным протоколом. В соответствии с [RFC6817]:

LEDBAT может применяться как часть транспортного протокола или приложения, пока механизмы передачи данных способны доставлять временные метки и подтверждения достаточно часто. LEDBAT можно применять с TCP, SCTP и DCCP9) с соответствующими расширениями, а также с фирменными протоколами, такими как работающие на основе одноранговых (P2P10) приложений UDP.

На момент написания этого документа расширений для TCP, SCTP или DCCP еще не было.

В спецификации LEDBAT имеется много настраиваемых параметров. Параметр TARGET, задающий целевое значение задержки, при которой LEDBAT пытается работать, должен иметь значение не более 100 мсек. Параметр allowed_increase (следует устанавливать 1 и должно быть больше 0) ограничивает скорость, с которой LEDBAT повышает свою скорость работы. Параметр gain, который в соответствии с [RFC6817] «должен быть не больше 1» для предотвращения более быстрого роста скорости, чем TCP Reno, определяет, как быстро отправитель реагирует на изменение задержки в очередях. Реализации могут делить gain на два параметра, один для роста, другой (возможно, больший) для снижения. Здесь эти параметры названы Gain_Inc и Gain_Dec. Base_History указывает размер списка измеренных базовых задержек и в соответствии с [RFC6817] для него «следует устанавливать значение 10». Этот список может фильтроваться с помощью функции Filter, которая не задана в [RFC6817], но дает список размером Current_Filter. Для начального и минимального размера окна (Init_CWND и Min_CWND) следует задавать значение 2.

Что касается контролируемых приложением параметров, как крайний вариант они могут не раскрываться совсем, а в качестве другой крайности, все, что не указано в спецификации как требуемое, может считаться параметром. Реализации функций не предоставляются в качестве параметров для какого-либо из рассматриваемых здесь транспортных протоколов, поэтому функция Filter не считается параметром. Однако для предотвращения ненужных ограничений в будущих реализациях все остальные параметры, упомянутые выше, считаются настраиваемыми параметрами, которые следует раскрывать.

4. Этап 2

Здесь классифицируются примитивы, выбранные на этапе 1 в зависимости от того, относятся они к организации соединения или передаче данных. Примитивы представлены в соответствии с номенклатурой «Категория.[Субкатегория].Свойство.Протокол» (CATEGORY.[SUBCATEGORY].FEATURENAME.PROTOCOL). Категориями могут быть соединения (CONNECTION) и данные (DATA). В категории соединений могут рассматриваться подкатегории организации (ESTABLISHMENT), доступности (AVAILABILITY), поддержки (MAINTENANCE) и прерывания (TERMINATION). В категории данных подкатегорий не задано. Имя протокола UDP(-Lite) используется в примитивах, которые эквивалентны для UDP и UDP-Lite, TCP — в примитивах, эквивалентных для TCP и MPTCP. Соединения представлены как базовая концепция, независимая от протокола и служащая для обозначения, например, соединений TCP (указываются уникальными парами адресов IP и портов TCP), ассоциаций SCTP (указываются множеством пар адресов IP и номеров портов), а также соединения UDP и UDP-Lite (указываются уникальной парой сокетов).

Для общности некоторые мелкие детали не рассматриваются, например, Close в SCTP [RFC4960] возвращает успех или отказ и позволяет приложению контролировать разрешение или запрет последующих операций приема и передачи [RFC6458]. Это не описывается так же для TCP [RFC0793], но эти детали не играют важной роли для примитивов, предоставляемых TCP или SCTP (для общности можно предположить запрет операций приема и передачи в обоих случаях).

Примитивы TCP Send и Receive включают использование параметра urgent, который контролирует механизм, требуемый для реализации «сигналов синхронизации», применяемых в telnet [RFC0854], но в [RFC6093] сказано: «новым приложениям не следует поддерживать механизм TCP». Поскольку этап 2 создает основу для будущих систем, механизм urgent здесь исключен. Это отноится и к уведомлению Urgent Pointer Advance в Error_Report (параграф 4.2.4.1 в [RFC1122]).

Поскольку LEDBAT является механизмом контроля насыщения, а не протоколом, в настоящее время не задано, когда следует включать и отключать или настраивать этот механизм. Например, это может быть однократным выбором при организации соединения или прослушивании входящих вызовов и в этих случаях его следует отнести к категории CONNECTION.ESTABLISHMENT или CONNECTION.AVAILABILITY, соответственно. Для предотвращения ненужных ограничений в будущих реализациях принято решение поместить его в категорию CONNECTION.MAINTENANCE с параметрами, описанными в [RFC6817], сделав все параметры настраиваемыми.

4.1. Примитивы, связанные с соединением

Организация

Активная организация соединения одной транспортной конечной точки с одной или множеством транспортных конечных точек. Интерфейсы UDP и UDP-Lite позволяют использовать API на основе соединений и без них [RFC8085].

CONNECT.TCP

Событие или примитив этапа 1: Open (активно) или Open (пассивно) с сокетом, затем Send.

Параметры: 1 локальный адрес IP (необязательно), 1 транспортный адрес получателя (для активного open, иначе сокет и локальный адрес IP последующего входящего запроса на соединение), тайм-аут (необязательно), опции (необязательно), конфигурация MKT (необязательно), пользовательское сообщение (необязательно).

Комментарии: Если локальный адрес IP не указан, автоматически выбирается принятый по умолчанию. Тайм-аут может быть задан числом попыток повтора. Опциями являются опции IP для использования во всех сегментах соединения. Для соединения TCP требуется представлять хотя бы одну опцию Source Route. Конфигурация MKT относится к возможности настроить кортежи MKT для проверки подлинности. Пользовательское сообщение может передаваться партнерскому приложению сразу после приема пакета TCP SYN. Для снижения задержки с помощью экспериментального механизма TFO размер первого сообщения должен быть не больше максимального сегмента TCP (за вычетом опций TCP, использованных в SYN). Сообщение (первое) может быть доставлено приложению на удаленном хосте в нескольких экземплярах.

CONNECT.SCTP

Событие или примитив этапа 1: Initialize, затем Enable/Disable Interleaving (необязательно) и Associate.

Параметры: список локальных пар «порт SCTP — адрес IP» (Initialize), один или несколько сокетов (идентификация партнера), число выходных потоков, максимальное число входных потоков, уровень адаптации (необязательно), аутентифицируемые типы блоков (необязательно), запрос чередования (on/off)Ю максимальное число попыток INIT (необязательно), максимальное начальное значение RTO для INIT (необязательно), пользовательское сообщение (необязательно), номер удаленного порта UDP (необязательно).

Возврат: список сокетов или отказ.

Комментарии: Initialize нужно вызывать лишь один раз для списка локальных пар «порт SCTP — адрес IP». Автоматически выбирается один сокет, который можно потом изменить с помощью MAINTENANCE. Пользовательское сообщение может быть передано партнерскому приложению сразу после приема пакета с блоком COOKIE-ECHO. Для сокращения задержки размер первого сообщения должен ограничиваться так, чтобы оно помещалось в блок COOKIE-ECHO. Если указан удаленный порт UDP, пакеты SCTP инкапсулируются в UDP.

CONNECT.MPTCP

Похож на CONNECT.TCP, но включает 1 дополнительных логический параметр, позволяющий включать или отключать MPTCP для отдельного соединения или сокета (по умолчанию включено).

CONNECT.UDP(-Lite)

Событие или примитив этапа 1: Connect, затем Send.

Параметры: 1 локальный адрес IP (по умолчанию ANY или указанный адрес), 1 транспортный адрес получателя, 1 локальный порт (по умолчанию выбранный ОС или указанный), 1 порт получателя (по умолчанию выбранный ОС или указанный).

Комментарии: Связывает транспортный адрес, создавая соединение сокета UDP(-Lite). Может вызываться неоднократно с разными транспортными адресами для организации новых соединений. Функция CONNECT позволяет приложению получать сведения об ошибках при передаче сообщений по транспортному адресу.

Доступность

Подготовка к приему входящих запросов на соединения.

LISTEN.TCP

Событие или примитив этапа 1: Open (пассивно).

Параметры: 1 локальный адрес IP (необязательно), 1 сокет (необязательно), тайм-аут (необязательно), буфер для приема пользовательского сообщения (необязательно), конфигурация MKT (необязательно).

Комментарии: Если представлен сокет и/или локальный адрес IP, входящие соединения ожидаются лишь из указанного адреса и/или сокета, в ином случае принимаются все вызовы. Позднее можно выполнить организацию соединения (ESTABLISHMENT) с примитивом Send. Если представлен приемный буфер для пользовательского сообщения, это сообщение может быть принято от отправителя с поддержкой TFO до завершения согласования TCP. Такое сообщение может быть получено несколько раз. Конфигурация MKT указывает возможность настройки MKT для аутентификации.

LISTEN.SCTP

Событие или примитив этапа 1: Initialize, затем уведомление Communication Up или Restart и, возможно, уведомление Adaptation Layer.

Параметры: Список локальных пар «порт SCTP — адрес IP» (инициализация).

Возврат: Список сокетов, число выходных потоков, число входных потоков, уровень адаптации, типы аутентифицируемых блоков, поддержка чередования на обеих сторонах (yes/no).

Комментарии: Initialize нужно вызывать лишь один раз для списка локальных пар «порт SCTP — адрес IP». За уведомлением Communication Lost может следовать Communication Up, указывая восстановление потерянной связи. Если партнер указал уровень адаптации, выдается уведомление Adaptation Layer.

LISTEN.MPTCP

Похож на LISTEN.TCP, но включает дополнительный логический параметр, включающий или отключающий MPTCP для определенного соединения или сокета (по умолчанию включено).

LISTEN.UDP(-Lite)

Событие или примитив этапа 1: Receive.

Параметры: 1 локальный адрес IP (по умолчанию ANY или заданный), 1 транспортный адрес получателя, локальный порт (по умолчанию выбранный ОС или заданный), порт назначения (по умолчанию выбранный ОС или заданный).

Комментарии: Функция Receive регистрирует приложение, слушающее входящие дейтаграммы UDP(-Lite) на конечной точке.

Поддержка

Выполняет настройку имеющегося соединения или уведомлений о нем. Существуют передаваемые по отдельному каналу (out-of-band) сообщения для протокола, которые могут передаваться в любой момент, по крайней мере после организации соединения и до его разрыва (за исключением CHANGE_TIMEOUT.TCP, которое может передаваться лишь для открытых соединений, когда вызывается DATA.SEND.TCP). В некоторых случаях эти примитивы могут напрямую вызываться при организации (ESTABLISHMENT) или контроле доступности (AVAILABILITY) соединения без ожидания его организации (например, CHANGE_TIMEOUT.TCP можно выполнить с использованием примитива TCP Open). Для UDP и UDP-Lite эти функции могут задавать настройки на уровне соединения или отдельного сообщения.

CHANGE_TIMEOUT.TCP

Событие или примитив этапа 1: Open или Send с незаданными переменными контроля состояния соединения.

Параметры: Тайм-аут, adv_uto (необязательно); uto_enabled (необязательно, по умолчанию false), изменяемость (необязательно, по умолчанию true).

Комментарии: При передаче данных приложение может настроить тайм-аут для соединения (время, по истечении которого соединение разрывается, если данные не удается доставить). Если uto_enabled = true, значение тайм-аута (или adv_uto при наличии) будет анонсироваться TCP на другой стороне соединения для адаптации там значения пользовательского тайм-аута. Значение uto_enabled контролирует опцию UTO для соединения в обоих направлениях. Параметр changeable управляет возможностью изменять тайм-аут на основе опции UTO, принятой с другой стороны соединения, он получает значение becomes при использовании значения timeout.

CHANGE_TIMEOUT.SCTP

Событие или примитив этапа 1: Change Heartbeat вместе с Configure Max. Retransmissions of an Association.

Параметры: Change Heartbeat для частоты heartbeat и Configure Max. Retransmissions of an Association для Association.Max.Retrans.

Комментарии: Change Heartbeat может включать или выключать сообщения heartbeat в SCTP, а также менять их частоту. Параметр Association.Max.Retrans определяет число неудачных передач любых пакетов (включая heartbeat), после которого ассоциация разрывается. Таким образом, эти примитивы и параметры вместе могут задавать поведение ассоциации SCTP как CHANGE_TIMEOUT.TCP для соединений TCP.

DISABLE_NAGLE.TCP

Событие или примитив этапа 1: Не задано.

Параметры: 1 логическое значение.

Комментарии: Алгоритм Nagle задерживает передачу данных для повышения вероятности отправки полноразмерного сегмента. Приложение должно иметь возможность отключать этот алгоритм для соединения.

DISABLE_NAGLE.SCTP

Событие или примитив этапа 1: Enable/Disable NoDelay.

Параметры: 1 логическое значение.

Комментарии: Алгоритмы класса Nagle задерживают передачу данных для повышения вероятности отправки полноразмерного сегмента. Приложение должно иметь возможность отключать этот алгоритм для соединения.

REQUEST_HEARTBEAT.SCTP

Событие или примитив этапа 1: Request Heartbeat.

Параметры: Сокет.

Возврат: успех или отказ.

Комментарии: Запрашивает незамедлительное сообщение heartbeat на пути, возвращая отказ или успех.

ADD_PATH.MPTCP

Событие или примитив этапа 1: Не задано.

Параметры: Локальный адрес IP, локальный номер порта (необязательно).

Комментарии: Приложение указывает локальный адрес IP и номер порта, которые должны использоваться для субпотока.

ADD_PATH.SCTP

Событие или примитив этапа 1: Change Local Address/Set Peer Primary.

Параметры: Локальный адрес IP.

REM_PATH.MPTCP

Событие или примитив этапа 1: Не задано.

Параметры: Локальный адрес IP, локальный номер порта, удаленный адрес IP, удаленный номер порта.

Комментарии: Приложение удаляет субпоток, указанный парой «адрес IP — порт». Реализация MPTCP должна инициировать удаление субпотока, относящегося к этой паре.

REM_PATH.SCTP

Событие или примитив этапа 1: Change Local Address/Set Peer Primary.

Параметры: Локальный адрес IP.

SET_PRIMARY.SCTP

Событие или примитив этапа 1: Set Primary.

Параметры: Сокет.

Возврат: Результат предпринятой попытки.

Комментарии: Обновляет текущий первичный адрес на основе доступных в ассоциации сокетов.

SET_PEER_PRIMARY.SCTP

Событие или примитив этапа 1: Change Local Address/Set Peer Primary.

Параметры: Локальный адрес IP.

Комментарии: Это лишь рекомендация для партнера.

CONFIG_SWITCHOVER.SCTP

Событие или примитив этапа 1: Configure Path Switchover.

Параметры: Основное максимальное число повторов (после которого путь считается неактивным) и максимальное число повторов для PF (после которого путь считается ненадежным и применяются другие пути) (необязательно).

STATUS.SCTP

Событие или примитив этапа 1: Уведомления Status, Enable/Disable Interleaving и Network Status Change.

Возврат: Блок данных о конкретной ассоциации, содержащий состояние соединения, список транспортных адресов получателей и состояния их доступности, текущие размеры окна (локального и принимающего партнера), текущие размеры локального окна насыщения, число неподтвержденных блоков DATA, число блоков DATA, ожидающих приема, первичный путь, последнее значение SRTT на первичном пути, RTO на первичном пути, SRTT и RTO на других адресах получателей, MTU на каждом пути и поддержка чередования (yes/no).

Комментарии: Уведомление Network Status Change информирует приложение а смене состояния активности сокета. Оно влияет лишь на стиль программирования, поскольку эти же данные доступны через примитив Status.

STATUS.MPTCP

Событие или примитив этапа 1: Не задано.

Возврат: Список пар «адрес IP — порт TCP» каждого субпотока. Первая пара содержит локальный адрес IP и порт, вторая — удаленные.

SET_DSCP.TCP

Событие или примитив этапа 1: Не задано.

Параметры: Значение DSCP.

Комментарии: Примитив позволяет приложению изменить значение DSCP для исходящих сегментов.

SET_DSCP.SCTP

Событие или примитив этапа 1: Установка DSCP.

Параметры: Значение DSCP.

Комментарии: Примитив позволяет приложению изменить значение DSCP для исходящих пакетов на пути.

SET_DSCP.UDP(-Lite)

Событие или примитив этапа 1: Set_DSCP.

Параметры: Значение DSCP.

Комментарии: Примитив позволяет приложению изменить значение DSCP для исходящих дейтаграмм UDP(-Lite). Рекомендации по использованию поля приведены в [RFC7657] и [RFC8085].

ERROR.TCP

Событие или примитив этапа 1: Error_Report.

Возврат: Причина (кодирование не занято) и субпричина (кодирование не занято).

Комментарии: Мягкие ошибки многие приложения могут игнорировать без ущерба и следует обеспечивать возможность такого игнорирования. Ошибки включают сообщения ICMP об ошибках и избыточные повторы.

ERROR.UDP(-Lite)

Событие или примитив этапа 1: Error_Report.

Возврат: Сообщение об ошибке.

Комментарии: Примитив возвращает мягкие ошибки, которые приложения могут игнорировать без ущерба и следует обеспечивать возможность такого игнорирования.

SET_AUTH.TCP

Событие или примитив этапа 1: Не задано.

Параметры: current_key и rnext_key.

Комментарии: Ключи current_key и rnext_key указывают предпочтительный исходящий и входящий MKT, соответственно, для сегмента, передаваемого в соединение.

SET_AUTH.SCTP

Событие или примитив этапа 1: Set/Get Authentication Parameters.

Параметры: key_id, key, hmac_id.

GET_AUTH.TCP

Событие или примитив этапа 1: Не задано.

Параметры: current_key и rnext_key

Комментарии: current_key и rnext_key указывают предпочтительный исходящий и входящий MKT, соответственно, переданные в недавно принятом сегменте.

GET_AUTH.SCTP

Событие или примитив этапа 1: Set/Get Authentication Parameters.

Параметры: key_id и chunk_list.

RESET_STREAM.SCTP

Событие или примитив этапа 1: Add/Reset Streams, Reset Association.

Параметры: sid и направление.

RESET_STREAM-EVENT.SCTP

Событие или примитив этапа 1: Уведомление Stream Reset.

Параметры: Информация о результате RESET_STREAM.SCTP.

Комментарии: Вызывается при завершении процедуры сброса потоков.

RESET_ASSOC.SCTP

Событие или примитив этапа 1: Add/Reset Streams, Reset Association.

Параметры: Информация, относящаяся к расширению, как определено в [RFC3260].

RESET_ASSOC-EVENT.SCTP

Событие или примитив этапа 1: Уведомление Association Reset.

Параметры: Информация о результате RESET_ASSOC.SCTP.

Комментарии: Вызывается при завершении процедуры сброса ассоциации.

ADD_STREAM.SCTP

Событие или примитив этапа 1: Add/Reset Streams, Reset Association.

Параметры: Число добавленных исходящих и входящих потоков.

ADD_STREAM-EVENT.SCTP

Событие или примитив этапа 1: Уведомление Stream Change.

Параметры: Информация о результате ADD_STREAM.SCTP.

Комментарии: Вызывается при завершении процедуры добавления потока.

SET_STREAM_SCHEDULER.SCTP

Событие или примитив этапа 1: Set Stream Scheduler.

Параметры: Идентификатор планировщика.

Комментарии: Выбор из планировщиков First-Come-First-Served, Round-Robin, Round-Robin по пакетам, Priority-Based, Fair Bandwidth, Weighted Fair Queuing.

CONFIGURE_STREAM_SCHEDULER.SCTP

Событие или примитив этапа 1: Configure Stream Scheduler.

Параметры: Приоритет.

Комментарии: Значение приоритета применимо лишь при выборе планировщика Priority-Based или Weighted Fair Queuing с помощью SET_STREAM_SCHEDULER.SCTP. Смысл параметра разный для этих планировщиков, но в обоих случаях он реализует ту или иную форму приоритизации в части распределения пропускной способности между потоками.

SET_FLOWLABEL.SCTP

Событие или примитив этапа 1: Set IPv6 Flow Label

Параметры: Метка потока.

Комментарии: Примитив позволяет приложению изменять метку потока в заголовке IPv6 для исходящих пакетов на пути.

AUTHENTICATION_NOTIFICATION-EVENT.SCTP

Событие или примитив этапа 1: Уведомление Authentication.

Возврат: Информация, относящаяся к управлению ключами.

CONFIG_SEND_BUFFER.SCTP

Событие или примитив этапа 1: Configure Send Buffer Size.

Параметры: Размер в октетах.

CONFIG_RECEIVE_BUFFER.SCTP

Событие или примитив этапа 1: Configure Receive Buffer Size.

Параметры: Размер в октетах.

Комментарии: Примитив управляет размером окна у получателя.

CONFIG_FRAGMENTATION.SCTP

Событие или примитив этапа 1: Configure Message Fragmentation.

Параметры: Логическое значение (enable/disable) и максимальный размер (необязательно, по умолчанию PMTU).

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

CONFIG_PMTUD.SCTP

Событие или примитив этапа 1: Configure Path MTU Discovery.

Параметры: Одно логическое значение (PMTUD вкл./выкл.) и размер PMTU (необязательно).

Возврат: Значение PMTU.

Комментарии: Примитив возвращает осмысленное значение PMTU, когда PMTUD включено (логический параметр true) и позволяет установить PMTU, если PMTUD отключено (логический параметр false).

CONFIG_DELAYED_SACK.SCTP

Событие или примитив этапа 1: Configure Delayed SACK Timer.

Параметры: Одно логическое значение (задержка SACK вкл./выкл.), значение таймера (необязательно) и число пакетов для ожидания (по умолчанию 2).

Комментарии: Если задержка SACK разрешена, SCTP будет слоть SACK при получении заданного числа пакетов или по таймеру (что наступит раньше).

CONFIG_RTO.SCTP

Событие или примитив этапа 1: Configure RTO Calculation.

Параметры: Начальное (необязательно), минимальное (необязательно), максимальное (необязательно) значение RTO.

Комментарии: Примитив настраивает начальное, минимальное и максимальное значение RTO.

SET_COOKIE_LIFE.SCTP

Событие или примитив этапа 1: Set Cookie Life Value

Параметры: Значение cookie life.

SET_MAX_BURST.SCTP

Событие или примитив этапа 1: Set Maximum Burst.

Параметры: Максимальный размер пика.

Комментарии: Не все реализации поддерживают значения больше 4.

SET_PARTIAL_DELIVERY_POINT.SCTP

Событие или примитив этапа 1: Set Partial Delivery Point

Параметры: Граница частичной доставки (integer).

Комментарии: Этот параметр должен быть не больше размера приемного буфера сокета.

SET_CHECKSUM_ENABLED.UDP

Событие или примитив этапа 1: Checksum_Enabled.

Параметры: 0 когда отправитель задает нулевую контрольную сумму, 1 при расчете контрольной суммы отправителем (принято по умолчанию).

SET_CHECKSUM_REQUIRED.UDP

Событие или примитив этапа 1: Require_Checksum.

Параметры 0 разрешает нулевую контрольную сумму, 1 требует на приемной стороне контрольную сумму, отличную от 0 (принято по умолчанию).

SET_CHECKSUM_COVERAGE.UDP-Lite

Событие или примитив этапа 1: Set_Checksum_Coverage.

Параметры: Размер покрытия контрольной суммой у отправителя (по умолчанию максимальное покрытие).

SET_MIN_CHECKSUM_COVERAGE.UDP-Lite

Событие или примитив этапа 1: Set_Min_Coverage.

Параметры: Размер покрытия контрольной суммой у получателя (по умолчанию минимальное покрытие).

SET_DF.UDP(-Lite)

Событие или примитив этапа 1: Set_DF.

Параметры: 0, если флаг DF не установлен (принято по умолчанию) в заголовке IPv4, 1 при установленном флаге.

GET_MMS_S.UDP(-Lite)

Событие или примитив этапа 1: Get_MM_S.

Комментарии: Примитив позволяет определить максимальный размер транспортного сообщения, которое может быть передано без фрагментации IP с настроенного интерфейса.

GET_MMS_R.UDP(-Lite)

Событие или примитив этапа 1: Get_MMS_R.

Комментарии: Примитив позволяет определить максимальный размер транспортного сообщения, которое можно принять через настроенный интерфейс.

SET_TTL.UDP(-Lite) (IPV6_UNICAST_HOPS)

Событие или примитив этапа 1: Set_TTL и Set_IPV6_Unicast_Hops.

Параметры: Значение IPv4 TTL или IPv6 Hop Count.

Комментарии: Примитив позволяет приложению изменить значение IPv4 TTL или IPv6 Hop Count для исходящих дейтаграмм UDP(-Lite).

GET_TTL.UDP(-Lite) (IPV6_UNICAST_HOPS)

Событие или примитив этапа 1: Get_TTL и Get_IPV6_Unicast_Hops.

Возврат: Значение IPv4 TTL или IPv6 Hop Count.

Комментарии: Примитив позволяет приложению узнать значение IPv4 TTL или IPv6 Hop Count из входящей дейтаграммы UDP(-Lite).

SET_ECN.UDP(-Lite)

Событие или примитив этапа 1: Set_ECN.

Параметры: Значение ECN.

Комментарии: Примитив позволяет приложению UDP(-Lite) установить код явного контроля перегрузок (ECN) в исходящих дейтаграммах UDP(-Lite). По умолчанию передается 00.

GET_ECN.UDP(-Lite)

Событие или примитив этапа 1: Get_ECN.

Параметры: Значение ECN.

Комментарии: Примитив позволяет приложению UDP(-Lite) прочитать код явного контроля перегрузок (ECN) из входящей дейтаграммы UDP(-Lite).

SET_IP_OPTIONS.UDP(-Lite)

Событие или примитив этапа 1: Set_IP_Options.

Параметры: Опции.

Комментарии: Примитив позволяет приложению UDP(-Lite) установить опции IP для исходящих дейтаграмм UDP(-Lite). Опции могут включать по меньшей мере Source Route, Record Route, Timestamp.

GET_IP_OPTIONS.UDP(-Lite)

Событие или примитив этапа 1: Get_IP_Options.

Возврат: Опции.

Комментарии: Примитив позволяет приложению UDP(-Lite) прочитать любые опции IP из входящей дейтаграммы UDP(-Lite).

CONFIGURE.LEDBAT

Событие или примитив этапа 1: Не задано.

Параметры: enable (логическое значение), target, allowed_increase, gain_inc, gain_dec, base_history, current_filter, init_cwnd, min_cwnd.

Комментарии: Новый параметр enable включает или отключает сервис LEDBAT.

Прерывание

Gracefully or forcefully closing a connection or being informed about this event happening.

CLOSE.TCP

Событие или примитив этапа 1: Close.

Комментарии: Примитив закрывает соединение на передающей стороне после гарантированной доставки оставшихся данных.

CLOSE.SCTP

Событие или примитив этапа 1: Shutdown.

Комментарии: Прерывает соединение после гарантированной доставки оставшихся данных.

ABORT.TCP

Событие или примитив этапа 1: Abort.

Комментарии: Прерывает соединение без доставки оставшихся данных и передает удаленной стороне сообщение об ошибке.

ABORT.SCTP

Событие или примитив этапа 1: Abort.

Параметры: Причина разрыва для партнера (необязательно).

Комментарии: Прерывает соединение без доставки оставшихся данных и передает другой стороне сообщение об ошибке.

ABORT.UDP(-Lite)

Событие или примитив этапа 1: Close.

Комментарии: Прерывает соединение без доставки оставшихся данных. Через этот экземпляр соединения дейтаграммы UDP(-Lite) больше не передаются и не принимаются.

TIMEOUT.TCP

Событие или примитив этапа 1: Событие User Timeout.

Комментарии: Приложение уведомляется о разрыве соединения. Событие вызывается по таймеру, установленному CONNECTION.ESTABLISHMENT.CONNECT.TCP (возможно изменено с помощью CONNECTION.MAINTENANCE.CHANGE_TIMEOUT.TCP).

TIMEOUT.SCTP

Событие или примитив этапа 1: Событие Communication Lost.

Комментарии: Приложение уведомляется о разрыве соединения. Событие вызывается по тайм-ауту, который следует включать по умолчанию (см. начало параграфа 8.3 в [RFC4960]). Тайм-аут может быть изменен с помощью CONNECTION.MAINTENANCE.CHANGE_TIMEOOUT.SCTP.

ABORT-EVENT.TCP

Событие или примитив этапа 1: Не задано.

ABORT-EVENT.SCTP

Событие или примитив этапа 1: Событие Communication Lost.

Возврат: Причина разрыва от партнера (при ее доступности).

Комментарии: Сообщает приложению о разрыве соединения партнером с помощью CONNECTION.TERMINATION.ABORT.SCTP.

CLOSE-EVENT.TCP

Событие или примитив этапа 1: Не задано.

CLOSE-EVENT.SCTP

Событие или примитив этапа 1: Событие Shutdown Complete.

Комментарии: Приложение информируется об успешном вызове CONNECTION.TERMINATION.CLOSE.SCTP.

4.2. Примитивы, связанные с передачей данных

Все примитивы в этом разделе относятся к существующему соединению, т. е. соединению, которое было организовано или стало доступным для получения данных (хотя это не обязательно для примитивов UDP(-Lite)). В дополнение к указанным параметра все примитивы передачи включают ссылку на блок данных, а примитивы приема — ссылку на буфер для размещения принимаемых данных. Отметим, что примитивы CONNECT.TCP и LISTEN.TCP в категории организации (ESTABLISHMENT) и доступности (AVAILABILITY) соединения также позволяют передавать данные (необязательное пользовательское сообщение) до завершения процесса организации соединения.

SEND.TCP

Событие или примитив этапа 1: Send.

Параметры: timeout (необязательно), current_key (необязательно), rnext_key (необязательно).

Комментарии: Примитив отдает TCP блок данных для гарантированной передачи TCP на другой стороне соединения. Этот вызов позволяет указать тайм-аут(см. CONNECTION.MAINTENANCE.CHANGE_TIMEOUT.TCP). Параметры current_key и rnext_key связаны с проверкой подлинности и могут мыть настроены этим вызовом (см. CONNECTION.MAINTENANCE.SET_AUTH.TCP).

SEND.SCTP

Событие или примитив этапа 1: Send.

Параметры: Номер потока, контекст (необязательно), сокет (необязательно), флаг неупорядоченности (необязательно), флаг управления группировкой (необязательно), идентификатор протокола в данных (необязательно), pr-policy (необязательно), pr-value (необязательно), флаг незамедлительного подтверждения (необязательно), key-id (необязательно).

Комментарии: Примитив отдает SCTP блок данных для передачи SCTP на другой стороне соединения (ассоциации SCTP). Параметр stream number указывает используемый поток, context может позднее применяться для ссылки на корректное сообщение при уведомлении об ошибке, socket может служить для указания предпочтительного состояния пути при наличии нескольких путей (см. CONNECTION.MAINTENANCE.SETPRIMARY.SCTP). Блок данных может доставляться без сохранения порядка, если установлен флаг unordered. Флаг no-bundle устанавливается для указания предпочтительности отказа от группировки сообщений. Идентификатор протокола в поле данных указывает принимающему приложению способ обработки данных. С помощью параметров pr-policy и pr-value можно управлять уровнем гарантий, флаг sack-immediately указывает партнеру, что следует передать соответствующее подтверждение SACK без задержки. Параметр key-id применяется для аутентификации пользовательского сообщения.

SEND.UDP(-Lite)

Событие или примитив этапа 1: Send.

Параметры: IP-адрес и номер порта у получателя (необязательно при соединении).

Комментарии: Примитив предоставляет сообщение для гарантированной доставки с использованием UDP(-Lite) по указанному транспортному адресу. Адрес IP и номер порта можно не указывать для соединенных сокетов UDP(-Lite). При отправке сообщения применяются все примитивы CONNECTION.MAINTENANCE.SET_*.UDP(-Lite).

RECEIVE.TCP

Событие или примитив этапа 1: Receive.

Параметры: current_key (необязательно) и rnext_key (необязательно).

Комментарии: Параметры аутентификации current_key и rnext_key могут быть прочитаны этим вызовом (см. CONNECTION.MAINTENANCE.GET_AUTH.TCP).

RECEIVE.SCTP

Событие или примитив этапа 1: Уведомление Data Arrive, затем Receive.

Параметры: Номер потока (необязательно).

Возврат: Порядковый номер потока (необязательно) и флаг частичной передачи (необязательно).

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

RECEIVE.UDP(-Lite)

Событие или примитив этапа 1: Receive.

Параметры: Буфер для принимаемой дейтаграммы.

Комментарии: К сообщению применяются все примитивы CONNECTION.MAINTENANCE.GET_*.UDP(-Lite).

SENDFAILURE-EVENT.SCTP

Событие или примитив этапа 1: Уведомление Send Failure, за которым может слодовать сообщение Receive Unsent или Receive Unacknowledged.

Возврат: Код причины и не переданное (не подтвержденное) сообщение (необязательно).

Комментарии: Код причины указывает причину отказа, а context — номер контекста, если он предоставлен в DATA.SEND.SCTP, для последующего использования с сообщением Receive Unsent или Receive Unacknowledged. Эти примитивы можно применять для извлечения сообщений (или частей в случае частичной передачи), которые не были переданы или подтверждены.

SEND_FAILURE.UDP(-Lite)

Событие или примитив этапа 1: Send.

Комментарии: Этот примитив можно применять для определения эффективного PMTU при использовании с примитивом MAINTENANCE.SET_DF.

SENDER_DRY-EVENT.SCTP

Событие или примитив этапа 1: Уведомление Sender Dry.

Комментарии: Информирует приложение об отсутствии в стеке пользовательских данных для передачи.

PARTIAL_DELIVERY_ABORTED-EVENT.SCTP

Событие или примитив этапа 1: Уведомление Partial Delivery Aborted.

Комментарии: Информирует получателя частичного сообщения о том, что последующая доставка была прервана.

5. Этап 3

Здесь представлен расширенный набор всех транспортных свойств всех протоколов, описанных в предшествующих разделах, на основе списка примитивов этапа 2, а также текста этапа 1 для включения транспортных свойств, которые могут быть настроены в одном протоколе и являются статическими в другом (например, контроль перегрузок). Некоторые мелкие детали опущены в целях общности, например, TCP может предоставлять разные опции IP, но лишь опция source route обязательна для реализации и эта деталь не видна на этапе 3 в транспортном свойстве «задание опций IP». Как и раньше, UDP(-Lite) представляет протоколы UDP и UDP-Lite, а TCP — TCP и MPTCP.

5.1. Свойства, связанные с соединением

Организация

Активное создание соединения одной транспортной конечной точки с одной или несколькими другими конечными транспортными точками.

Connect

Протоколы: TCP, SCTP, UDP(-Lite).

Задание опций IP, которые должны применяться всегда

Протоколы: TCP и UDP(-Lite).

Запрос множества потоков

Протоколы: SCTP.

Ограничение числа входящих потоков

Протоколы: SCTP.

Задание числа попыток и/или тайм-аутов для первого сообщения при организации

Протоколы: TCP и SCTP.

Получение множества сокетов

Протоколы: SCTP.

Отключение MPTCP

Протоколы: MPTCP.

Настройка аутентификации

Протоколы: TCP и SCTP.

Комментарии: В TCP это позволяет настраивать кортежи MKT, в SCTP — указание блоков, для которых нужна аутентификация. DATA, ACK и т. п. Являются в SCTP разными блоками и могут включаться в один пакет.

Указание кода уровня адаптации

Протоколы: SCTP.

Запрос согласования для чередования пользовательских сообщений

Протоколы: SCTP.

Отправка сообщения с гарантией доставки (возможно несколько раз) до организации соединения

Протоколы: TCP.

Отправка сообщения с гарантией доставки в процессе организации соединения

Протоколы: SCTP.

Включение UDP-инкапсуляции с заданным удаленным портом UDP

Протоколы: SCTP.

Доступность

Подготовка к приему входящих запросов на соединение.

Listen, 1 локальный интерфейс

Протоколы: TCP, SCTP, UDP(-Lite).

Listen, N локальных интерфейсов

Протоколы: SCTP.

Listen, все локальные интерфейсы

Протоколы: TCP, SCTP, UDP(-Lite).

Получение запрошенного числа потоков

Протоколы: SCTP.

Ограничение числа входящих потоков

Протоколы: SCTP.

Задание опций IP, которые должны применяться всегда

Протоколы: TCP и UDP(-Lite).

Отключение MPTCP

Протоколы: MPTCP.

Настройка аутентификации

Протоколы: TCP и SCTP.

Комментарии: В TCP это позволяет настраивать кортежи MKT, в SCTP — указание блоков, для которых нужна аутентификация. DATA, ACK и т. п. Являются в SCTP разными блоками и могут включаться в один пакет.

Указание кода уровня адаптации

Протоколы: SCTP.

Поддержка

Настройки для открытых соединений или уведомления о них.

Смена тайм-аута для разрыва соединения (время или число повторов)

Протоколы: TCP и SCTP.

Предложенный партнеру тайм-аут

Протоколы: TCP.

Отключение алгоритма Nagle

Протоколы: TCP и SCTP.

Запрос немедленной передачи heartbeat с возвратом результата

Протоколы: SCTP.

Уведомление об избыточных повторах (ранее до достижения порога разрыва)

Протоколы: TCP.

Добавление пути

Протоколы: MPTCP и SCTP.

Параметры MPTCP: source-IP, source-Port, destination-IP, destination-Port.

Параметры SCTP: локальный IP-адрес.

Удаление пути

Протоколы: MPTCP и SCTP.

Параметры MPTCP: source-IP, source-Port, destination-IP, destination-Port.

Параметры SCTP: локальный IP-адрес.

Задание основного пути

Протоколы: SCTP.

Предложение основного пути партнеру

Протоколы: SCTP.

Настройка переключения пути

Протоколы: SCTP.

Определение статуса (запрос или уведомление)

Протоколы: SCTP и MPTCP.

Параметры SCTP: Состояние соединения для ассоциации, список транспортных адресов получателей, состояния доступности транспортных адресов получателей, текущие размеры окна получателя (локальный и удаленный), текущий размер локального окна перегрузки, число неподтвержденных блоков DATA, число блоков DATA, ожидающих приема, последнее значение SRTT на основном пути, RTO на основном пути, SRTT и RTO для других адресов получателей, MTU для путей, поддержка чередования (yes/no).

Параметры MPTCP: Список субпотоков (идентификация по source-IP, source-Port, destination-IP, destination-Port).

Задание поля DSCP

Протоколы: TCP, SCTP, UDP(-Lite).

Уведомление о приеме сообщения ICMP об ошибке

Протоколы: TCP и UDP(-Lite).

Смена параметров аутентификации

Протоколы: TCP и SCTP.

Получение данных аутентификации

Протоколы: TCP и SCTP.

Сброс потока

Протоколы: SCTP.

Уведомление о сбросе потока

Протоколы: STCP.

Сброс ассоциации

Протоколы: SCTP.

Уведомление о сбросе ассоциации

Протоколы: STCP.

Добавление потоков

Протоколы: SCTP.

Уведомление о добавлении потоков

Протоколы: STCP.

Выбор планировщика для потоков в ассоциации

Протоколы: SCTP.

Настройка приоритета или веса для планировщика

Протоколы: SCTP.

Установка метки потока IPv6

Протоколы: SCTP.

Настройка размера буфера передачи

Протоколы: SCTP.

Настройка размера приемного буфера (и rwnd)

Протоколы: SCTP.

Настройка фрагментации сообщений

Протоколы: SCTP.

Настройка PMTUD

Протоколы: SCTP.

Настройка таймера задержки SACK

Протоколы: SCTP.

Установка значения Cookie life

Протоколы: SCTP.

Установка максимального пика

Протоколы: SCTP.

Настройка размера сообщений для частичной доставки

Протоколы: SCTP.

Запрет контрольной суммы при отправке

Протоколы: UDP.

Запрет требования контрольной суммы при получении

Протоколы: UDP.

Задание покрытия для контрольной суммы у отправителя

Протоколы: UDP-Lite.

Минимальное покрытие для контрольной суммы, требуемое получателем

Протоколы: UDP-Lite.

Задание поля DF

Протоколы: UDP(-Lite).

Определение максимального размера транспортного сообщения для передачи без фрагментации IP через настроенный интерфейс

Протоколы: UDP(-Lite).

Определение максимального размера транспортного сообщения от настроенного интерфейса

Протоколы: UDP(-Lite).

Задание поля TTL/Hop Count

Протоколы: UDP(-Lite).

Получение поля TTL/Hop Count

Протоколы: UDP(-Lite).

Задание поля ECN

Протоколы: UDP(-Lite).

Получение поля ECN

Протоколы: UDP(-Lite).

Задание опций IP

Протоколы: UDP(-Lite).

Получение опций IP

Протоколы: UDP(-Lite).

Включение и настройка LEDBAT

Протоколы: Протоколы, реализующие механизм контроля перегрузок LEDBAT.

Прерывание

Аккуратное или жесткое завершение соединения или информирование о таком событии.

Закрытие после гарантированной доставки оставшихся данных с уведомлением приложения на другой стороне

Протоколы: TCP и SCTP.

Комментарии: Конечная точка TCP локально закрывает соединение лишь для передачи и может ждать приема данных.

Разрыв без доставки оставшихся данных с уведомлением приложения на другой стороне

Протоколы: TCP и SCTP.

Комментарии: В SCTP приложение может указывать причину разрыва, которая может передаваться приложению на другой стороне.

Разрыв без доставки оставшихся данных и уведомления приложения на другой стороне

Протоколы: UDP(-Lite).

Тайм-аут, когда данные не удается доставить слишком долго

Протоколы: TCP и SCTP.

Комментарии: Тайм-аут задается CONNECTION.MAINTENANCE.

5.2. Свойства, связанные с передачей данных

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

5.2.1. Передача

Все транспортные свойства в этом разделе предоставляются примитивом DATA.SEND этапа 2. DATA.SEND получает блок данных от приложения, который называется сообщением, если можно указать его начало и конец, и данными — в противном случае.

Гарантированная доставка данных с контролем перегрузок

Протоколы: TCP.

Гарантированная доставка сообщения с контролем перегрузок

Протоколы: SCTP.

Негарантированная доставка сообщения с контролем перегрузок

Протоколы: SCTP.

Негарантированная доставка сообщения без контроля перегрузок

Протоколы: UDP(-Lite).

Настраиваемые гарантии для сообщений

Протоколы: SCTP.

Выбор потока

Протоколы: SCTP.

Выбор пути (адреса получателя)

Протоколы: SCTP.

Упорядоченная доставка сообщений (возможно медленней неупорядоченной)

Протоколы: SCTP.

Неупорядоченная доставка сообщений (возможно быстрей упорядоченной)

Протоколы: SCTP и UDP(-Lite).

Запрос отмены группировки сообщений

Протоколы: SCTP.

Задание идентификатора протокола в области данных (для получателя)

Протоколы: SCTP.

Задание идентификатора ключа для аутентификации сообщения

Протоколы: SCTP.

Запрос передачи без задержки подтверждения SACK для сообщения

Протоколы: SCTP.

5.2.2. Прием

Все транспортные свойства в этом разделе предоставляются примитивом DATA.RECEIVE этапа 2. DATA.RECEIVE получает блок данных от приложения, который называется сообщением, если можно указать его начало и конец, и данными — в противном случае.

Прием данных (без границ сообщений)

Протоколы: TCP.

Прием сообщения

Протоколы: SCTP и UDP(-Lite).

Выбор потока для приема

Протоколы: SCTP.

Информация о частичной доставке сообщения

Протоколы: SCTP.

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

5.2.3. Ошибки

Здесь описаны отказы при передаче, связанные с конкретными вызовами примитива DATA.SEND этапа 2.

Уведомление о неотправленном (частино) сообщении

Протоколы: SCTP и UDP(-Lite).

Уведомление о неподтвержденном (частино) сообщении

Протоколы: SCTP.

Уведомление об отсутствии в стеке данных для передачи

Протоколы: SCTP.

Уведомление получателя об отказе при частичной доставке сообщения

Протоколы: SCTP.

6. Взаимодействие с IANA

Этот документ не требует действий со стороны IANA.

7. Вопросы безопасности

Проверка подлинности, защита целостности и конфиденциальности указаны как транспортные свойства в [RFC8095]. Эти свойства обычно присущи транспортному протоколоу или расположенному над ним уровню. Ни один из рассмотренных здесь транспортных протоколов сам по себе не обеспечивает этих свойств, поэтому они не рассматриваются в документе за исключением естественных функций аутентификации в TCP и SCTP, для которых применимы вопросы безопасности, отмеченные в [RFC5925] и [RFC4895].

Вопросы безопасности для UDP и UDP-Lite рассмотрены в упомянутых RFC. Рекомендации по защите приложений, использующих UDP, даны в [RFC8085].

8. Литература

8.1. Нормативные документы

[RFC0793] Postel, J., «Transmission Control Protocol», STD 7, RFC 793, DOI 10.17487/RFC0793, September 1981, <https://www.rfc-editor.org/info/rfc793>.

[RFC1122] Braden, R., Ed., «Requirements for Internet Hosts — Communication Layers», STD 3, RFC 1122, DOI 10.17487/RFC1122, October 1989, <https://www.rfc-editor.org/info/rfc1122>.

[RFC3758] Stewart, R., Ramalho, M., Xie, Q., Tuexen, M., and P. Conrad, «Stream Control Transmission Protocol (SCTP) Partial Reliability Extension», RFC 3758, DOI 10.17487/RFC3758, May 2004, <https://www.rfc-editor.org/info/rfc3758>.

[RFC4895] Tuexen, M., Stewart, R., Lei, P., and E. Rescorla, «Authenticated Chunks for the Stream Control Transmission Protocol (SCTP)», RFC 4895, DOI 10.17487/RFC4895, August 2007, <https://www.rfc-editor.org/info/rfc4895>.

[RFC4960] Stewart, R., Ed., «Stream Control Transmission Protocol», RFC 4960, DOI 10.17487/RFC4960, September 2007, <https://www.rfc-editor.org/info/rfc4960>.

[RFC5061] Stewart, R., Xie, Q., Tuexen, M., Maruyama, S., and M. Kozuka, «Stream Control Transmission Protocol (SCTP) Dynamic Address Reconfiguration», RFC 5061, DOI 10.17487/RFC5061, September 2007, <https://www.rfc-editor.org/info/rfc5061>.

[RFC5482] Eggert, L. and F. Gont, «TCP User Timeout Option», RFC 5482, DOI 10.17487/RFC5482, March 2009, <https://www.rfc-editor.org/info/rfc5482>.

[RFC5925] Touch, J., Mankin, A., and R. Bonica, «The TCP Authentication Option», RFC 5925, DOI 10.17487/RFC5925, June 2010, <https://www.rfc-editor.org/info/rfc5925>.

[RFC6182] Ford, A., Raiciu, C., Handley, M., Barre, S., and J. Iyengar, «Architectural Guidelines for Multipath TCP Development», RFC 6182, DOI 10.17487/RFC6182, March 2011, <https://www.rfc-editor.org/info/rfc6182>.

[RFC6458] Stewart, R., Tuexen, M., Poon, K., Lei, P., and V. Yasevich, «Sockets API Extensions for the Stream Control Transmission Protocol (SCTP)», RFC 6458, DOI 10.17487/RFC6458, December 2011, <https://www.rfc-editor.org/info/rfc6458>.

[RFC6525] Stewart, R., Tuexen, M., and P. Lei, «Stream Control Transmission Protocol (SCTP) Stream Reconfiguration», RFC 6525, DOI 10.17487/RFC6525, February 2012, <https://www.rfc-editor.org/info/rfc6525>.

[RFC6817] Shalunov, S., Hazel, G., Iyengar, J., and M. Kuehlewind, «Low Extra Delay Background Transport (LEDBAT)», RFC 6817, DOI 10.17487/RFC6817, December 2012, <https://www.rfc-editor.org/info/rfc6817>.

[RFC6824] Ford, A., Raiciu, C., Handley, M., and O. Bonaventure, «TCP Extensions for Multipath Operation with Multiple Addresses», RFC 6824, DOI 10.17487/RFC6824, January 2013, <https://www.rfc-editor.org/info/rfc6824>.

[RFC6897] Scharf, M. and A. Ford, «Multipath TCP (MPTCP) Application Interface Considerations», RFC 6897, DOI 10.17487/RFC6897, March 2013, <https://www.rfc-editor.org/info/rfc6897>.

[RFC6951] Tuexen, M. and R. Stewart, «UDP Encapsulation of Stream Control Transmission Protocol (SCTP) Packets for End-Host to End-Host Communication», RFC 6951, DOI 10.17487/RFC6951, May 2013, <https://www.rfc-editor.org/info/rfc6951>.

[RFC7053] Tuexen, M., Ruengeler, I., and R. Stewart, «SACK-IMMEDIATELY Extension for the Stream Control Transmission Protocol», RFC 7053, DOI 10.17487/RFC7053, November 2013, <https://www.rfc-editor.org/info/rfc7053>.

[RFC7413] Cheng, Y., Chu, J., Radhakrishnan, S., and A. Jain, «TCP Fast Open», RFC 7413, DOI 10.17487/RFC7413, December 2014, <https://www.rfc-editor.org/info/rfc7413>.

[RFC7496] Tuexen, M., Seggelmann, R., Stewart, R., and S. Loreto, «Additional Policies for the Partially Reliable Stream Control Transmission Protocol Extension», RFC 7496, DOI 10.17487/RFC7496, April 2015, <https://www.rfc-editor.org/info/rfc7496>.

[RFC7829] Nishida, Y., Natarajan, P., Caro, A., Amer, P., and K. Nielsen, «SCTP-PF: A Quick Failover Algorithm for the Stream Control Transmission Protocol», RFC 7829, DOI 10.17487/RFC7829, April 2016, <https://www.rfc-editor.org/info/rfc7829>.

[RFC8085] Eggert, L., Fairhurst, G., and G. Shepherd, «UDP Usage Guidelines», BCP 145, RFC 8085, DOI 10.17487/RFC8085, March 2017, <https://www.rfc-editor.org/info/rfc8085>.

[RFC8260] Stewart, R., Tuexen, M., Loreto, S., and R. Seggelmann, «Stream Schedulers and User Message Interleaving for the Stream Control Transmission Protocol», RFC 8260, DOI 10.17487/RFC8260, November 2017, <https://www.rfc-editor.org/info/rfc8260>.

[RFC8304] Fairhurst, G. and T. Jones, «Transport Features of the User Datagram Protocol (UDP) and Lightweight UDP (UDP-Lite)», RFC 8304, DOI 10.17487/RFC8304, February 2018, <https://www.rfc-editor.org/info/rfc8304>.

8.2. Дополнительная литература

[RFC0854] Postel, J. and J. Reynolds, «Telnet Protocol Specification», STD 8, RFC 854, DOI 10.17487/RFC0854, May 1983, <https://www.rfc-editor.org/info/rfc854>.

[RFC2119] Bradner, S., «Key words for use in RFCs to Indicate Requirement Levels», BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.

[RFC2474] Nichols, K., Blake, S., Baker, F., and D. Black, «Definition of the Differentiated Services Field (DS Field) in the IPv4 and IPv6 Headers», RFC 2474, DOI 10.17487/RFC2474, December 1998, <https://www.rfc-editor.org/info/rfc2474>.

[RFC2475] Blake, S., Black, D., Carlson, M., Davies, E., Wang, Z., and W. Weiss, «An Architecture for Differentiated Services», RFC 2475, DOI 10.17487/RFC2475, December 1998, <https://www.rfc-editor.org/info/rfc2475>.

[RFC3260] Grossman, D., «New Terminology and Clarifications for Diffserv», RFC 3260, DOI 10.17487/RFC3260, April 2002, <https://www.rfc-editor.org/info/rfc3260>.

[RFC5461] Gont, F., «TCP’s Reaction to Soft Errors», RFC 5461, DOI 10.17487/RFC5461, February 2009, <https://www.rfc-editor.org/info/rfc5461>.

[RFC6093] Gont, F. and A. Yourtchenko, «On the Implementation of the TCP Urgent Mechanism», RFC 6093, DOI 10.17487/RFC6093, January 2011, <https://www.rfc-editor.org/info/rfc6093>.

[RFC7414] Duke, M., Braden, R., Eddy, W., Blanton, E., and A. Zimmermann, «A Roadmap for Transmission Control Protocol (TCP) Specification Documents», RFC 7414, DOI 10.17487/RFC7414, February 2015, <https://www.rfc-editor.org/info/rfc7414>.

[RFC7657] Black, D., Ed. and P. Jones, «Differentiated Services (Diffserv) and Real-Time Communication», RFC 7657, DOI 10.17487/RFC7657, November 2015, <https://www.rfc-editor.org/info/rfc7657>.

[RFC8095] Fairhurst, G., Ed., Trammell, B., Ed., and M. Kuehlewind, Ed., «Services Provided by IETF Transport Protocols and Congestion Control Mechanisms», RFC 8095, DOI 10.17487/RFC8095, March 2017, <https://www.rfc-editor.org/info/rfc8095>.

[TAPS-MINSET] Welzl, M. and S. Gjessing, «A Minimal Set of Transport Services for TAPS Systems», Work in Progress11, draft-ietf-taps-minset-01, February 2018.

Приложение A. Список RFC, использованных для этапа 1

TCP

[RFC0793], [RFC1122], [RFC5482], [RFC5925], [RFC7413].

MPTCP

[RFC6182], [RFC6824], [RFC6897].

SCTP

RFC без спецификаций API сокетов: [RFC3758], [RFC4895], [RFC4960], [RFC5061].

RFC со спецификациями API сокетов: [RFC6458], [RFC6525], [RFC6951], [RFC7053], [RFC7496], [RFC7829].

UDP(-Lite)

См. [RFC8304].

LEDBAT

[RFC6817].

Приложение B. Как разрабатывался этот документ

В этом приложении представлен обзор метода, использованного при разработке документа. Метод представлен для разработчиков и может быть полезен при будущих изменениях или обновлениях.

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

Примитивы, которые могут быть реализованы транспортным протоколом, были исключены из рассмотрения. Для включения в документ минимальным требованием служило указание, что примитив следует реализовать. Там, где не применялся стиль обозначения требований из [RFC2119], примитивы исключались, если они были описаны вместе с такими утверждениями, как «некоторые реализации также представляют» или «реализация может также». Исключенные примитивы и параметры кратко упомянуты в специальных параграфах документа.

Этап 1 начинался с определения текста, описывающего примитив. Обычно примитивы описываются в спецификации API (возможно, абстрактной), но здесь представляли интерес не только спецификации API. Текст, описывающий примитив Send в API [RFC0793], например, не говорит о гарантированной передаче данных. Однако такие гарантии становятся очевидными из текста раздела 1 в [RFC0793].

Протокол TCP предназначен для надежной и гарантированной доставки данных между хостами в компьютерных сетях с коммутацией пакетов и между такими сетями через промежуточные системы.

Часть текста на этапе 1 была заимствована из соответствующих RFC с корректировкой терминов в соответствии с разделом 2 и сокращением фраз для сохранения стиля документа. Была попытка представить все как описания примитивов, сделав их максимально полными (например, примитив SEND.TCP на этапе 2 явно описан как гарантированная доставка данных). Текст, относящийся к представленным на этом этапе примитивам, но не входящий непосредственно в описание какого-либо примитива, использовался во вводной части параграфов.

Этап 2 служил для унификации примитивов. Входными данными служил лишь текст этапа 1 (без внешних источников). Список на этапе 2 упорядочен не по протоколам (в стиле «протокол X и его примитивы, протокол Y и его примитивы»), а по примитивам («примитив A реализован определенным способомв протоколе X, иным способом в протоколе Y …»). Цель состояла в получении на этапе 2 максимального числа похожих примитивов. Например, иной раз это достигалось за счет того, что не всегда поддерживалось отображение 1:1 между этапами 1 и 2, примитивы переименовывались и пр. Для каждого нового примитива рассматривались уже имеющиеся для обеспечения максимальной согласованности.

Для каждого примитива описание было представлено в стиле

ИмяПримитива.Протокол

Событие или примитив этапа 1:

Параметры:

Возврат:

Комментарии:

Записи «Параметры», «Возврат», «Комментарии» пропускались, если с примитивом не было связано соответствующей информации. Необяхательные параметры были помечены текстом «(необязательно)», при наличии принятых по умолчанию значений они указывались.

Этап 3 определял транспортные свойства (функции), являющиеся статическими свойствами протокола, и для них указывались соответствующие протоколы. Это создавало финальный список всех доступных транспортных функций. Список основан прежде всего на тексте этапа 2 с дополнительными данными этапа 1 (но без внешних источников).

Благодарности

Авторы благодарят (в алфавитном порядке) Bob Briscoe, Spencer Dawkins, Aaron Falk, David Hayes, Karen Nielsen, Tommy Pauly, Joe Touch, Brian Trammell за ценные отклики на этот документ. Большое спавибо Christoph Paasch за информацию о Multipath TCP, а Gorry Fairhurst и Tom Jones — за UDP(-Lite). Эта работа финансировалась исследовательской и инновационной программой Европейского Союза Horizon 2020 в рамках гранта 644334 (NEAT).

Адреса авторов

Michael Welzl

University of Oslo

PO Box 1080 Blindern

Oslo N-0316

Norway

Email: michawe@ifi.uio.no

Michael Tuexen

Muenster University of Applied Sciences

Stegerwaldstrasse 39

Steinfurt 48565

Germany

Email: tuexen@fh-muenster.de

Naeem Khademi

University of Oslo

PO Box 1080 Blindern

Oslo N-0316

Norway

Email: naeemk@ifi.uio.no

Перевод на русский язык

Николай Малых

nmalykh@protokols.ru

1Transmission Control Protocol — протокол управления передачей.

2MultiPath TCP — TCP с множеством путей.

3Stream Control Transmission Protocol — протокол управления передачей потоков.

4User Datagram Protocol — протокол пользовательских дейтаграмм.

5Lightweight User Datagram Protocol — облегченный протокол пользовательских дейтаграмм.

6Low Extra Delay Background Transport — базовый транспорт с очень низкими задержками.

7Internet Engineering Task Force.

8Internet Engineering Steering Group.

9Datagram Congestion Control Protocol — протокол контроля перегрузок для дейтаграмм.

10Peer-to-peer — «равный с равным».

11Опубликовано в RFC 8923. Прим. перев.

Рубрика: RFC | Комментарии к записи RFC 8303 On the Usage of Transport Features Provided by IETF Transport Protocols отключены

RFC 8304 Transport Features of the User Datagram Protocol (UDP) and Lightweight UDP (UDP-Lite)

Internet Engineering Task Force (IETF)                      G. Fairhurst
Request for Comments: 8304                                      T. Jones
Category: Informational                           University of Aberdeen
ISSN: 2070-1721                                            February 2018

Transport Features of the User Datagram Protocol (UDP) and Lightweight UDP (UDP-Lite)

Транспортные свойства UDP и UDP-Lite

PDF

Аннотация

Этот информационный документ описывает примитивы интерфейса транспортного протокола, предоставляемые протоколами UDP1 и UDP-Lite2. Он указывает службы дейтаграмм, раскрываемые приложениям, способы настройки приложений и использование ими функций, предоставляемых службой транспортировки дейтаграмм в Internet. В RFC 8303 описано применение транспортных функций, обеспечиваемых транспортными протоколами IETF3, с указанием как UDP, UDP-Lite и другие транспортные протоколы раскрывают свои услуги приложениям и как приложения могут настроить и использовать эти услуги. Этот документ предоставляет исходные данные и контекст для упомянутого документа, а также предлагает план документации, которая может помочь пользователям протоколов UDP и UDP-Lite.

Статус документа

Документ не содержит какой-либо спецификации (Internet Standards Track) и публикуется с информационными целями.

Документ является результатом работы IETF и представляет согласованный взгляд сообщества IETF. Документ прошел открытое обсуждение и был одобрен для публикации IESG4. Дополнительную информацию о стандартах Internet можно найти в разделе 2 в RFC 7841.

Информация о текущем статусе документа, найденных ошибках и способах обратной связи доступна по ссылке https://www.rfc-editor.org/info/rfc8304.

Авторские права

Copyright (c) 2018. Авторские права принадлежат IETF Trust и лицам, указанным в качестве авторов документа. Все права защищены.

К документу применимы права и ограничения, указанные в BCP 78 и IETF Trust Legal Provisions и относящиеся к документам IETF (http://trustee.ietf.org/license-info), на момент публикации данного документа. Прочтите упомянутые документы внимательно. Фрагменты программного кода, включённые в этот документ, распространяются в соответствии с упрощённой лицензией BSD, как указано в параграфе 4.e документа IETF Trust Legal Provisions, без каких-либо гарантий (как указано в Simplified BSD License).

1. Введение

В этом документе представлен некоторые взаимодействия между транспортными протоколами и приложениями в форме примитивов (вызовы функций) для протоколов UDP [RFC0768] и UDP-Lite [RFC3828]. В данном случае приложением считается любая программа, работающая на основе интерфейса дейтаграмм, включая туннели и другие протоколы вышележащих уровней, применяющий UDP и UDP-Lite.

Протокол UDP имеет множество реализаций и применяется для широкого спектра приложений. Особый класс приложений может получать преимущества при доставке поврежденных данных вместо их отбрасывания, когда нужно работать по путям с каналами, подверженными ошибкам. Приложения, устойчивые к повреждению данных, могут выбрать UDP-Lite вместо UDP и использовать API5 приложения для работы с контрольными суммами. Приложения UDP также могут выбрать UDP-Lite, но пока это менее распространено и пользователи могут столкнуться с путями, где UDP-Lite не поддерживается. Эти вопросы более подробно рассмотрены в параграфе 3.4 [RFC8085].

Стандартный IEEE API для приложений TCP/IP использует интерфейс сокетов [POSIX]. Приложение может использовать функции POSIX recv() и send() POSIX, а также recvfrom(), sendto(), recvmsg(), sendmsg(). API сокетов UDP и UDP-Lite отличается от интерфейса для TCP в нескольких важных аспектах (примеры использования API даны в [STEVENS]). В UDP и UDP-Lite каждая дейтаграмма является самодостаточным сообщением заданного размера и на транспортном уровне могут применяться опции, устанавливающие свойства для всех последующих дейтаграмм, передаваемых через этот сокет, или изменяемые для каждой дейтаграммы. Для дейтаграмм от приложения может потребоваться использование API при установке данных уровня IP (IP TTL6, кодов дифференцированного обслуживания DSCP7, фрагментирования IP и т. п.) для передаваемых и принимаемых дейтаграмм. При использовании TCP и другого транспорта, ориентированного на соединения) данные уровня IP обычно остаются неизменными в течение срока соединения или контролируются транспортным протоколом, а не приложением.

Опции сокета применяются в API для обеспечения дополнительных функций. Например, опция IP_RECVTTL применяется некоторыми групповыми приложениями UDP для возврата значения поля IP TTL из заголовка принятой дейтаграммы.

Некоторые платформы предлагают приложениям возможность напрямую собирать и передавать пакеты IP через «необрабатываемые» (raw) сокеты или аналогичные средства. API для raw-сокетов является вторым, более громоздким методом передачи дейтаграмм UDP. Использование этого API рассматривается в [RFC8085].

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

В описании раздела 3 используется терминология, определенная рабочей группой IETF TAPS в [RFC8303]. В частности, документ обеспечивает этап 1 описанного там процесса, который рассматривает RFC, описывающие примитивы каждого протокола. В [RFC8303] эти данные служат входной информацией для описания использования транспортных функций, предоставляемых транспортными протоколами IETF, показывающего как UDP, UDP-Lite и другие транспортные протоколы раскрывают свои услуги приложениям и как приложения могут настроить и применять эти функции для использования транспортных услуг.

Представленный план документации транспортного интерфейса может также помочь разработчикам при работе с UDP и UDP-Lite.

2. Терминология

В этом документе представлены детали для этапа в анализе UDP и UDP-Lite, который используется в [RFC8303]. Документ использует определенную там терминологию , а также уровни требований из RFC 2119 [RFC2119].

3. Примитивы UDP и UDP-Lite

UDP [RFC0768] [RFC8200] и UDP-Lite [RFC3828] — это стандартизованные IETF транспортные протоколы, обеспечивающие односторонние услуги на основе дейтаграмм, поддерживающие операции приема и передачи с сохранением границ сообщений.

В этом разделе приведены относящиеся к теме фрагменты текста RFC, описывающих протоколы UDP и UDP-Lite, с акцентом на предоставление транспортными протоколами своих услуг приложениях и способах использования приложениями этих услуг (на основе описаний абстрактных API, когда они доступны). Описано, как UDP применяется с протоколами IPv4 и IPv6 для отправки индивидуальных и anycast-дейтаграмм, а также для отправки широковещательных дейтаграмм для IPv4. Набор примитивов сетевого уровня для использования UDP или UDP-Lite при групповой адресации IP (IPv4 и IPv6) был задан в серии RFC. Приложение A описывает источники информации о примитивах сетевого уровня, нужных для использования с UDP или UDP-Lite при групповой адресации IP (IPv4 и IPv6).

3.1. Примитивы, предоставляемые UDP

В [RFC0768] сказано:

Протокол передачи пользовательских дейтаграмм (User Datagram Protocol или UDP) предназначен для поддержки режима обмена дейтаграммами на основе коммутации пакетов в среде связанных между собой компьютерных сетей. … Протокол UDP обеспечивает прикладным программам процедуры для передачи сообщений другим приложениям с минимальным сервисом.

В разделе «Пользовательский интерфейс» RFC 768 сказано, что интерфейс с приложением должен предоставлять:

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

Протокол UDP был определен для IPv6 [RFC8200] вместе с расширениями API «Расширения базового интерфейса сокетов для IPv6» [RFC3493].

В [RFC6935] и [RFC6936] обновлено определение транспорта UDP, заданное исходно в [RFC2460] (заменен RFC 8200). Это позволяет использовать режим с нулевой контрольной суммой UDP для туннельных протоколов при условии, что метод удовлетворяет требованиям в соответствующем заявлении о применимости [RFC6936].

UDP предлагает лишь базовый транспортный интерфейс. Дейтаграммы UDP можно напрямую передавать и принимать без обмена между конечными точками сообщениями для организации соединения (т. е. транспортный протокол не выполняет согласования перед обменом данными). Используя API сокетов, приложения могут принимать пакеты от нескольких адресов IP на один сокет UDP. Общая поддержка позволяет указать один локальный IP-адрес, IP-адрес получателя, локальный и удаленный порт. Можно использовать для любого или всех этих полей принятые по умолчанию значения, предоставляемые локальной системой. Локальный адрес конечной точки устанавливается с помощью вызова BIND, адрес удаленной точки — с помощью CONNECT. Функция CLOSE имеет лишь локальную значимость и не влияет на состояние удаленной точки.

Ни UDP, ни UDP-Lite не поддерживают контроля перегрузок, повтора передачи, механизмов пакетизации на прикладном уровне, позволяющих избежать фрагментации IP, и других транспортных функций. Это означает, что использующие UDP приложения должны обеспечивать дополнительные функции на основе транспортного API UDP [RFC8085]. Некоторые транспортные функции требует передачи через API параметров для управления сетевым уровнем (IPv4 или IPv6). Эти дополнительные примитивы можно считать частью сетевого уровня (например, контроль установки флага запрета фрагментирования DF в передаваемых дейтаграммах IPv4), но они необходимы для того, чтобы позволить пользователю UDP API реализовать функции, которые обычно связаны с транспортным уровнем (такие как зондирование максимального размера пакетов на пути). Этот документ включает такие примитивы.

Руководство по использованию услуг UDP представлено в [RFC8085], где также сказано:

Многие операционные системы позволяют подключить сокет UDP, т. е. связать его с конкретной парой адресов и портов. Это похоже на соответствующую функциональность API сокетов TCP, однако в UDP эта операция локальна и служит лишь для упрощения локальных функций приема и передачи, а также фильтрации трафика для конкретных адресов и портов. Привязка сокета UDP не организует соединения и UDP не уведомляет удаленную сторону о привязке локального сокета UDP. Привязка сокета также помогает в настройке опций, влияющих на уровень UDP или IP, например, использование контрольной суммы UDP или опции IP Timestamp. В некоторых стеках привязка сокета также позволяет приложению получать уведомления при получении сообщения ICMP об ошибках для переданных через сокет пакетов [RFC1122].

Базовая спецификация POSIX [POSIX] определяет API, который предлагает приложениям механизмы получения асинхронных событий, связанных с данными, на уровне сокета. Такие вызовы, как poll, select, queue позволяют приложению получать уведомления о прибытии данных в сокет или очистке сокетом своих буферов.

Управляемый обратными вызовами (callback) API сетевой интерфейс можно структурировать поверх этих вызовов. Неявная организация соединений позволяет приложениям передать управление «жизнью» соединения транспортному API, который использует примитивы протокола, чтобы предлагать приложению примитивы через API сокета. Комбинация примитивов UDP (CONNECT.UDP и SEND.UDP) с API вышележащего уровня может предоставлять похожие услуги.

Ниже перечислены примитивы дейтаграмм.

CONNECT

Примитив CONNECT позволяет связать наборы портов отправителя и получателя с сокетом, чтобы организовать «соединение» для трафика UDP. Такое соединение позволяет приложению получать сведения об ошибках от сетевого стека и обеспечивает сокращенный доступ к примитивам SEND и RECEIVE. Поскольку сам протокол UDP не использует соединений, выполнение этого примитива не вызывает передачи дейтаграммы. Для изменения ассоциации можно снова вызвать connect.

Роли клиента и сервера часто не подходят для UDP, где соединения могут быть peer-to-peer (равный с равным или одноранговые). Функция прослушивания выполняется с использованием одной из форм примитива CONNECT.

  1. bind() — операция привязки устанавливает локальный порт неявно, запуская операцию sendto на непривязанном и несоединенном сокете и использованием эфемерного порта, или с явной привязкой для использования настроенного или общепринятого порта.
  2. bind(); connect() — операция привязки с последующим примитивом CONNECT. Привязка организует использование известного локального порта для дейтаграмм вместо эфемерного порта. Операция connect задает известную комбинацию адрес-порт для использования по умолчанию в будущих дейтаграммах. Эта форма применяется после приема дейтаграммы от конечной точки, которая вызвала создание соедиения или может быть инициирована сторонней конфигурацией или протокольным триггером (например, прием записи протокола описания сервиса UDP SDP [RFC4566]).

SEND

Примитив SEND передает предоставленное число байтов, которые UDP следует отправить на другую сторону соединения UDP в дейтаграмме UDP. Примитив может применяться приложением для отправки дейтаграмм напрямую конечной точке, указанной адресом и портом. Если соединение создано, пара адрес-порт выводится из текущего соединения для сокета. Подключение сокета позволяет возвращать приложению сетевые ошибки в качестве уведомлений от примитива SEND. Переданные SEND сообщения, которые нельзя передать неделимо в пакете IP, не будут передаваться на сетевой уровень и вызовут ошибку.

RECEIVE

Примитив RECEIVE выделяет приемный буфер для размещения полученной дейтаграммы. Примитив возвращает число байтов из полученной дейтаграммы UDP. В параграфе 4.1.3.5 [RFC1122] сказано: «При получении дейтаграммы UDP указанный адрес получателя должен передаваться на уровень приложений».

CHECKSUM_ENABLED

Необязательный примитив CHECKSUM_ENABLED определяет, включает ли отправитель контрольную сумму UDP при передаче дейтаграмм [RFC0768] [RFC6935] [RFC6936] [RFC8085]. Если примитив сброшен (unset), это отменяет принятое по умолчанию поведение UDP, отключая контрольную сумму при отправке. В параграфе 4.1.3.4 [RFC1122] сказано: «Приложения могут управлять процессом генерации контрольных сумм UDP, но по умолчанию протокол должен включать контрольную сумму».

REQUIRE_CHECKSUM

Необязательный примитив REQUIRE_CHECKSUM определяет поведение при получении дейтаграмм с нулевой контрольной суммой UDP. По умолчанию UDP требует наличия контрольной суммы. В параграфе 4.1.3.4 [RFC1122] сказано: «Приложения могут управлять решением вопроса об отбрасывании дейтаграмм без контрольной суммы». Параграф 3.1 спецификации UDP-Lite [RFC3828] требует отличной от 0 контрольной суммы, поэтому интерфейс UDP-Lite API должен отбрасывать дейтаграммы с нулевой контрольной суммой.

SET_IP_OPTIONS

Примитив SET_IP_OPTIONS запрашивает у сетевого уровня передачу дейтаграммы с заданными опциями IP. В параграфе 4.1.3.2 [RFC1122] сказано: «Приложениям должна предоставляться возможность установки опций IP для передаваемых дейтаграмм UDP и протокол UDP должен передавать эти опции уровню IP».

GET_IP_OPTIONS

Примитив GET_IP_OPTIONS отыскивает опции IP для принятой сетевым уроынем дейтаграммы. В параграфе 4.1.3.2 [RFC1122] указано, что на стороне получателя UDP: «Протокол UDP должен передавать прикладным программам все опции IP, полученные от уровня IP».

SET_DF

Примитив SET_DF позволяет сетевому уровню фрагментировать пакеты с использованием Fragment Offset в IPv4 [RFC6864], а хосту — использовать Fragment Headers в IPv6 [RFC8200]. SET_DF устанавливает флаг DF в заголовке пакета IPv4 с дейтаграммой UDP, управляющий возможностью фрагментирования пакетов IPv4 маршрутизаторами. Хотя некоторые приложения опираются на поддержку фрагментирования, в общем случае приложениям UDP следует реализовать метод предотвращения фрагментации IP (раздел 4 в [RFC8085]). Отметим, что во многих других транспортных протоколах IETF (например, TCP и SCTP) транспорт обеспечивает поддержку, требуемую для применения DF. Однако при использовании UDP приложение отвечает за методы, требуемые для определения эффективного Path MTU (PMTU) на пути через сеть в координации с сетевым уровнем. Классический механизм Path MTU Discovery (PMTUD) [RFC1191] основан на возврате с пути сообщений ICMP Fragmentation Needed или ICMPv6 Packet Too Big. Когда такие сообщения ICMP не доставляются (или фильтруются), отправитель не может узнать реальное значение PMTU и дейтаграммы UDP, размер которых превышает PMTU, уйдут в «черную дыру». Для предотвращения этого приложение может реализовать механизм PLPMTUD (Packetization Layer Path MTU Discovery) [RFC4821], который не опирается на поддержку в сети сообщений ICMPv6 и поэтому считается более отказоустойчивым, нежели стандартный механизм PMTUD, как отмечено в [RFC8085] и [RFC8201].

GET_MMS_S

Примитив GET_MMS_S извлекает значение сетевого уровня, указывающее максимальный размер сообщения (MMS), которое можно передать на транспортном уровне в нефрагментируемом пакете IP через настроенный интерфейс. Это значение задано в параграфе 6.1 [RFC1191] и параграфе 5.1 [RFC8201]. Значение рассчитывается по эффективному MTU для передачи (Effective MTU for Sending или EMTU_S) и MTU на канале для данного IP-адреса отправителя. Учитывается размер заголовка IP и пространство, резервируемое уровнем IP для дополнительных заголовков (при наличии). Приложениям UDP следует использовать это значение как часть метода предотвращения отправки дейтаграмм UDP, порождающих пакеты IP, размер которых превышает эффективное значение PMTU на пути через сеть. Эффективное значение PMTU (см. раздел 1 в [RFC1191]) эквивалентно EMTU_S (задано в [RFC1122]). В спецификации PLPMTUD [RFC4821] сказано:

Если PLPMTUD обновляет MTU для определенного пути, все сессии уровня пакетизации, использующие этот путь (см. параграф 5.2), следует уведомить об использовании нового MTU и выполнении требуемой корректировки контроля перегрузок.

GET_MMS_R

Примитив GET_MMS_R извлекает значение сетевого уровня, указывающее MMS для пакетов, принимаемых транспортным уровнем через настроенный интерфейс. Значение задано в параграфе 3.1 [RFC1191] и рассчитывается из эффективного MTU для приема (Effective MTU for Receiving или EMTU_R) и MTU канала для данного IP-адреса отправителя с учетом размера заголовка IP и пространства, резервируемого уровнем IP для дополнительных заголовков (при наличии).

SET_TTL

Примитив SET_TTL устанавливает значение Hop Limit (поле TTL) на сетевом уровне, которое используется в заголовке IPv4 пакета с дейтаграммой UDP. Поле служит для ограничения области действия индивидуальных дейтаграмм. В параграфе 3.2.2.4 [RFC1122] сказано: «Принимаемые сообщения Time Exceeded должны передаваться на транспортный уровень».

GET_TTL

Примитив GET_TTL извлекает значение поля TTL в пакете IP, принятом сетевым уровнем. Приложения с поддержкой обобщенного механизма защиты GTSM [RFC5082] могут использовать эту информацию, чтобы доверять дейтаграммам со значением TTL в ожидаемом диапазоне, как указано в разделе 3 RFC 5082.

SET_MIN_TTL

Примитив SET_MIN_TTL ограничивает доставку дейтаграмм приложению на основании поля IP TTL, значение которого должно быть не меньше указанного параметром. Примитив можно использовать для реализации таких приложений, как GTSM (раздел 3 в RFC 5082) [RFC5082], но RFC не задает этого метода.

SET_IPV6_UNICAST_HOPS

Примитив SET_IPV6_UNICAST_HOPS задает поле сетевого уровня Hop Limit в заголовке пакета IPv6 [RFC8200] с дейтаграммой UDP. Для индивидуальных дейтаграмм IPv6 это функционально эквивалентно SET_TTL в IPv4.

GET_IPV6_UNICAST_HOPS

Примитив GET_IPV6_UNICAST_HOPS является функцией сетевого уровня, считывающей значение счетчика интервалов в заголовкеIPv6 header [RFC8200] полученной дейтаграммы UDP. Это задано в параграфе 6.3 RFC 3542. Для индивидуальных дейтаграмм IPv6 это функционально эквивалентно GET_TTL в IPv4.

SET_DSCP

Примитив SET_DSCP является функцией сетевого уровня, устанавливающей значение DSCP (или Type of Service — ToS) [RFC2474] в поле заголовка IP пакета с дейтаграммой UDP. В параграфе 2.4 [RFC1123] сказано: «Приложение должно выбирать приемлемые значения TOS при обращении к службам транспортного уровня и эти значения должны быть настраиваемыми». Приложению следует поддерживать возможность изменения ToS в процессе работы соединения и значение ToS следует передавать на уровень IP без изменения. В параграфе 4.1.4 [RFC1122] сказано: «UDP может передавать полученные значения TOS на уровень приложений». Модель Diffserv [RFC2475] [RFC3260] заменила это поле заголовка IP, выделив 6 старших битов для поля DSCP [RFC2474]. Сохранение требования [RFC1122] разрешать приложению задавать тип обслуживания следует понимать как сохранение возможности API разрешать приложению установку DSCP. В параграфе 3.1.8 [RFC8085] описаны способы использования этого поля приложениями UDP. Обычно сокет UDP будет назначать одно значение DSCP для всех дейтаграмм потока, но отправителю разрешено в некоторых случаях применять разные значения DSCP для дейтаграмм одного потока [RFC8085]. Рекомендации для WebRTC иллюстрируют такой случай [RFC7657].

SET_ECN

Примитив SET_ECN является функцией сетевого уровня, устанавливающей поле явного контроля перегрузок (Explicit Congestion Notification или ECN) в заголовке IP дейтаграммы UDP. Поле ECN по умолчанию имеет значение 00. Когда использование поля ToS было переопределено в Diffserv [RFC3260], 2 бита поля были выделены для ECN [RFC3168]. В параграфе 3.1.78 [RFC8085] описано, как приложению UDP следует использовать это поле. Отметим, что во многих других транспортных протоколах IETF (например, TCP) транспорт обеспечивает поддержку ECN. При использовании UDP за методы, требуемые для применения ECN отвечает приложение или протокол вышележащего уровня.

GET_ECN

Примитив GET_ECN является функцией сетевого уровня, возвращающей значение поля ECN в заголовке IP принятой дейтаграммы UDP. В параграфе 3.1.5 [RFC8085] сказано: «UDP, должен проверять поле ECN для каждой принятой на этом порту дейтаграммы UDP». Это требует от UDP API на стороне получателя передавать полученное поле ECN прикладному уровню для подобающей реакции на перегрузку.

ERROR_REPORT

Событие ERROR_REPORT информирует приложение о некритичной ошибке (soft errors), включая прием сообщения ICMP или ICMPv6 об ошибке. В параграфе 4.1.3.39 [RFC1122] сказано: «Протокол UDP должен передавать на уровень приложений все сообщения ICMP об ошибках, полученные от уровня IP». Это событие требуется, например, для реализации на основе ICMP механизма Path MTU Discovery [RFC1191] [RFC8201]. Приложения UDP должны выполнять примитив CONNECT для получения сообщений ICMP об ошибках.

CLOSE

Примитив CLOSE закрывает соединение. После этого дейтаграммы не передаются и не принимаются. Поскольку сам протокол UDP не использует соединения, после выполнения этого примитива дейтаграммы не передаются.

3.1.1. Исключенные примитивы

В параграфе 3.4 [RFC1122] описаны примитивы GET_MAXSIZES и ADVISE_DELIVPROB, а в параграфе 3.3.4.4 — GET_SRCADDR. Эти механизмы больше не применяются. Документ также задает использование сообщений ICMP Source Quench, которые были отменены [RFC6633].

Функция IPV6_V6ONLY является примитивом сетевого уровня, который применяется ко всем транспортным службам, как указано в параграфе 5.3 для базового интерфейса сокетов IPv6 [RFC3493]. Это ограничивает использование информации от распознавателя имен, разрешая лишь коммуникации сокетов AF_INET6 с использованием IPv6 и не считается частью транспортного сервиса.

3.2. Примитивы, предоставляемые UDP-Lite

UDP-Lite [RFC3828] предоставляет услуги, похожие на UDP. В протоколе изменена семантика поля размера данных UDP (payload length) на семантику поля покрытия контрольной суммой. UDP-Lite требует расчета контрольной суммы псевдозаголовка отправителем и ее проверки получателем. В остальном UDP-Lite семантически эквивалентен UDP.

Передающий интерфейс UDP-Lite отличается от UDP добавление одной опции (сокета), указывающей область покрытия контрольной суммы, которая задает учитываемую в расчете часть данных, а остальные называют нечувствительными к ошибкам (error-insensitive part).

Приемный интерфейс UDP-Lite отличается от UDP добавлением одной опции (сокета), задающей минимальное покрытие контрольной суммой. База UDP-Lite MIB (Management Information Base) [RFC5097] дополняет метод покрытия контрольной суммой. Рекомендации по использованию услуг UDP-Lite приведены в [RFC8085].

UDP-Lite требует наличия контрольной суммы UDP или UDP-Lite, поэтому не разрешает использовать функцию DISABLE_CHECKSUM для запрета расчета контрольной суммы и не позволяет отключить проверку контрольной суммы получателем с помощью REQUIRE_CHECKSUM. Остальные примитивы и функции UDP разрешены.

Кроме того, добавлено 2 примитива, описанных ниже.

SET_CHECKSUM_COVERAGE

Примитив SET_CHECKSUM_COVERAGE задает облать дейтаграммы, учитываемую в контрольной сумме. Трафик UDP-Lite использует этот примитив для установки размера области покрытия контрольной суммой UDP. В параграфе 3.3 спецификации UDP-Lite [RFC3828] сказано: «Приложениям, желающим определить часть своих данных, как нечувствительные к битовым ошибкам …, следует делать это путем явного системного вызова на стороне отправителя». По умолчанию применяется такое же покрытие как в UDP.

SET_MIN_COVERAGE

Примитив SET_MIN_COVERAGE задает минимальную приемлемую область покрытия контрольной суммой в принимаемой дейтаграмме. Трафик UDP-Lite использует этот примитив для установки области покрытия, проверяемой на приеме (в параграфе 1.1 [RFC5097] указана запись MIB udpliteEndpointMinCoverage). В параграфе 3.3 спецификации UDP-Lite [RFC3828] сказано: «Приложениям, желающим получать данные с неполным покрытием для контрольной суммы, следует информировать принимающую систему с помощью явного системного вызов». По умолчанию для принимаемых дейтаграмм требуется лишь минимальное покрытие.

4. Взаимодействие с IANA

Этот документ не требует действий со стороны IANA.

5. Вопросы безопасности

Вопросы безопасности при использовании UDP и UDP-Lite рассмотрены в упомянутых RFC. Рекомендации по защите приложений, использующих протоколы, даны в [RFC8085].

6. Литература

6.1. Нормативные документы

[RFC0768] Postel, J., «User Datagram Protocol», STD 6, RFC 768, DOI 10.17487/RFC0768, August 1980, <https://www.rfc-editor.org/info/rfc768>.

[RFC1112] Deering, S., «Host extensions for IP multicasting», STD 5, RFC 1112, DOI 10.17487/RFC1112, August 1989, <https://www.rfc-editor.org/info/rfc1112>.

[RFC1122] Braden, R., Ed., «Requirements for Internet Hosts — Communication Layers», STD 3, RFC 1122, DOI 10.17487/RFC1122, October 1989, <https://www.rfc-editor.org/info/rfc1122>.

[RFC1123] Braden, R., Ed., «Requirements for Internet Hosts — Application and Support», STD 3, RFC 1123, DOI 10.17487/RFC1123, October 1989, <https://www.rfc-editor.org/info/rfc1123>.

[RFC1191] Mogul, J. and S. Deering, «Path MTU discovery», RFC 1191, DOI 10.17487/RFC1191, November 1990, <https://www.rfc-editor.org/info/rfc1191>.

[RFC2119] Bradner, S., «Key words for use in RFCs to Indicate Requirement Levels», BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.

[RFC3168] Ramakrishnan, K., Floyd, S., and D. Black, «The Addition of Explicit Congestion Notification (ECN) to IP», RFC 3168, DOI 10.17487/RFC3168, September 2001, <https://www.rfc-editor.org/info/rfc3168>.

[RFC3493] Gilligan, R., Thomson, S., Bound, J., McCann, J., and W. Stevens, «Basic Socket Interface Extensions for IPv6», RFC 3493, DOI 10.17487/RFC3493, February 2003, <https://www.rfc-editor.org/info/rfc3493>.

[RFC3828] Larzon, L-A., Degermark, M., Pink, S., Jonsson, L-E., Ed., and G. Fairhurst, Ed., «The Lightweight User Datagram Protocol (UDP-Lite)», RFC 3828, DOI 10.17487/RFC3828, July 2004, <https://www.rfc-editor.org/info/rfc3828>.

[RFC6864] Touch, J., «Updated Specification of the IPv4 ID Field», RFC 6864, DOI 10.17487/RFC6864, February 2013, <https://www.rfc-editor.org/info/rfc6864>.

[RFC6935] Eubanks, M., Chimento, P., and M. Westerlund, «IPv6 and UDP Checksums for Tunneled Packets», RFC 6935, DOI 10.17487/RFC6935, April 2013, <https://www.rfc-editor.org/info/rfc6935>.

[RFC6936] Fairhurst, G. and M. Westerlund, «Applicability Statement for the Use of IPv6 UDP Datagrams with Zero Checksums», RFC 6936, DOI 10.17487/RFC6936, April 2013, <https://www.rfc-editor.org/info/rfc6936>.

[RFC8085] Eggert, L., Fairhurst, G., and G. Shepherd, «UDP Usage Guidelines», BCP 145, RFC 8085, DOI 10.17487/RFC8085, March 2017, <https://www.rfc-editor.org/info/rfc8085>.

[RFC8200] Deering, S. and R. Hinden, «Internet Protocol, Version 6 (IPv6) Specification», STD 86, RFC 8200, DOI 10.17487/RFC8200, July 2017, <https://www.rfc-editor.org/info/rfc8200>.

[RFC8201] McCann, J., Deering, S., Mogul, J., and R. Hinden, Ed., «Path MTU Discovery for IP version 6», STD 87, RFC 8201, DOI 10.17487/RFC8201, July 2017, <https://www.rfc-editor.org/info/rfc8201>.

[RFC8303] Welzl, M., Tuexen, M., and N. Khademi, «On the Usage of Transport Features Provided by IETF Transport Protocols», RFC 8303, DOI 10.17487/RFC8303, February 2018, <https://www.rfc-editor.org/info/rfc8303>.

6.2. Дополнительная литература

[POSIX] IEEE, «Standard for Information Technology — Portable Operating System Interface (POSIX(R)) Base Specifications», Issue 7, IEEE 1003.1, <http://ieeexplore.ieee.org/document/7582338/>.

[RFC2460] Deering, S. and R. Hinden, «Internet Protocol, Version 6 (IPv6) Specification», RFC 2460, DOI 10.17487/RFC2460, December 1998, <https://www.rfc-editor.org/info/rfc2460>.

[RFC2474] Nichols, K., Blake, S., Baker, F., and D. Black, «Definition of the Differentiated Services Field (DS Field) in the IPv4 and IPv6 Headers», RFC 2474, DOI 10.17487/RFC2474, December 1998, <https://www.rfc-editor.org/info/rfc2474>.

[RFC2475] Blake, S., Black, D., Carlson, M., Davies, E., Wang, Z., and W. Weiss, «An Architecture for Differentiated Services», RFC 2475, DOI 10.17487/RFC2475, December 1998, <https://www.rfc-editor.org/info/rfc2475>.

[RFC3260] Grossman, D., «New Terminology and Clarifications for Diffserv», RFC 3260, DOI 10.17487/RFC3260, April 2002, <https://www.rfc-editor.org/info/rfc3260>.

[RFC3376] Cain, B., Deering, S., Kouvelas, I., Fenner, B., and A. Thyagarajan, «Internet Group Management Protocol, Version 3», RFC 3376, DOI 10.17487/RFC3376, October 2002, <https://www.rfc-editor.org/info/rfc3376>.

[RFC3678] Thaler, D., Fenner, B., and B. Quinn, «Socket Interface Extensions for Multicast Source Filters», RFC 3678, DOI 10.17487/RFC3678, January 2004, <https://www.rfc-editor.org/info/rfc3678>.

[RFC3810] Vida, R., Ed. and L. Costa, Ed., «Multicast Listener Discovery Version 2 (MLDv2) for IPv6», RFC 3810, DOI 10.17487/RFC3810, June 2004, <https://www.rfc-editor.org/info/rfc3810>.

[RFC4566] Handley, M., Jacobson, V., and C. Perkins, «SDP: Session Description Protocol», RFC 4566, DOI 10.17487/RFC4566, July 2006, <https://www.rfc-editor.org/info/rfc4566>.

[RFC4604] Holbrook, H., Cain, B., and B. Haberman, «Using Internet Group Management Protocol Version 3 (IGMPv3) and Multicast Listener Discovery Protocol Version 2 (MLDv2) for Source-Specific Multicast», RFC 4604, DOI 10.17487/RFC4604, August 2006, <https://www.rfc-editor.org/info/rfc4604>.

[RFC4821] Mathis, M. and J. Heffner, «Packetization Layer Path MTU Discovery», RFC 4821, DOI 10.17487/RFC4821, March 2007, <https://www.rfc-editor.org/info/rfc4821>.

[RFC5082] Gill, V., Heasley, J., Meyer, D., Savola, P., Ed., and C. Pignataro, «The Generalized TTL Security Mechanism (GTSM)», RFC 5082, DOI 10.17487/RFC5082, October 2007, <https://www.rfc-editor.org/info/rfc5082>.

[RFC5097] Renker, G. and G. Fairhurst, «MIB for the UDP-Lite protocol», RFC 5097, DOI 10.17487/RFC5097, January 2008, <https://www.rfc-editor.org/info/rfc5097>.

[RFC5790] Liu, H., Cao, W., and H. Asaeda, «Lightweight Internet Group Management Protocol Version 3 (IGMPv3) and Multicast Listener Discovery Version 2 (MLDv2) Protocols», RFC 5790, DOI 10.17487/RFC5790, February 2010, <https://www.rfc-editor.org/info/rfc5790>.

[RFC6633] Gont, F., «Deprecation of ICMP Source Quench Messages», RFC 6633, DOI 10.17487/RFC6633, May 2012, <https://www.rfc-editor.org/info/rfc6633>.

[RFC7657] Black, D., Ed. and P. Jones, «Differentiated Services (Diffserv) and Real-Time Communication», RFC 7657, DOI 10.17487/RFC7657, November 2015, <https://www.rfc-editor.org/info/rfc7657>.

[STEVENS] Stevens, W., Fenner, B., and A. Rudoff, «UNIX Network Programming, The sockets Networking API», Volume 1, ISBN-13: 9780131411555, October 2003.

Приложение A. Примитивы групповой адресации

В этом приложении описаны примитивы, служащие для поддержки в UDP и UDP-Lite групповой адресации IPv4 и IPv6. Групповые услуги не рассматриваются рабочей группой IETF TAPS, но имеющиеся примитивы для полноты указаны в этом приложении. Рекомендации по использованию групповых услуг UDP и UDP-Lite даны в [RFC8085].

Групповая адресация IP может поддерживаться с помощью модели ASM10 или SSM11. Во втором случае требуется применять фильтр отправителей (Multicast Source Filter или MSF) при указании группового IP-адреса получателей.

Для использования групповой адресации нужны дополнительные примитивы в транспортном API, которые вызываются для координации действий протоколов сетевого уровня IPv4 и IPv6. Например, для получения отправленных в группу дейтаграмм конечная точка должна сначала стать членом группы на сетевом уровне. Локальный прием групповых пакетов IPv4 использует сигнализацию протокола Internet Group Management Protocol (IGMP) [RFC3376] [RFC4604]. В IPv6 используется эквивалентный протокол Multicast Listener Discovery (MLD) [RFC3810] [RFC5790], работающий на основе ICMPv6. Облегченные версии этих протоколов заданы в [RFC5790].

Ниже перечислены примитивы, применяемые для групповой адресации.

JoinHostGroup

В параграфе 7.1 [RFC1112] представлены функции, позволяющие принимать трафик из multicast-групп IP.

JoinLocalGroup

В параграфе 7.3 [RFC1112] представлены функции, позволяющие принимать трафик из локальных групп IP.

LeaveHostGroup

В параграфе 7.1 [RFC1112] представлены функции, позволяющие выходить из multicast-групп IP.

LeaveLocalGroup

В параграфе 7.3 представлены функции, позволяющие выходить из локальных multicast-групп IP.

IPV6_MULTICAST_IF

В параграфе 5.2 IPv6 [RFC3493] сказано, что этот примитив задает интерфейс, используемый для исходящих групповых пакетов.

IP_MULTICAST_TTL

Устанавливает поле времени жизни t в исходящих групповых пакетах IPv4 для ограничения области распространения дейтаграмм. Такие методы, как GTSM [RFC5082], устанавливают это значение для обеспечения передачи в локальный канал. GTSM также требует у получателя UDP API для передачи этого значения приложению.

IPV6_MULTICAST_HOPS

В параграфе 5.2 [RFC3493] сказано, что этот примитив задает число интервалов (hop count) для исходящих групповых пакетов IPv6 (эквивалент IP_MULTICAST_TTL в IPv4).

IPV6_MULTICAST_LOOP

В параграфе 5.2 [RFC3493] сказано, что этот примитив задает, нужно ли возвращать копию дейтаграммы на уровень IP для локальной доставки при передаче дейтаграммы в группу, к которой относится передающий хост.

IPV6_JOIN_GROUP

В параграфе 5.2 [RFC3493] представлена функция для включения конечной точки в multicast-группу IPv6.

SIOCGIPMSFILTER

В параграфе 8.1 [RFC3678] представлена функция, позволяющая читать фильтры multicast-отправителей.

SIOCSIPMSFILTER

В параграфе 8.1 [RFC3678] представлена функция для установки и изменения фильтров multicast-отправителей.

IPV6_LEAVE_GROUP

В параграфе 5.2 [RFC3493] представлена функция для выхода конечной точки из multicast-группы IPv6.

В [RFC3678] обновлен групповой интерфейс с добавлением поддержки MSF для IPv4 и IPv6, требуемой IGMPv3. В разделе 3 определены базовый и расширенный API, а раздел 5 описывает независимые от протокола версии этих API. Определены 4 набора функциональности API:

  1. Базовый IPv4 API. Каждый вызов функции задает один адрес отправителя, который следует добавить или удалить для фильтра данной прослушиваемой группы адресов.

  2. Расширенный IPv4 API. Позволяет приложению задать полный фильтр отправителей на замену имеющемуся.

  3. Независимый от протокола базовый MSF API.

  4. Независимый от протокола расширенный MSF API.

Определенные вновь примитивы перечислены ниже.

IP_ADD_MEMBERSHIP

Служит для присоединения к группе ASM.

IP_BLOCK_SOURCE

Этот фильтр MSF может блокировать данные от указанного отправителя в данную группу ASM или SSM.

IP_UNBLOCK_SOURCE

Обновляет MSF для отмены предыдущего вызова IP_UNBLOCK_SOURCE для группы ASM или SSM.

IP_DROP_MEMBERSHIP

Служит для выхода из группы ASM или SSM (в SSM ведет к отбрасыванию всех отправителей, присоединенных к конкретной группе и интерфейсу, что эквивалентно закрытию сокета).

В параграфе 4.1.2 MSF [RFC3678] обновлен интерфейс путем добавления поддержки IPv4 MSF в IGMPv3 с ASM.

IP_ADD_SOURCE_MEMBERSHIP

Служит для присоединения к группе SSM.

IP_DROP_SOURCE_MEMBERSHIP

Служит для выхода из группы SSM.

Параграф 4.2 [RFC3678] определяет расширенный API, примитивы которого указаны ниже.

setipv4sourcefilter

Служит для присоединения к группе IPv4 или разрешения multicast-трафика из указанного источника.

getipv4sourcefilter

Служит для выхода из группы IPv4 или фильтрации multicast-трафика из указанного источника.

В параграфе 5.1 [RFC3678] заданы функции независимого от протокола базового API, перечисленные ниже.

MCAST_JOIN_GROUP

Служит для присоединения к группе ASM.

MCAST_JOIN_SOURCE_GROUP

Служит для присоединения к группе SSM.

MCAST_BLOCK_SOURCE

Служит для блокировки отправителя в группе ASM.

MCAST_UNBLOCK_SOURCE

Удаляет прежний фильтр MSF, установленный MCAST_BLOCK_SOURCE.

MCAST_LEAVE_GROUP

Служит для выхода из группы ASM или SSM.

MCAST_LEAVE_SOURCE_GROUP

Служит для выхода из группы SSM.

В параграфе 5.2 [RFC3678] заданы функции независимого от протокола расширенного API для IPv4 и IPv6.

setsourcefilter

Служит для присоединения к группе IPv4 или IPv6 и разрешения multicast-трафика из указанного источника.

getsourcefilter

Служит для выхода из группы IPv4 или IPv6 и фильтрации multicast-трафика из указанного источника.

Протоколы Lightweight IGMPv3 (LW_IGMPv3) и MLDv2 [RFC5790] обновляют этот интерфейс (параграф 7.2 в RFC 5790).

Благодарности

Эта работа частично финансировалась исследовательской и инновационной программой Европейского Союза Horizon 2020 в рамках гранта 644334 (NEAT). Спасибо всем, кто внес свои комментарии или вклад в работу, включая Joe Touch, Ted Hardie, Aaron Falk, Tommy Pauly и Francis Dupont.

Адреса авторов

Godred Fairhurst

University of Aberdeen

School of Engineering

Fraser Noble Building

Fraser Noble Building Aberdeen AB24 3UE

United Kingdom

Email: gorry@erg.abdn.ac.uk

Tom Jones

University of Aberdeen

School of Engineering

Fraser Noble Building

Aberdeen AB24 3UE

United Kingdom

Email: tom@erg.abdn.ac.uk

Перевод на русский язык

Николай Малых

nmalykh@protokols.ru

1User Datagram Protocol — протокол пользовательских дейтаграмм.

2Lightweight User Datagram Protocol — облегченный протокол UDP.

3Internet Engineering Task Force.

4Internet Engineering Steering Group.

5Application programming interface — интерфейс с прикладными программами.

6Time To Live — время жизни.

7Differentiated Services Code Point.

8В оригинале ошибочно указан параграф 3.1.5. См. https://www.rfc-editor.org/errata/eid6370. Прим. перев.

9В оригинале ошибочно указан параграф 4.1.4. https://www.rfc-editor.org/errata/eid6371. Прим. перев.

10Any Source Multicast — групповая адресация для любого источника.

11Source-Specific Multicast — зависимая от источника групповая адресация.

Рубрика: RFC | Комментарии к записи RFC 8304 Transport Features of the User Datagram Protocol (UDP) and Lightweight UDP (UDP-Lite) отключены

GNU Automake

PDF

1. Введение

Automake служит инструментом для автоматического создания файла Makefile.in из файла Makefile.am. Каждый файл Makefile.am содержит набор определений переменных make1, в которые иногда добавляются правила. Создаваемые файлы Makefile.in соответствуют стандартам GNU Makefile.

Документ GNU Makefile Standards достаточно велик, сложен и может изменяться. Цель Automake заключается в упрощении поддержки файлов Makefile для сопровождающих пакеты людей и перенесение этой нагрузки на сопровождающих пакет Automake.

Обычно входными данными Automake являются просто определения переменных, а результатом — файлы Makefile.in.

Automake вносит в проекты ряд ограничений. Например, предполагается, что проект использует программу Autoconf. Имеются также ограничения для содержимого configure.ac.

Для Automake требуется пакет perl, применяемый при создании Makefile.in. Однако создаваемые с помощью Automake дистрибутивы полностью соответствуют стандартам GNU и не требуют для сборки наличия perl.

2. Введение в Autotools

Automake является частью пакета Autotools, включающего файлы configure, configure.ac, Makefile.in, Makefile.am, aclocal.m4 и др., часть которых создается Autoconf или Automake. Целью этого раздела является ознакомление с механизмами работы и связями между этими файлами. Обучающие материалы, подготовленные Alexandre Duret-Lutz, доступны по ссылке.

2.1. Введение в систему сборки GNU

Очевидно, что разработчикам программ требуется система сборки. В среде Unix такая среда реализуется на основе команды make, а рецепт (задание) для сборки содержится в файле Makefile. Этот файл представляет собой набор правил для сборки файлов пакета. Например, программа prog может быть собрана путем запуска компоновщика для файлов main.o, foo.o и bar.o, а файл main.o — путем запуска компилятора для main.c и т. д.. При каждом запуске make считывается Makefile, проверяется наличие и время изменения упомянутых в нем файлов и принимается решение о сборке (повторной сборке), после чего выполняются соответствующие команды.

Когда пакет нужно собрать не на той платформе, где он был разработан, файл Makefile обычно приходится редактировать. Например, на платформе сборки компилятор может называться иначе или использовать другие опции. В 1991 г. David J. MacKenzie, устав от настройки Makefile для 20 разных платформ, с которыми приходилось работать, написал простой shell-сценарий configure для автоматического создания файлов Makefile. Для компиляции пакета теперь было достаточно ввести команду

./configure && make.

Сегодня этот процесс стандартизован в проекте GNU. Документ GNU Coding Standards указывает, что в каждый проект GNU следует включать сценарий configure, и описывает его минимальный интерфейс. Для файлов Makefile также имеется ряд соглашений. В результате получилась унифицированная система сборки, которая позволяет установить почти любой пакет похожим способом. В простейшем варианте это последовательность команд

./configure && make && make install

Эту систему сборки назвали GNU Build System, поскольку она «выросла» из проекта GNU. Однако система используется множеством других пакетов, поскольку единые соглашения обеспечивают ряд преимуществ.

Пакет Autotools включает набор инструментов создания системы сборки GNU для конкретного пакета. Программа Autoconf работает в основном с configure, а Automake — с файлами Makefile. Систему сборки GNU можно создать и без этих инструментов, однако это довольно обременительно и часто ведет к ошибкам.

2.2 Примеры использования GNU Build System

Здесь рассмотрено несколько примеров использования системы сборки GNU. Эти примеры можно выполнить самостоятельно, воспользовавшись файлом amhello-1.0.tar.gz из состава Automake. При наличии Automake в системе этот файл будет в каталоге PREFIX/share/doc/automake/, где PREFIX указывает путь установки, заданный в конфигурации (по умолчанию /usr/local, но Automake в большинстве дистрибутивов GNU/Linux устанавливается в /usr). Если пакет Automake не установлен в системе, можно найти копию файла в каталоге doc/ пакета Automake.

В части примеров представлены расширения GNU Build System, включенные в систему сборки, создаваемую Autotools.

2.2.1. Базовая установка

Базовая процедура установки имеет вид

    ~ % tar zxf amhello-1.0.tar.gz 
    ~ % cd amhello-1.0 
    ~/amhello-1.0 % ./configure 
    ... 
    config.status: creating Makefile 
    config.status: creating src/Makefile 
    ... 
    ~/amhello-1.0 % make 
    ... 
    ~/amhello-1.0 % make check 
    ... 
    ~/amhello-1.0 % su 
    Password: 
    /home/adl/amhello-1.0 # make install 
    ... 
    /home/adl/amhello-1.0 # exit 
    ~/amhello-1.0 % make installcheck 
    ...

Сначала пользователь распаковывает архив. Здесь и далее для простоты используется команда tar zxf, применимая не во всех системах. В системах без GNU tar взамен следует применять команду gunzip -c amhello-1.0.tar.gz | tar xf -.

Затем пользователь переходит в созданный каталог для запуска сценария configure, которые проверяет различные переменные системы и создает файлы Makefile. В этом примере создается два Makefile, но в реальности такие файлы обычно создаются в каждом каталоге пакета.

После этого можно запускать make. Эта команда будет собирать все программы, библиотеки и сценарии для пакета., размещая файлы в заданные места дерева исходного кода. В примере компилируется программа hello.

Команда make check запускает тесты для собранного пакета. Этот этап не обязателен, но зачастую имеет смысл проверить, как ведет себя программа, еще до ее установки. В приведенном примере make check ничего не делает.

После сборки и тестирования пакета можно установить его в системе. Установка включает копирование программ, библиотек, заголовочных файлов, сценариев и других данных в нужные каталоги системы. Для этого служит команда make install. По умолчанию все будет устанавливаться в каталог /usr/local — исполняемые файлы в /usr/local/bin, библиотеки — в /usr/local/lib и т. д. Обычно эти каталоги не доступны для рядового пользователя, поэтому нужно получить полномочия root. В примере команда make install будет копировать программу hello в /usr/local/bin и файл README — в /usr/local/share/doc/amhello.

Последней операцией является проверка установки с помощью команды make installcheck, которая может запускать тесты для установленных файлов (make check выполняет тесты в дереве исходных кодов, а make installcheck — в реальной системе). Эти тесты могут отличаться от предшествующих, например, могут включать проверки, которые невозможны в дереве кода. В тестах могут также использоваться иные файлы, нежели при команде make check. Тесты позволяют проверить полноту и корректность установки.

В настоящее время далеко не все пакеты включают тесты installcheck, поэтому проверку используют редко.

2.2.2. Стандартные цели Makefile

До этого были рассмотрены 4 способа применения команды make в системе сборки GNU — make, make check, make install и make installcheck. Слова check, install и installcheck, передаваемые в качестве аргумента команды make, называют целями (target). Команда make является сокращением make all, а цель all применяется по умолчанию в GNU Build System. Ниже приведен список наиболее распространенных целей, указанных в GNU Coding Standards.

make all

Собирает программы, библиотеки, документацию и т. п. (совпадает с make).

make install

Устанавливает все нужное, копируя файлы из дерева кода в системные каталоги.

make install-strip

Делает то же, что make install, а затем вырезает отладочные символы (они могут пригодиться при диагностике).

make uninstall

Противоположна make install и удаляет установленные файлы (должна быть запущена из того же дерева, которое служило для установки).

make clean

Удаляет из дерева сборки файлы, созданные командой make all.

make distclean

Удаляет также файл ./configure.

make check

Запускает тесты, если они имеются.

make installcheck

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

make dist

Заново создает архив PACKAGE-VERSION.tar.gz из файлов исходного кода.

2.2.3. Переменные для стандартных каталогов

Стандарт кодирования GNU задает также иерархию переменных для указания каталогов установки.

Переменная

Значение по умолчанию

prefix

/usr/local

exec_prefix

${prefix}

bindir

${exec_prefix}/bin

libdir

${exec_prefix}/lib

includedir

${prefix}/include

datarootdir

${prefix}/share

datadir

${datarootdir}

mandir

${datarootdir}/man

infodir

${datarootdir}/info

docdir

${datarootdir}/doc/${PACKAGE}

Роли большинства этих каталогов понятны из имен. В пакетах каждый из устанавливаемых файлов помещается в один из таких каталогов. Например, в пакете amhello-1.0 программа hello помещается в BINDIR — каталог для исполняемых файлов. По умолчанию это каталог /usr/local/bin, но пользователь может указать иное имя при вызове сценария configure. Файл README будет помещен в каталог DOCDIR (по умолчанию /usr/local/share/doc/amhello).

Пользователь может изменить каталоги установки, как показано ниже.

    ~/amhello-1.0 % ./configure --prefix ~/usr 
    ... 
    ~/amhello-1.0 % make 
    ... 
    ~/amhello-1.0 % make install

Это приведет к установке ~/usr/bin/hello и ~/usr/share/doc/amhello/README.

Список переменных для каталогов можно получить с помощью команды ./configure —help.

2.2.4. Стандартные переменные конфигурации

Стандарт кодирования GNU определяет также список конфигурационных переменных, используемых при сборке.

CC

команда компилятора C.

CFLAGS

флаги компилятора C.

CXX

команда компилятора C++.

CXXFLAGS

флаги компилятора C++.

LDFLAGS

флаги компоновщика.

CPPFLAGS

флаги препроцессора C/C++.

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

Ниже показано, как можно использовать configure для выбора компилятора gcc-3, использования заголовочных файлов ~/usr/include при компиляции и библиотек ~/usr/lib — при сборке.

    ~/amhello-1.0 % ./configure --prefix ~/usr CC=gcc-3 \ 
    CPPFLAGS=-I$HOME/usr/include LDFLAGS=-L$HOME/usr/lib

Список переменных конфигурации можно получить с помощью команды ./configure —help.

2.2.5. Переопределение принятых по умолчанию значений в config.site

При установке нескольких пакетов с общими параметрами удобно создать файл для хранения таких настроек. Если в системе существует файл PREFIX/share/config.site, сценарий configure будет использовать его (команда source).

Воспользуемся приведенной ниже командой настройки конфигурации

    ~/amhello-1.0 % ./configure --prefix ~/usr CC=gcc-3 \ 
    CPPFLAGS=-I$HOME/usr/include LDFLAGS=-L$HOME/usr/lib

В предположении установки множества пакетов в ~/usr и использования общих определений CC, CPPFLAGS и LDFLAGS, можно автоматизировать процесс, создав файл ~/usr/share/config.site вида

    test -z "$CC" && CC=gcc-3 
    test -z "$CPPFLAGS" && CPPFLAGS=-I$HOME/usr/include 
    test -z "$LDFLAGS" && LDFLAGS=-L$HOME/usr/lib

После этого сценарий configure при каждом использовании префикса ~/usr будет использовать приведенный выше файл config.site и задавать указанные в нем переменные.

    ~/amhello-1.0 % ./configure --prefix ~/usr 
    configure: loading site script /home/adl/usr/share/config.site 
    ...

2.2.6. Параллельная сборка (VPATH)

Система сборки GNU различает деревья исходного кода и сборки. Корнем дерева кодов является каталог, где находится сценарий configure. Здесь хранятся файлы кода, которые могут быть размещены в разных подкаталогах.

Деревом сборки является каталог, из которого запускается сценарий configure. В этом каталоге размещаются все объектные файлы, программы, библиотеки и другие файлы, собранные из исходного кода. В дереве сборки обычно используются подкаталоги, автоматически создаваемые системой сборки.

Если configure выполняется из своего каталога, деревья исходного кода и сборки объединяются как в показанном выше примере сборки. Но можно эти деревья разделить, как показано ниже.

    ~ % tar zxf ~/amhello-1.0.tar.gz 
    ~ % cd amhello-1.0 
    ~/amhello-1.0 % mkdir build && cd build 
    ~/amhello-1.0/build % ../configure 
    ... 
    ~/amhello-1.0/build % make 
    ...

Такие конфигурации часто называют деревом параллельной сборки (parallel builds или VPATH builds), хотя этот термин может вводить в заблуждение. Поэтому дальше будет применяться термин сборка VPATH2. Такой вариант позволяет использовать одно дерево исходных кодов для сборки разных конфигураций. Например,

    ~ % tar zxf ~/amhello-1.0.tar.gz
    ~ % cd amhello-1.0 
    ~/amhello-1.0 % mkdir debug optim && cd debug 
    ~/amhello-1.0/debug % ../configure CFLAGS='-g -O0' 
    ... 
    ~/amhello-1.0/debug % make 
    ... 
    ~/amhello-1.0/debug % cd ../optim 
    ~/amhello-1.0/optim % ../configure CFLAGS='-O3 -fomit-frame-pointer' 
    ... 
    ~/amhello-1.0/optim % make 
    ...

На сетевых файловых системах может применяться похожая модель для сборки на разных машинах. Например, исходные коды могут размещаться в каталоге, доступном хостам HOST1 и HOST2, которые могут использовать разные платформы.

    ~ % cd /nfs/src 
    /nfs/src % tar zxf ~/amhello-1.0.tar.gz

на первом хосте можно создать локальный каталог сборки build, как показано ниже.

    [HOST1] ~ % mkdir /tmp/amh && cd /tmp/amh 
    [HOST1] /tmp/amh % /nfs/src/amhello-1.0/configure 
    ... 
    [HOST1] /tmp/amh % make && sudo make install 
    ...

Здесь предполагается, что имя устанавливающего программы указано в конфигурации sudo, что позволяет ему использовать команду make install с правами root (это удобней использования su, описанного выше).

На втором хосте можно поступить также (может быть даже в то же самое время).

    [HOST2] ~ % mkdir /tmp/amh && cd /tmp/amh 
    [HOST2] /tmp/amh % /nfs/src/amhello-1.0/configure 
    ... 
    [HOST2] /tmp/amh % make && sudo make install 
    ...

В этом случае ничто не препятствует сделать каталог /nfs/src/amhello-1.0 доступным лишь для чтения. Фактически сборки VPATH позволяет работать даже с исходными кодами, размещенными на недоступных для записи средах, например, CD-ROM.

2.2.7. Раздельная установка

В предыдущем примере два хоста использовали общее дерево исходных кодов, но компиляция и установка выполнялись на каждом хосте раздельно. Система сборки GNU поддерживает также сетевые варианты, где часть устанавливаемых файлов совместно используется несколькими хостами. Это достигается путем разделения зависимых и независимых от архитектуры файлов и создания двух целей Makefile для установки этих компонент. Этими целями служат install-exec для зависимых от архитектуры файлов и install-data — для остальных. Использованная выше команда make install может считаться комбинацией make install-exec install-data.

С точки зрения системы сборки GNU различие между зависимыми и независимыми от архитектуры файлами основано лишь на переменной каталога, указанного для установки. В представленном выше списке переменные, основанные на EXEC-PREFIX, указывают зависимые от архитектуры компоненты, чьи файлы устанавливаются командой make install-exec. Другие каталоги относятся к архитектурно-независимым компонентам, устанавливаемым make install-data.

Ниже показано, как можно было разделить установку для двух хостов с размещением пакета непосредственно в /usr и использованием общего каталога /usr/share. На первом хосте можно использовать команды

    [HOST1] ~ % mkdir /tmp/amh && cd /tmp/amh 
    [HOST1] /tmp/amh % /nfs/src/amhello-1.0/configure --prefix /usr 
    ... 
    [HOST1] /tmp/amh % make && sudo make install 
    ...

На втором хосте достаточно установить лишь зависимые от архитектуры файлы, как показано ниже.

    [HOST2] ~ % mkdir /tmp/amh && cd /tmp/amh 
    [HOST2] /tmp/amh % /nfs/src/amhello-1.0/configure --prefix /usr 
    ... 
    [HOST2] /tmp/amh % make && sudo make install-exec 
    ...

В пакетах с проверкой установки имеет смысл команда make installcheck для тестирования корректности инсталляции.

2.2.8. Кросс-компиляция

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

—build=BUILD

Система, на которой собирается пакет.

—host=HOST

Система, где будут работать собранные программы и библиотеки.

При указании опции —host сценарий configure будет искать набор инструментов кросс-компиляции для этой платформы. Обычно в именах таких инструментов название целевой платформы служит префиксом. Например, кросс-инструменты для MinGW32 могут называться i586-mingw32msvc-gcc, i586-mingw32msvc-ld, i586-mingw32msvc-as и т. п.

Ниже приведен пример сборки amhello-1.0 для i586-mingw32msvc на компьютере GNU/Linux.

    ~/amhello-1.0 % ./configure --build i686-pc-linux-gnu --host i586-mingw32msvc 
    checking for a BSD-compatible install... /usr/bin/install -c 
    checking whether build environment is sane... yes 
    checking for gawk... gawk 
    checking whether make sets $(MAKE)... yes 
    checking for i586-mingw32msvc-strip... i586-mingw32msvc-strip 
    checking for i586-mingw32msvc-gcc... i586-mingw32msvc-gcc 
    checking for C compiler default output file name... a.exe 
    checking whether the C compiler works... yes 
    checking whether we are cross compiling... yes 
    checking for suffix of executables... .exe 
    checking for suffix of object files... o 
    checking whether we are using the GNU C compiler... yes 
    checking whether i586-mingw32msvc-gcc accepts -g... yes 
    checking for i586-mingw32msvc-gcc option to accept ANSI C... 
    ... 
    ~/amhello-1.0 % make 
    ... 
    ~/amhello-1.0 % cd src; file hello.exe 
    hello.exe: MS Windows PE 32-bit Intel 80386 console executable not relocatable

Обычно при кросс-компиляции требуется указать опции —host и —build. Единственным исключением является случай сборки кросс-компилятора, где должна использоваться еще одна опция.

—target=TARGET

При сборке инструментов кросс-компиляции задает систему, для которой будет предназначен инструмент. Например, при установке GCC (GNU Compiler Collection) можно использовать —target=TARGET для указания сборки GCC как кросс-компилятора для TARGET. Комбинация —build и —target указывает кросс-компиляцию кросс-компилятора. Обычно это называют канадской кросс-компиляцией (Canadian cross).

2.2.9. Переименование программ при установке

Система сборки GNU позволяет автоматически переименовывать исполняемые файлы и страницы man перед их установкой. Это особенно удобно при установке пакетов GNU в системы, где уже есть иная реализация пакета, которую нужно сохранить. Например, можно установить GNU tar как gtar, если в системе уже имеется tar. Для решения этой задачи имеется три опции.

—program-prefix=PREFIX

Добавляет префикс PREFIX к именам устанавливаемых программ.

—program-suffix=SUFFIX

Добавляет суффикс SUFFIX к именам устанавливаемых программ.

—program-transform-name=PROGRAM

Запускает команду sed PROGRAM для имен установленных программ.

Приведенные ниже команды будут устанавливать hello как /usr/local/bin/test-hello.

    ~/amhello-1.0 % ./configure --program-prefix test- 
    ... 
    ~/amhello-1.0 % make 
    ... 
    ~/amhello-1.0 % sudo make install 
    ...

2.2.10. Сборка двоичных пакетов с использованием DESTDIR

В системе сборки GNU команды make install и make uninstall не вполне соответствуют потребностям системных администраторов, устанавливающих или обновляющих пакеты на многочисленных хостах. Словом, GNU Build System не является заменой менеджеру пакетов. Таким менеджерам нужно знать, какие файлы устанавливаются пакетом, поэтому make install им не подходит.

Можно использовать переменную DESTDIR для поэтапной установки. Пакет следует настраивать для установки в нужное место (например, —prefix /usr), но при запуске make install указывать в переменной DESTDIR абсолютное имя каталога для копирования устанавливаемых файлов. В этом каталоге можно легко просмотреть установленные файлы, а затем скопировать в нужное место. Например, можно создать двоичный пакет с «моментальным» снимком (snapshot) всех устанавливаемых файлов.

    ~/amhello-1.0 % ./configure --prefix /usr 
    ... 
    ~/amhello-1.0 % make 
    ... 
    ~/amhello-1.0 % make DESTDIR=$HOME/inst install 
    ... 
    ~/amhello-1.0 % cd ~/inst 
    ~/inst % find . -type f -print > ../files.lst 
    ~/inst % tar zcvf ~/amhello-1.0-i686.tar.gz `cat ../files.lst` 
    ./usr/bin/hello 
    ./usr/share/doc/amhello/README

После этого файл amhello-1.0-i686.tar.gz будет готов для распаковки в корневой каталог (/) на разных хостах. Использование `cat ../files.lst` вместо . в качестве аргумента tar предотвращает изменение подкаталогов (не нужно менять время изменения каталогов /, /usr/ и т. п.).

Отметим, что при сборке пакетов для нескольких архитектур может потребоваться использование команд make install-data и make install-exec (2.2.7. Раздельная установка).

2.2.11. Подготовка дистрибутива

Выше уже была упомянута команда make dist, позволяющая собрать все нужные исходные файлы и части системы сборки для создания архива PACKAGE-VERSION.tar.gz. Более полезной командой является make distcheck, создающая архив PACKAGE-VERSION.tar.gz как dist, но обеспечивающая дополнительные операции.

  • попытка полной компиляции пакета, распаковки недавно созданных архивов, запуск команд make, make check, make install, а также make installcheck и make dist;

  • тестирование сборки VPATH для доступных лишь на чтение исходных кодов (2.2.6. Параллельная сборка (VPATH));

  • гарантирует, что команды make clean, make distclean и make uninstall не пропустят файлов;

  • проверка установки с DESTDIR (2.2.10. Сборка двоичных пакетов с использованием DESTDIR).

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

Выпуск пакета с отказом make distcheck означает, что какое-то из перечисленных выше действий не работает. Поэтому хорошим тоном является выпуск пакетов лишь после успешного прохождения make distcheck. Это не говорит о безупречности пакета, но позволяет надеяться на меньшее число ошибок.

2.2.12. Автоматическая проверка зависимостей

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

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

    ~/amhello-1.0 % ./configure --prefix /usr 
    ... 
    checking dependency style of gcc... gcc3 
    ...

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

—disable-dependency-tracking

Ускоряет первоначальную сборку.

Некоторые компиляторы не предлагают практического способа получить список зависимостей в качестве побочного результата компиляции и требуется отдельный запуск (или другой инструмент) для отслеживания зависимостей. Снижение производительности за счет этого достаточно велико, чтобы по умолчанию эти методы были отключены. При необходимости их можно включить опцией —enable-dependency-tracking сценария configure.

—enable-dependency-tracking

Не отключать медленное отслеживание зависимостей.

2.2.13. Вложенные пакеты

Автоматическая настройка Autotools может работать с пакетами, содержащими в себе другие пакеты, настраиваемые таким же способом. Предположим, что пакет A содержит библиотеку в одном из своих каталогов и эта библиотека B является полным пакетом со своей системой сборки GNU. Сценарий configure пакета A будет запускать configure пакета B в процессе своей работы. В результате будут собраны и установлены оба пакета A и B.

Таким способом можно собрать несколько пакетов в один прием. В GCC это используется широко, позволяя установщикам настраивать, собирать и устанавливать множество пакетов сразу, а разработчикам — независимо создавать такие пакеты.

При настройке вложенных пакетов опции configure верхнего уровня рекурсивно передаются вложенным сценариям configure. Если пакет не понимает ту или иную опцию, он просто игнорирует ее, предполагая, что она предназначена для другого пакета.

Опции, поддерживаемые всеми включенными пакетами можно увидеть по команде configure —help=recursive.

2.3. Пакет Autotools

Пакет GNU Autotools выполняет значительную часть работы, требуемой для настройки и сборки пакета, создавая переносимую, полную и самодостаточную систему сборки GNU с помощью простых инструментов. Самодостаточность означает, что подготовленной системе сборки уже не требуется GNU Autotools.

Однако в некоторых случаях использовать Autotools не удастся (например, при использовании несовместимой системы сборки). Инструменты Autotools будут работать лишь при поддержке GNU Build System.

2.4. Сборка небольшого пакета

В этом разделе описано создание пакета amhello-1.0 с нуля.

2.4.1. Создание amhello-1.0.tar.gz

Рассмотрим процесс создания архива amhello-1.0.tar.gz. Пакет достаточно прост и включает лишь 5 файлов. Если не хочется набирать текст этих файлов, можно воспользоваться архивом amhello-1.0.tar.gz из пакета Automake.

  • Файл src/main.c содержит код программы hello. Он помещен в каталог src/, поскольку по мере создания проекта будут организованы каталоги man/ для руководства, data/ — для данных и т. п.

             ~/amhello % cat src/main.c 
             #include <config.h> 
             #include <stdio.h> 
    
             int 
             main (void) 
             { 
               puts ("Hello World!"); 
               puts ("This is " PACKAGE_STRING "."); 
               return 0; 
             }
  • Файл README содержит краткое описание нашего небольшого пакета.

             ~/amhello % cat README 
             This is a demonstration package for GNU Automake. 
             Type 'info Automake' to read the Automake manual.
  • Файлы Makefile.am и src/Makefile.am содержат инструкции Automake для двух каталогов.

             ~/amhello % cat src/Makefile.am 
             bin_PROGRAMS = hello 
             hello_SOURCES = main.c 
             ~/amhello % cat Makefile.am 
             SUBDIRS = src 
             dist_doc_DATA = README
  • Файл configure.ac содержит инструкции Autoconf для создания сценария configure.

             ~/amhello % cat configure.ac 
             AC_INIT([amhello], [1.0], [bug-automake@gnu.org])
             AM_INIT_AUTOMAKE([-Wall -Werror foreign]) 
             AC_PROG_CC 
             AC_CONFIG_HEADERS([config.h]) 
             AC_CONFIG_FILES([ 
              Makefile 
              src/Makefile 
             ]) 
             AC_OUTPUT

Когда эти 5 файлов созданы, можно запустить Autotools для организации системы сборки. Для этого служит команда autoreconf.

    ~/amhello % autoreconf --install 
    configure.ac: installing './install-sh' 
    configure.ac: installing './missing' 
    configure.ac: installing './compile' 
    src/Makefile.am: installing './depcomp'

На этом создание системы сборки завершается.

В дополнение к трем указанным в выводе autoreconf сценариям будут созданы файлы configure, config.h.in, Makefile.in и src/Makefile.in. Три последних файла являются шаблонами, на основе которых сценарий configure создаст файлы config.h, Makefile и src/Makefile. Запустим этот сценарий.

    ~/amhello % ./configure 
    checking for a BSD-compatible install... /usr/bin/install -c 
    checking whether build environment is sane... yes 
    checking for gawk... no 
    checking for mawk... mawk 
    checking whether make sets $(MAKE)... yes 
    checking for gcc... gcc 
    checking for C compiler default output file name... a.out 
    checking whether the C compiler works... yes 
    checking whether we are cross compiling... no 
    checking for suffix of executables... 
    checking for suffix of object files... o 
    checking whether we are using the GNU C compiler... yes 
    checking whether gcc accepts -g... yes 
    checking for gcc option to accept ISO C89... none needed 
    checking for style of include used by make... GNU 
    checking dependency style of gcc... gcc3 
    configure: creating ./config.status 
    config.status: creating Makefile 
    config.status: creating src/Makefile
    config.status: creating config.h 
    config.status: executing depfiles commands

Файлы Makefile, src/Makefile и config.h созданы и можно начать сборку. Например,

    ~/amhello % make 
    ... 
    ~/amhello % src/hello 
    Hello World! 
    This is amhello 1.0. 
    ~/amhello % make distcheck 
    ... 
    ============================================= 
    amhello-1.0 archives ready for distribution: 
    amhello-1.0.tar.gz 
    =============================================

Отметим, что команда autoreconf нужна лишь при отсутствии GNU Build System. Если позднее изменить инструкции в Makefile.am или configure.ac, соответствующие части системы сборки будут автоматически обновлены при выполнении команды make.

Сценарий autoreconf вызывает autoconf, automake и выполняет множество других команд в нужном порядке. Команда autoconf создает файл configure на основе configure.ac, а automake — файлы Makefile.in из Makefile.am и configure.ac.

2.4.2. Файл configure.ac

Рассмотрим содержимое файла configure.ac.

    AC_INIT([amhello], [1.0], [bug-automake@gnu.org]) 
    AM_INIT_AUTOMAKE([-Wall -Werror foreign]) 
    AC_PROG_CC 
    AC_CONFIG_HEADERS([config.h]) 
    AC_CONFIG_FILES([ 
     Makefile 
     src/Makefile 
    ]) 
    AC_OUTPUT

Этот файл читает команда autoconf (для создания configure) и automake (для создания файлов Makefile.in). В файле содержится последовательность макросов M4, которые преобразуются в код оболочки (shell) для сценария configure. Синтаксис файла описан в руководстве Autoconf.

Макросы с префиксом AC_ относятся к Autoconf, а с AM_ — к Automake и описаны в соответствующих руководствах.

Первые две строки configure.ac инициализируют Autoconf и Automake. Макрос AC_INIT принимает в качестве параметров имя пакета, номер версии и адрес для отправки сообщений об ошибках (этот адрес выводится в конце по команде ./configure —help). При создании своих пакетов следует указывать корректный адрес. Аргументом AM_INIT_AUTOMAKE служит список опций automake (17. Смена поведения Automake). Опции -Wall и -Werror запрашивают у automake вывод всех предупреждений как ошибок. Речь идет о предупреждениях Automake, таких как сомнительные инструкции в Makefile.am. Это не имеет отношения к вызовам компилятора, даже если он поддерживает опции с такими именами. Использование -Wall -Werror безопасно на начальном этапе работы с пакетом и позволяет увидеть все проблемы. Позднее можно сузить вывод предупреждений. Опция foreign говорит Automake, что пакет не следует стандартам GNU. В пакеты GNU всегда следует включать такие файлы как ChangeLog, AUTHORS и т. п. Наличие этой опции указывает automake, что не следует выводить предупреждений об отсутствии этих файлов.

Строка AC_PROG_CC заставляет сценарий configure найти компилятор C и указать в переменной CC его имя. Файл src/Makefile.in, созданный Automake, использует переменную CC для сборки hello. Когда сценарий configure создает файл src/Makefile из src/Makefile.in, он будет включать в файл найденное значение CC. Если у Automake запрошено создание файла Makefile.in, использующего CC, но в configure.ac не определена эта строка, будет предложено добавить вызов AC_PROG_CC.

Вызов AC_CONFIG_HEADERS([config.h]) заставляет сценарий configure создать файл config.h, собирая операторы #define, заданные другими макросами в configure.ac. В нашем случае некоторые из них уже определены в макросе AC_INIT. Ниже приведен фрагмент файла config.h после работы сценария configure.

    ... 
    /* Define to the address where bug reports for this package should be sent. */ 
    #define PACKAGE_BUGREPORT "bug-automake@gnu.org" 

    /* Define to the full name and version of this package. */ 
    #define PACKAGE_STRING "amhello 1.0" 
    ...

Файл src/main.c включает config.h и поэтому может использовать PACKAGE_STRING. В реальных проектах файл config.h может быть большим с операторами #define для каждого свойства, проверенного в системе.

Макрос AC_CONFIG_FILES объявляет список файлов, которые должен создать сценарий configure по шаблонам *.in. Automake также просматривает этот список в поиске файлов Makefile.am, которые нужно обработать. Об этом важно помнить при добавлении каталогов в проект, указывая в списке соответствующие файлы Makefile.

Строка AC_OUTPUT является завершающей командой, которая фактически отвечает за создание файлов, зарегистрированных в AC_CONFIG_HEADERS и AC_CONFIG_FILES.

В новых проектах можно начать с этого простого файла configure.ac и добавить в него другие нужные проверки. Команда autoscan может предложить некоторые проверки, которые могут потребоваться для пакета.

2.4.3. Файл Makefile.am

Файл src/Makefile.am содержит инструкции Automake для сборки и установки программы hello.

    bin_PROGRAMS = hello 
    hello_SOURCES = main.c

Синтаксис Makefile.am не отличается от синтаксиса Makefile. Когда automake обрабатывает Makefile.am этот файл целиком копируется в выходной файл Makefile.in, на основе которого затем создается файл Makefile сценарием configure. При этом для некоторых определений переменных создаются правила сборки и другие переменные. Зачастую файлы Makefile.am содержат лишь список переменных, показанных выше, но могут включать также другие определения переменных и правил, которые automake будет пропускать без обработки.

Переменные с суффиксом _PROGRAMS перечисляют программы, которые следует собрать с помощью финального файла Makefile. На языке Automake переменная _PROGRAMS называется первичной (primary). Другими первичными переменными являются _SCRIPTS, _DATA, _LIBRARIES и т. п., соответствующие разным типам файлов.

Префикс bin в bin_PROGRAMS говорит automake, что полученную программу следует установить в BINDIR. Система сборки GNU использует набор переменных для указания целевых каталогов и позволяет пользователям управлять ими (2.2.3. Переменные для стандартных каталогов). Любую из таких переменных можно указать в качестве префикса для primary (без dir в конце), чтобы сказать automake, куда нужно поместить файлы.

Программы собираются из файлов исходного кода, поэтому для каждой программы PROG, указанной в переменной _PROGRAMS, automake будет искать другую переменную с именем PROG_SOURCES, указывающую исходные файлы (их может быть много для последующей компиляции и компоновки).

Automake также знает, что исходные файлы нужно включить в создаваемый архив (в отличие от сборки). Поэтому побочным результатом объявления hello_SOURCES является включение файла main.c в архив по команде make dist.

На верхнем уровне файл Makefile.am включает дополнительные определения.

    SUBDIRS = src
    dist_doc_DATA = README

Специальная переменная SUBDIRS указывает все каталоге, которые команде make следует просмотреть перед обработкой текущего каталога. Таким образом, эта строка отвечает за сборку src/hello даже при запуске make из каталога верхнего уровня. Она также заставляет команду make install установить src/hello перед установкой README.

Строка dist_doc_DATA = README включает распространение файла README и его установку в DOCDIR. Файлы, указанные в _DATA, не становятся автоматически частью архива, создаваемого командой make dist, поэтому для их распространения используется префикс dist_. Однако для файла README это не требуется и automake будет автоматически распространять файл README (список автоматически распространяемых файлов можно увидеть по команде automake —help).

3. Основные идеи

3.1. Базовые операции

Automake читает файлы Makefile.am и создает Makefile.in. Некоторые переменные и правила из Makefile.am указывают Automake генерировать более специализированный код. Например, определение bin_PROGRAMS ведет к генерации правил для компиляции и компоновки программ.

Определения переменных и правила в основном копируются дословно из Makefile.am в создаваемый файл и определения переменных помещаются до правил. Это позволяет добавлять практически любой код в создаваемый файл Makefile.in. Например, дистрибутив Automake включает нестандартное правило для цели git-dist, которую сопровождающий Automake использует для создания дистрибутивов из системы контроля версий.

Отметим, что большинство расширений GNU make не распознается Automake и использование таких расширений в Makefile.am ведет к ошибкам или путаному поведению. Особым исключением является поддержка оператора добавления в конце +=, который добавляет указанный справа аргумент в конец заданной слева переменной. Automake транслирует этот оператор в обычный оператор =, поэтому += работает с любой программой make.

Automake пытается сохранить комментарии, сгруппированные с примыкающими правилами или определениями переменных.

В общем случае Automake не очень хорошо разбирает необычные конструкции Makefile, поэтому рекомендуется избегать таких конструкций, а также «творческого» использования пробелов. Например, символы <TAB> не могут помещаться между именем цели и следующим символом :, а в назначениях переменных не следует применять <TAB> для отступа. Использование сложных макросов в именах целей может создавать проблемы. Например,

    % cat Makefile.am 
    $(FOO:=x): bar 
    % automake 
    Makefile.am:1: bad characters in variable name '$(FOO' 
    Makefile.am:1: ':='-style assignments are not portable

Правило в Makefile.am обычно переопределяет правило с похожим именем, автоматически созданное программой automake. Хотя это поддерживаемая функция, ее лучше избегать, поскольку иногда сгенерированные правила очень специфичны..

Точно так же переменная, определенная в Makefile.am или подставленная AC_SUBST из configure.ac переопределяет любую переменную, которую обычно создает automake. Эта функция более полезна, чем возможность переопределения правила. Следует помнить, что многие переменные, созданные automake, предназначены лишь для внутреннего использования и их имена могут измениться в будущих выпусках.

При проверке определения переменной Automake рекурсивно проверяет упомянутые в определении переменные. Например, увидев содержимое foo_SOURCES в приведенном фрагменте

    xs = a.c b.c 
    foo_SOURCES = c.c $(xs)

Automake будет использовать файлы a.c, b.c и c.c как содержимое foo_SOURCES.

Automake также допускает форму комментария, которая не копируется на выход. Строки, начинающиеся с символов ## (допускаются пробелы в начале), Automake полностью игнорирует. Первая строка Makefile.am обычно содержит текст

    ## Process this file with automake to produce Makefile.in

3.2. Строгость

Хотя программа Automake предназначена для сопровождающих пакеты GNU, она прилагает усилия по адаптации к требованиям тех, кто хочет ее использовать, но не готов следовать всем соглашениям GNU. Для этого в Automake поддерживается три уровня строгости, определяющие уровень проверки в Automake соответствия стандартам.

foreign

Automake проверяет лишь те элементы, которые абсолютно необходимы для корректной работы. Например, стандарты GNU требуют наличия файла NEWS, в этом режиме файл не проверяется. На этом уровне также отключены некоторые предупреждения, используемые по умолчанию (например, по части переносимости). Название режима показывает, что он относится к программам, не совсем соответствующим стандартам GNU.

gnu

Automake будет по возможности проверять соответствие стандартам GNU. Режим принят по умолчанию.

gnits

Automake проверяет соответствие еще не написанным «стандартам Gnits», основанным на стандартах GNU, но более детальных. Рекомендуется не использовать этот режим, не будучи разработчиком стандартов Gnits, пока стандарт не опубликован.

3.3. Схема именования

Переменные Automake обычно следуют схеме унифицированного именования (uniform naming scheme), которая упрощает решение вопросов о сборке и установке программ (и производных элементов). Эта схема также позволяет определить время запуска configure для выбора собираемых компонент.

Во время работы make некоторые переменные используются для определения объектов, которые нужно собрать. Имена переменных собираются из нескольких частей путем конкатенации. Часть, информирующую automake о том, что нужно собирать, называют первичной (primary). Например, первичная переменная PROGRAMS содержит список программ, которые нужно компилировать и компоновать.

Для определения мест установки применяется другой набор имен, которые служат префиксами для primary и указывают стандартные каталоги (имена их заданы в стандартах GNU — 2.2.3. Переменные для стандартных каталогов).  Automake расширяет этот список переменными pkgdatadir, pkgincludedir, pkglibdir и pkglibexecdir, в которые добавлен суффикс $(PACKAGE). Например, pkglibdir определяется как $(libdir)/$(PACKAGE).

Для каждого первичного имени имеется дополнительная переменная с префиксом EXTRA_, которая служит для перечисления объектов, сборка которых не обязательная и определяется сценарием configure. Эти переменные нужны , поскольку Automake требуется заранее знать список объектов, которые могут быть собраны, для генерации файла Makefile.in, который будет работать в всех случаях. Например, cpio выбирает собираемые программы во время выполнения configure. Некоторые программы устанавливаются в bindir, а другие — в sbindir, как показано ниже.

    EXTRA_PROGRAMS = mt rmt 
    bin_PROGRAMS = cpio pax 
    sbin_PROGRAMS = $(MORE_PROGRAMS)

Указание первичной переменной без префикса (например, PROGRAMS) приведет к ошибке. Отметим, что базовый суффикс dir не включается в имена, т. е. используется переменная bin_PROGRAMS, а не bindir_PROGRAMS.

К каждом из каталогов можно размещать лишь определенные объекты. Automake будет фиксировать попытки некорректного размещения и сообщать об ошибках (однако имеются способы обхода этого запрета). Кроме того, Automake замечает опечатки в именах стандартных каталогов.

Иногда стандартных каталогов недостаточно, даже с расширениями Automake. В частности, иногда нужно поместить файлы в подкаталог определенного стандартного каталога. Для этого Automake позволяет расширять список возможных каталогов установки. Заданный префикс (например, zar) будет действительным, если определена переменная с таким именем и суффиксом dir (например, zardir). Ниже приведен пример установки файла file.xml в каталог $(datadir)/xml.

    xmldir = $(datadir)/xml 
    xml_DATA = file.xml

Это свойство можно использовать для переопределения проверок работоспособности, выполняемых Automake для обнаружения подозрительных каталогов и привязок. Например Automake может возвращать ошибку, показанную ниже.

    # Forbidden directory combinations, automake will error out on this. 
    pkglib_PROGRAMS = foo 
    doc_LIBRARIES = libquux.a

Но эта ошибка исчезнет в приведенном ниже варианте.

    # Work around forbidden directory combinations. Do not use this 
    # without a very good reason! 
    my_execbindir = $(pkglibdir) 
    my_doclibdir = $(docdir) 
    my_execbin_PROGRAMS = foo 
    my_doclib_LIBRARIES = libquux.a

Подстрока exec в переменной my_execbindir позволяет установить файлы в нужное время (2.2.7. Раздельная установка). Специальный префикс noinst_ указывает, что объект следует собрать, но не нужно устанавливать. Обычно это применяется для объектов, которые нужны для сборки остальной части пакета (например, библиотеки или вспомогательного сценария). Специальный префикс check_ указывает, что объекты не следует собирать, пока не используется команда make check. Такие объекты не устанавливаются совсем.

К основным именам переменных относятся PROGRAMS, LIBRARIES, LTLIBRARIES, LISP, PYTHON, JAVA, SCRIPTS, DATA, HEADERS, MANS и TEXINFOS. Некоторые из этих имен допускают префиксы, управляющие другими аспектами поведения automake. К таким префиксам относятся dist_, nodist_, nobase_ и notrans_ (8.4. Переменные для программ и библиотек).

3.4. Ограничение размера команд

В большинстве unix-подобных систем размер аргументов команды и содержимого окружения при создании новых процессов ограничены (см., например, http://www.in-ulm.de/~mascheck/various/argmax/), что применимо и к командам, выполняемым make. POSIX требует, чтобы предельный размер составлял не менее 4096 байтов и в большинстве современных систем предельный размер достаточно велик или неограничен.

Для создания переносимых файлов Makefile, в которых не нарушается это ограничение, нужно ограничивать размер списков. К сожалению это не совсем прозрачно в Automake и могут потребоваться дополнительные действия. Обычно для этого достаточно вручную разделять списки и использовать для каждого свое имя каталога установки. Например,

    data_DATA = file1 ... fileN fileN+1 ... file2N

можно записать в форме

    data_DATA = file1 ... fileN 
    data2dir = $(datadir) 
    data2_DATA = fileN+1 ... file2N

и Automake будет обрабатывать отдельно два списка при выполнении команды make install. Выбор имен каталогов для раздельной установки описан в параграфе 2.2.7. Раздельная установка. Отметим, что команда make dist по-прежнему будет работать лишь в средах с большим размером строки команд.

В Automake применяется несколько стратегий предотвращения длинных строк. Например, при добавлении к именам файлов префикса ${srcdir}/ может возникать ситуация, подобная упомянутым выше спискам $(data_DATA) и ограничивается число аргументов, передаваемых внешним командам.

К сожалению в некоторых системах команды make могут автоматически использовать префиксы VPATH, такие как ${srcdir}/, для имен файлов в дереве исходных кодов. В таких случаях пользователю имеет смысл перейти на работу с GNU Make или воздержаться от сборок VPATH.

Для программ и библиотек, собираемых из множества источников можно применять удобные промежуточные архивы для сокращения размера элементов (8.3.5. Вспомогательные библиотеки Libtool).

3.5. Именование производных переменных

Иногда имена переменных Makefile выводятся из текста, предоставленного сопровождающим. Например, имя программы в _PROGRAMS может переопределяться в переменную _SOURCES. В таких случаях Automake «канонизирует» текст. Все символы в именах за исключением букв, цифр @ и _ превращаются в _ при создании ссылок на переменные. Например, для программы sniff-glue производным именем станет sniff_glue_SOURCES, а не sniff-glue_SOURCES, а исходные коды библиотеки libmumble++.a получат имя libmumble___a_SOURCES.

3.6. Пользовательские переменные

Некоторые переменные Makefile зарезервированы стандартами кодирования GNU для пользователя (сборщика пакета). Одним из примеров служит переменная CFLAGS.

Иногда разработчики пытаются установить такие переменные, как CFLAGS, для упрощения своей работы. Однако самому пакету не следует устанавливать пользовательские переменные, в частности для того, чтобы не влиять на переключатели, используемые при компиляции пакета. Поскольку эти переменные указаны как предназначенные для разработчика, тот вправе ожидать, что он может переопределить любую из них при сборке.

Для обхода этой проблемы Automake использует «теневую» переменную для каждой переменной пользовательских флагов. Теневые переменные не применяются для таких переменных, как CC, где они не имеют смысла. Теневые переменные используют префикс AM_ перед именем пользовательской переменной. Например, теневой переменной для YFLAGS будет AM_YFLAGS. Сопровождающий пакет человек — автор файлов Makefile.am и configure.ac — может настроить теневые переменные при необходимости.

3.7. Программы, которые могут быть нужны automake

Automake иногда нужны вспомогательные программы, чтобы созданные файлы Makefile могли работать корректно. Таких программ достаточно много и они перечислены ниже. Все эти файлы распространяются с Automake, но некоторые поддерживаются независимо. Automake копирует обновления перед каждым выпуском, но программы могут обновляться в интервалах между выпусками.

ar-lib

Эта утилита делает архиватор Microsoft lib более похожим на POSIX.

compile

Это оболочка для компиляторов, не принимающих одновременно опции -c и -o, используемая лишь при безысходности. Такие компиляторы редки и наиболее заметным является Microsoft C/C++ Compiler. Оболочка при необходимости также делает для компилятора доступными при преобразовании имен файлов опции общего назначения -I, -L, -l, -Wl и -Xlinker.

config.guess

config.sub

Эти две программы определяют канонические триплеты архитектуры build, host и target. Программы обновляются регулярно для поддержки новых архитектур и устранения проблем, возникающих при смене версии ядра. В каждый новый выпуск Automake включаются актуальные копии программ. При необходимости можно получить новые версии по ссылке.

depcomp

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

install-sh

Замена программы install для работы на платформах, где та не доступна или не работает.

mdate-sh

Сценарий для генерации файла version.texi, создающий файл и выводящий информацию о его дате.

missing

Оболочка для множества программ, нужных сопровождающему. Если нужной программы нет или она кажется устаревшей, missing будет выдавать предупреждение.

mkinstalldirs

Этот сценарий служит оболочкой для команды mkdir -p, которая не является переносимой. Сейчас применяется команда install-sh -d, когда configure находит неработающую команду mkdir -p. Для совместимости со старыми версиями продолжается использование mkinstalldirs и программа распространяется, если automake находит ее в пакете. Но она больше не устанавливается автоматически и может быть безопасно удалена.

py-compile

Служит для байтовой компиляции сценариев Python.

test-driver

Реализует используемый по умолчанию драйвер тестов.

texinfo.tex

Это не программа, а файл, нужный для команд make dvi, make ps и make pdf при работе с исходными файлами Texinfo. Свежая версия доступна по ссылке.

ylwrap

Программа обеспечивает для lex и yacc переименование выходных файлов, а также возможность параллельной работы нескольких экземпляров yacc в одном каталоге.

4. Примеры пакетов

В первом из рассматриваемых здесь примеров предполагается наличие проекта, использующего Autoconf с созданными вручную файлами Makefile, которые нужно преобразовать с помощью Automake. Здесь уместно обратиться к рассмотренному выше примеру (2.4. Сборка небольшого пакета).

Во втором примере показано, как можно собрать две программы из одного файла с использованием разных параметров компиляции.

4.1. Простой пример

Предположим, что завершена разработка программы zardoz, где применяется Autoconf для обеспечения переносимости, но файлы Makefile.in имеют особенности. Для обеспечения их переносимости применяется Automake.

Сначала нужно обновить файл configure.ac путем включения нужных automake команд. Для этого добавляется вызов AM_INIT_AUTOMAKE сразу после AC_INIT.

    AC_INIT([zardoz], [1.0]) 
    AM_INIT_AUTOMAKE 
    ...

Поскольку с программой не связано каких-либо осложнений (например, использование gettext или нежелание создавать общую библиотеку), на этом можно закончить.

Затем нужно заново создать сценарий configure, для чего нужно сказать autoconf, как найти новый макрос, который был использован. Простейшим путем является использование программы aclocal для создания файла aclocal.m4. Этот файл уже может существовать, поскольку для программы были созданы макросы. Программа aclocal позволяет поместить ваши макросы в файл acinclude.m4, который можно просто переименовать и выполнить команды

    mv aclocal.m4 acinclude.m4 
    aclocal 
    autoconf

Сейчас самое время создать файл Makefile.am для пакета zardoz. Поскольку zardoz является пользовательской программой, ее уместно поместить в каталог bindir. Кроме того, zardoz имеет документацию в формате Texinfo. Сценарий configure.ac использует AC_REPLACE_FUNCS, поэтому нужна привязка к $(LIBOBJS).

    bin_PROGRAMS = zardoz 
    zardoz_SOURCES = main.c head.c float.c vortex9.c gun.c 
    zardoz_LDADD = $(LIBOBJS) 

    info_TEXINFOS = zardoz.texi

После этого можно ввести команду automake —add-missing для создания файла Makefile.in и вспомогательных файлов.

4.2 Сборка двух программ из одного файла

Следующий пример несколько сложнее. Он показывает, как создать две программы (true и false) из одного исходного файла (true.c). Сложность состоит в том, что для каждой компиляции true.c применяются свои флаги cpp.

    bin_PROGRAMS = true false 
    false_SOURCES = 
    false_LDADD = false.o 

    true.o: true.c 
            $(COMPILE) -DEXIT_CODE=0 -c true.c 

    false.o: true.c 
            $(COMPILE) -DEXIT_CODE=1 -o false.o -c true.c

Отметим, что здесь нет определения true_SOURCES, поскольку Automake неявно предполагает наличие исходного файла true.c (8.5. Принятые по умолчанию значения _SOURCES) и задает правила для компиляции true.o и компоновки true. Приведенное выше правило true.o: true.c из Makefile.am будет переопределено созданным Automake правилом для сборки true.o.

Переменная false_SOURCES определена пустой, т. е. неявное значение не подставляется. Поскольку источник false не указан, нужно сказать Automake, как компоновать программу, для чего служит строка false_LDADD. Переменная false_DEPENDENCIES, указывающая зависимости для цели false, будет создана Automake автоматически на основе содержимого false_LDADD.

Приведенные выше правила не будут работать, если компилятор не поддерживает опции -c и -o вместе. Простейшим способом обойти это является использование фиктивной зависимости (чтобы не было проблем при параллельной работе make).

    true.o: true.c false.o 
            $(COMPILE) -DEXIT_CODE=0 -c true.c 

    false.o: true.c 
            $(COMPILE) -DEXIT_CODE=1 -c true.c && mv true.o false.o

Однако есть и более простой способ на основе флагов компиляции для каждой программы, как показано ниже.

    bin_PROGRAMS = false true 

    false_SOURCES = true.c 
    false_CPPFLAGS = -DEXIT_CODE=1 

    true_SOURCES = true.c 
    true_CPPFLAGS = -DEXIT_CODE=0

В этом случае Automake приведет к двухкратной компиляции true.c с разными флагами. В примере имена объектных файлов будут выбраны automake (false-true.o и true-true.o). Обычно имена таких файлов не имеют значения.

5. Создание Makefile.in

Для создания всех файлов Makefile.in в пакет следует использовать программу automake без аргументов из каталога верхнего уровня. Программа automake автоматически найдет все подходящие файлы Makefile.am (просматривая configure.ac) и создаст соответствующие файлы Makefile.in. Отметим, что automake имеет достаточно упрощенное представление о содержимом пакета и предполагает наличие файла configure.ac лишь на верхнем уровне. Если в пакете имеется несколько файлов configure.ac, нужно запустить automake из каждого каталога, где есть такой файл. Другим вариантом является использование программы autoreconf, которая рекурсивно просматривает каталоги дерева пакета и при необходимости запускает automake.

Можно передать программе automake аргумент. К этому аргументу будет добавлен суффикс .am и полученное имя будет использовано в качестве имени входного файла. Это свойство обычно применяется для автоматического обновления устаревших файлов Makefile.in. Отметим, что automake всегда следует выполнять из каталога верхнего уровня в проекте (даже при регенерации Makefile.in в том или ином подкаталоге. Это требуется потому, что automake нужно сканировать файл configure.ac, а также по причине использования automake информации о размещении файлов Makefile.in в подкаталогах для смены поведения в некоторых случаях.

Automake использует программу autoconf для сканирования файла configure.ac и его зависимостей (aclocal.m4 и включенные файлы), поэтому путь к autoconf должен быть включен в переменную PATH. При наличии в среде переменной AUTOCONF, ее значение будет использоваться вместо autoconf, что позволяет выбрать конкретную версию Autoconf. Следует отметить, что запуск из automake программы autoconf для сканирования файлов configure.ac не означает создания сценария configure и autoconf для этого придется запустить явно.

Опции automake перечислены ниже.

-a

—add-missing

Для Automake требуется в определенных ситуациях наличие некоторых файлов. Например, файл config.guess нужен при вызове из configure.ac макроса AC_CANONICAL_HOST. Automake распространяется с некоторыми из таких файлов (3.7. Программы, которые могут быть нужны automake) и эта опция приведет к автоматическому добавлению таких программ в пакет, когда это возможно. В общем случае, если Automake говорит об отсутствии файла, следует задать эту опцию. По умолчанию Automake пытается создать символьную ссылку на свою копию отсутствующего файла, что можно изменить с помощью опции —copy.

Многие из потенциально отсутствующих файлов являются сценариями общего назначения, чье расположение может быть задано макросом AC_CONFIG_AUX_DIR. Поэтому установка AC_CONFIG_AUX_DIR влияет на признание файла отсутствующим и его добавление (6.2. Дополнительные макросы). В некоторых режимах строгости устанавливаются дополнительные файлы (22. Опции —gnu и —gnits).

—libdir=DIR

Задает поиск файлов данных Automake в каталоге DIR вместо каталога установки (обычно служит для отладки). Переменная среды AUTOMAKE_LIBDIR обеспечивает другой способ задания каталога с файлами данных Automake, однако приоритет —libdir выше.

—print-libdir

Выводит путь к установочному каталогу, представленных Automake сценариев и файлов данных (например, texinfo.texi и install-sh).

-c

—copy

При использовании с опцией —add-missing обеспечивает копирование установленных файлов вместо создания символьных ссылок.

-f

—force-missing

При использовании с опцией —add-missing обеспечивает повторную установку стандартных файлов даже при их наличии в дереве кода.

—foreign

Устанавливает глобально уровень строгости foreign (3.2. Строгость).

—gnits

Устанавливает глобально уровень строгости gnits (22. Опции —gnu и —gnits).

—gnu

Устанавливает глобально принятый по умолчанию уровень строгости (22. Опции —gnu и —gnits).

—help

Выводит краткую справку по опциям командной строки.

-i

—ignore-deps

Отключает отслеживание зависимостей в создаваемых Makefile (8.19. Автоматическое отслеживание зависимостей).

—include-deps

Включает отслеживание зависимостей. Опция включена по умолчанию и сохранена по историческим причинам.

—no-force

Исходно automake создает все файлы Makefile.in, упомянутые в configure.ac. Эта опция позволяет обновить лишь файлы, устаревшие в соответствии с какой-либо из их зависимостей.

-o DIR

—output-dir=DIR

Задает размещение созданных Makefile.in в каталоге DIR. Исходно каждый Makefile.in создается в каталоге с соответствующим файлом Makefile.am. Опция устарела и будет удалена.

-v

—verbose

Заставляет Automake выводить информацию о читаемых и создаваемых файлах.

—version

Выводит номер версии Automake и завершает работу.

-W CATEGORY

—warnings=CATEGORY

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

gnu

предупреждения, связанные с GNU Coding Standards;

obsolete

устаревшие функции или конструкции;

override

пользовательские переопределения правил или переменных Automake;

portability

проблемы переносимости (например, использование не переносимых свойств make);

extra-portability

дополнительные вопросы переносимости, связанные с экзотическими инструментами (например, архиватор Microsoft lib);

syntax

странный синтаксис, неиспользуемые переменные, опечатки;

unsupported

неподдерживаемые или неполные функции;

all

все предупреждения;

none

не выводить никаких предупреждений;

error

считать предупреждения ошибками.

Категорию можно отключить с помощью префикса no-. Например, -Wno-syntax будет отключать предупреждения о неиспользуемых переменных.

По умолчанию выводятся сообщения категорий obsolete, syntax и unsupported. В дополнение к этому категории gnu и portability включаются на уровнях строгости gnu и gnits.

Выключение категории portability выключает опцию extra-portability, а включение extra-portability включает portability. Однако включение portability или выключение extra-portability не влияет на другую категорию.

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

Если переменная среды AUTOMAKE_JOBS содержит положительное значение, оно определяет максимальное число потоков Perl, используемых automake при одновременной генерации файлов Makefile.in.

6. Сканирование configure.ac и использование aclocal

Automake сканирует файл configure.ac для поиска информации о пакете. Некоторые макросы autoconf являются обязательными и ряд переменных необходимо определить в configure.ac. Automake также использует данные из configure.ac для дополнительной настройки вывода.

Automake предоставляет некоторые макросы Autoconf для упрощения поддержки. Эти макросы могут автоматически включаться в файл aclocal.m4 с помощью программы aclocal.

6.1. Конфигурационные требования

Одним из реальных требований Automake является вызов из configure.ac макроса AM_INIT_AUTOMAKE, который выполняет ряд задач, требуемых для корректной работы Automake (6.4. Макросы Autoconf из пакета Automake).

К другим макросам, требуемым Automake, но не исполняемым AM_INIT_AUTOMAKE, относятся AC_CONFIG_FILES и AC_OUTPUT. Обычно два эти макроса вызываются в конце configure.ac.

         ... 
         AC_CONFIG_FILES([ 
           Makefile 
           doc/Makefile 
           src/Makefile 
           src/lib/Makefile 
           ... 
         ]) 
         AC_OUTPUT

Automake использует эти макросы для определения создаваемых файлов. Указанные файлы считаются создаваемыми Automake файлами Makefile.in, если имеется файл с таким же именем и расширением .am. Обычно AC_CONFIG_FILES([foo/Makefile]) будет заставлять Automake генерировать файл foo/Makefile.in при наличии foo/Makefile.am.

При использовании AC_CONFIG_FILES со множеством входных файлов (например, AC_CONFIG_FILES([Makefile:top.in:Makefile.in:bot.in]) automake будет сначала создавать файл .in, для которого имеется .am. Если таких файлов нет, вывод не считается созданным Automake.

Файлы, созданные AC_CONFIG_FILES (Makefile от Automake и прочие), удаляются командой make distclean. Их ввода распространяется автоматически, если он не не является выводом предшествующих команд AC_CONFIG_FILES.  Правила пересборки создаются в Automake Makefile, имеющихся в подкаталоге выходного файла (если он есть) или Makefile верхнего уровня.

Описанный выше механизм (очистка, распространение, пересборка)хорошо работает, если AC_CONFIG_FILES содержит лишь литералы. При использовании переменных среды automake не сможет выполнить эту настройку и придется поработать руками. Например, в случае

         file=input 
         ... 
         AC_CONFIG_FILES([output:$file],, [file=$file])

automake будет выводить правила для очистки вывода и повторной сборки. Однако правило повторной сборки не будет зависеть от ввода и файл не будет распространен (нужно добавить EXTRA_DIST = input в файл Makefile.am, если input содержит исходный код).

Аналогично

         file=output 
         file2=out:in 
         ... 
         AC_CONFIG_FILES([$file:input],, [file=$file]) 
         AC_CONFIG_FILES([$file2],, [file2=$file2])

будет приводить лишь к распространению input. Файлы не будут очищаться автоматически (для этого следует добавить DISTCLEANFILES = output out) и не будет выводиться правило пересборки.

Обычно automake не может предсказать, какое значение будет содержать $file при последующем запуске configure и не может использовать переменную $file в Makefile. Однако при наличии ссылки на $file как ${file} (т. е. совместимым с make способом) и использовании AC_SUBST для обеспечения осмысленности ${file} в Makefile, automake сможет использовать ${file} для создания правил. Например, ниже показано, как сам пакет Automake создает сценарии для тестов с учетом версии.

         AC_SUBST([APIVERSION], ...) 
         ... 
         AC_CONFIG_FILES( 
           [tests/aclocal-${APIVERSION}:tests/aclocal.in], 
           [chmod +x tests/aclocal-${APIVERSION}], 
           [APIVERSION=$APIVERSION]) 
         AC_CONFIG_FILES( 
           [tests/automake-${APIVERSION}:tests/automake.in], 
           [chmod +x tests/automake-${APIVERSION}])

Очистка, распространение и повторная сборка здесь выполняются автоматически, поскольку значение ${APIVERSION} известно при запуске make.

Отметим, что не следует применять переменные оболочки для объявления файлов Makefile, для которых automake нужно создавать Makefile.in. AC_SUBST здесь не поможет, поскольку automake нужно знать имя файла во время работы, чтобы проверить наличие Makefile.am. В сложной ситуации, когда требуется такое использование переменных, нужно сказать Automake, какие файлы Makefile.in следует генерировать, из строки команды.

Можно разрешить automake выдавать дополнительные правила для AC_CONFIG_FILES с помощью AM_COND_IF (6.2. Дополнительные макросы).

В качестве заключения ниже приведены несколько рекомендаций.

  • Используйте по возможности литералы для Makefile и других файлов.

  • Используйте $file (или ${file} без AC_SUBST([file])) для файлов, которые automake следует игнорировать.

  • Используйте ${file} и AC_SUBST([file]) для файлов, которые automake не следует игнорировать.

6.2. Дополнительные макросы

При каждом запуске Automake программа запускает Autoconf для трассировки файла configure.ac. Таким способом могут распознаваться некоторые макросы и соответствующим образом корректироваться создаваемые файлы Makefile.in. В настоящее время распознаются и работают макросы AC_CANONICAL_BUILD, AC_CANONICAL_HOST, AC_CANONICAL_TARGET. Automake обеспечивает наличие файлов config.guess и config.sub, а также переменных build_triplet, host_triplet и target_triplet в Makefile.

AC_CONFIG_AUX_DIR

Automake будет искать разные вспомогательные сценарии (такие как install-sh, ar-lib, config.guess, config.sub, depcomp, compile, install-sh, ltmain.sh, mdate-sh, missing, mkinstalldirs, py-compile, test-driver, texinfo.tex, ylwrap), в каталоге, заданном при вызове этого макроса. Поиск некоторых сценариев может выполняться лишь в тех случаях, когда они нужны в создаваемом Makefile.in.

Если макрос AC_CONFIG_AUX_DIR не задан, поиск сценариев ведется в стандартных каталогах. Например, для mdate-sh, texinfo.tex и ylwrap поиск ведется в каталоге исходного кода, соответствующем текущему файлу Makefile.am. Для остальных сценариев поиск происходит в каталогах ., .., ../.. относительно корневого каталога исходного кода.

Требуемые файлы из AC_CONFIG_AUX_DIR распространяются автоматически даже при отсутствии Makefile.am в данном каталоге.

AC_CONFIG_LIBOBJ_DIR

Automake будет требовать исходные файлы, объявленные с AC_LIBSOURCE в каталоге, заданном этим макросом.

AC_CONFIG_HEADERS

Automake будет генерировать правила для перестроения этих заголовков по соответствующим шаблонам (обычно шаблоном для foo.h служит foo.h.in). Старые версии Automake требовали использования макроса AM_CONFIG_HEADER, но сейчас он не применяется и будет удален.

Как и в случае AC_CONFIG_FILES (6.1. Конфигурационные требования), части спецификации, использующие переменные оболочки, будут игнорироваться при очистке, распространении и перестройке.

AC_CONFIG_LINKS

Automake будет генерировать правила для удаления созданных configure ссылок по команде make distclean и распространять указанные исходные файлы по команде make dist.

Как и в случае AC_CONFIG_FILES (6.1. Конфигурационные требования), части спецификации, использующие переменные оболочки, будут игнорироваться при очистке и распространении (перестройки здесь нет).

AC_LIBOBJ

AC_LIBSOURCE

AC_LIBSOURCES

Automake будет автоматически распространять любой файл, указанный в AC_LIBSOURCE или AC_LIBSOURCES. Отметим, что макрос AC_LIBOBJ вызывает AC_LIBSOURCE, поэтому при наличии в макросе Autoconf вызова AC_LIBOBJ([file]), file.c будет автоматически распространяться Automake. Это включает множество макросов, подобных AC_FUNC_ALLOCA, AC_FUNC_MEMCMP, AC_REPLACE_FUNCS и др. В результате прямые назначения LIBOBJS больше не поддерживаются и следует использовать AC_LIBOBJ.

AC_PROG_RANLIB

Требуется при наличии в пакете собираемых библиотек.

AC_PROG_CXX

Требуется при включении любых исходных файлов C++.

AC_PROG_OBJC

Требуется при включении любых исходных файлов Objective C.

AC_PROG_OBJCXX

Требуется при включении любых исходных файлов Objective C++.

AC_PROG_F77

Требуется при включении любых исходных файлов Fortran 77.

AC_F77_LIBRARY_LDFLAGS

Требуется для программ и общих библиотек, написанных на нескольких языках с использованием Fortran 77 (Ошибка: источник перекрёстной ссылки не найден). Макросы Autoconf распространяются с пакетом Automake.

AC_FC_SRCEXT

Automake будет добавлять флаги, рассчитанные AC_FC_SRCEXT, при компиляции файлов с соответствующими расширениями имен.

AC_PROG_FC

Требуется при включении любых исходных файлов Fortran 90/95. Макрос включен в Autoconf версии 2.58 и выше.

AC_PROG_LIBTOOL

Automake будет включать обработку для libtool.

AC_PROG_YACC

При наличии исходных файлов Yacc нужно использовать этот макрос или определить переменную YACC в файле configure.ac (предпочтительно).

AC_PROG_LEX

При наличии исходных файлов Lex требуется использовать этот макрос.

AC_REQUIRE_AUX_FILE

Для каждого макроса AC_REQUIRE_AUX_FILE([FILE]) программа automake будет обеспечивать наличие FILE в каталоге aux или «жаловаться». Этот макрос следует применять в сторонних макросах Autoconf, которым нужны файлы поддержки в каталоге aux, заданном AC_CONFIG_AUX_DIR.

AC_SUBST

Первый аргумент автоматически определяется как переменная в каждом создаваемом Makefile.in, если для этой переменной не применяется также AM_SUBST_NOTMAKE.

Для каждой подставленной переменной VAR программа automake будет добавлять строку VAR = VALUE в каждый файл Makefile.in. Многие макросы Autoconf вызывают AC_SUBST для установки таким способом выходных переменных. Например, AC_PATH_XTRA определяет X_CFLAGS и X_LIBS. Таким образом, можно обращаться к этим переменным как $(X_CFLAGS) и $(X_LIBS) в любом файле Makefile.am, если вызывается AC_PATH_XTRA.

AM_CONDITIONAL

Включает условные конструкции в Automake (20. Конструкции с условием).

AM_COND_IF

Позволяет automake обнаруживать последующий доступ в файле configure.ac к условной конструкции, созданной макросом AM_CONDITIONAL, делая возможным условие AC_CONFIG_FILES (20.1. Использование условных конструкций).

AM_GNU_GETTEXT

Этот макрос требуется для пакетов, использующих GNU gettext (10.2. Gettext), и распространяется в пакете gettext.  Увидев этот макрос, Automake обеспечивает соответствие пакета некоторым требованиям gettext.

AM_GNU_GETTEXT_INTL_SUBDIR

Этот макрос задает создание подкаталога intl/ даже при вызове AM_GNU_GETTEXT с первым аргументом external.

AM_MAINTAINER_MODE([DEFAULT-MODE])

Добавляет опцию —enable-maintainer-mode в команду configure и automake будет отключать по умолчанию правила maintainer-only в создаваемых файлах Makefile.in, если для DEFAULT-MODE не установлено enable. Макрос определяет условие MAINTAINER_MODE, которое можно использовать в файле Makefile.am (27.2. Сценарий missing и режим AM_MAINTAINER_MODE).

AM_SUBST_NOTMAKE(VAR)

Запрещает Automake определять переменную VAR, даже если для нее задана подстановка в config.status. Обычно Automake определяет переменную make для каждой подстановки configure, т. е. для каждого AC_SUBST([VAR]). Этот макрос запрещает такие определения. Если для этой переменной не было вызова AC_SUBST, макрос AM_SUBST_NOTMAKE не будет работать. Запрет определений переменных может быть полезен при подстановке многострочных значений, где VAR = @VALUE@ может давать непредсказуемые результаты.

m4_include

Файлы, включенные в configure.ac с использованием этого макроса, будут обнаруживаться Automake и автоматически распространяться, а также отображаться как зависимости в правилах Makefile.

Макрос m4_include редко применяется в файлах configure.ac, но может присутствовать в aclocal.m4, когда aclocal обнаруживает, что некоторые нужные макросы приходят из локальных файлов пакета, а не из общесистемных файлов.

6.3. Автоматическое создание aclocal.m4

Automake включает множество макросов Autoconf, которые могут применяться в пакете (6.4. Макросы Autoconf из пакета Automake). некоторые из этих макросов реально требуются Automake в определенных ситуациях. Они должны быть определены в файле aclocal.m4, иначе autoconf их не увидит.

Программа aclocal автоматически создает файлы aclocal.m4 на основе содержимого configure.ac. Это дает удобный способ получить макросы Automake без их поиска. Механизм aclocal позволяет другим пакетам предоставлять свои макросы (6.3.3. Создание макросов aclocal). Это можно применять также для поддержки своего набора макросов (6.3.4. Обработка локальных макросов).

При запуске aclocal сканирует все файлы .m4, которые можно найти, просматривая определения макросов (6.3.2. Путь поиска макросов), затем сканирует файл configure.ac. Любое из упоминаний одного из этих макросов на первом этапе приводит к включению этого макроса (и нужных ему макросов) в файл aclocal.m4.

Включение содержащего определение макроса файла в aclocal.m4 обычно выполняется путем копирования всего текста файла, включая неиспользуемые определения, а также комментарии # и dnl. Если нужно полностью игнорировать комментарии программой aclocal, следует использовать ##.

Когда выбранный aclocal файл размещается в каталоге, указанном относительным путем поиска в аргументе опции aclocal -I, программа aclocal предполагает, что файл относится к пакету и использует m4_include вместо копирования файла в aclocal.m4. Это снижает размер пакета, упрощает контроль зависимостей и обеспечивает автоматическое распространение файла (6.3.4. Обработка локальных макросов). Любой макрос, найденный в общесистемном каталоге или по абсолютному пути, будет копироваться. Поэтому следует использовать -I pwd/reldir вместо -I reldir, когда тот или иной относительный каталог следует считать расположенным вне пакета.

Содержимое acinclude.m4 (если файл существует) также автоматически включается в aclocal.m4. Не рекомендуется применять acinclude.m4 в новых пакетах (6.3.4. Обработка локальных макросов).

При создании aclocal.m4 программа aclocal запускает autom4te для отслеживания реально используемых макросов и пропуска в aclocal.m4 всех макросов, которые упоминаются, но не преобразуются (это может происходить при условном вызове макроса). Предполагается, что файл autom4te доступен через переменную PATH как и autoconf. Местоположение файла можно переопределить в переменной окружения AUTOM4TE.

6.3.1. Опции aclocal

Ниже перечислены опции, принимаемые командой aclocal.

—automake-acdir=DIR

Задает поиск макросов, предоставленных automake, в каталоге DIR вместо установочного каталога. Опция служит в основном для отладки. Аналогичного результата можно путем остановки переменной среды ACLOCAL_AUTOMAKE_DIR, но опция —automake-acdir имеет более высокий приоритет.

—system-acdir=DIR

Задает поиск сторонних общесистемных файлов с макросами (и специального файла dirlist) в каталоге DIR вместо установочного каталога. Опция служит в основном для отладки.

—diff[=COMMAND]

Выполнить команду COMMAND для файла M4, который будет установлен или перезаписан с помощью —install. По умолчанию в качестве COMMAND применяется diff -u. Опция предполагает наличие —install и —dry-run.

—dry-run

Не перезаписывать и не создавать на деле aclocal.m4 и файлы M4 при наличии опции —install (имитация работы).

—help

Выводит справочную информацию о программе.

-I DIR

Добавляет DIR в список каталогов для поиска файлов .m4.

—install

Задает установку сторонних общесистемных макросов в первый каталог, заданный -I DIR, вместо их копирования в выходной файл (DIR может быть и абсолютным путем). При использовании этой опции (и только в этом случае) aclocal будет учитывать строки #serial NUMBER в макросах. Файл M4 игнорируется, если в пути поиска есть другой файл M4 с таким же базовым именем и большим порядковым номером (6.3.5. Порядковые номера).

—force

Всегда переписывать выходной файл, что по умолчанию выполняется лишь при необходимости (т. е. при изменении его содержимого или обновлении зависимостей). Эта опция принудительно обновляет файл aclocal.m4 (или файл, заданный опцией —output) и только его, не затрагивая файлов, которые может понадобиться установить с помощью —install.

—output=FILE

Задает вывод в файл FILE вместо aclocal.m4.

—print-ac-dir

Задает вывод каталоге, где aclocal ищет сторонние файлы .m4, подавляя обычную обработку. Эта опция применялась в прошлом сторонними приложениями для определения мест установки файлов .m4, но сейчас ее использование не рекомендуется, поскольку она ведет к неполному соблюдению $(prefix) а нарушение GNU     Coding Standards. Похожая семантика может быть достигнута с помощью переменной среды ACLOCAL_PATH (6.3.3. Создание макросов aclocal).

—verbose

Задает вывод имен проверяемых файлов.

—version

Выводит номер версии Automake и завершает работу.

-W CATEGORY

—warnings=CATEGORY

Задает вывод предупреждений указанной категории CATEGORY:

syntax

сомнительные синтаксические конструкции, избыточные скобки в макросах, неиспользуемые макросы и т. п.;

unsupported

неизвестный макрос;

all

все предупреждения (принято по умолчанию);

none

отключить все предупреждения;

error

считать все предупреждения ошибками.

Переменная среды WARNINGS учитывается так же, как в случае automake (5. Создание Makefile.in).

6.3.2. Путь поиска макросов

По умолчанию aclocal ищет файлы .m4 в указанных ниже каталогах (в порядке их перечисления).

ACDIR-APIVERSION

Здесь хранятся макросы .m4 из пакета Automake. Значение APIVERSION зависит от выпуска Automake, например, для Automake 1.11.x значением APIVERSION будет 1.11.

ACDIR

Этот каталог предназначен для сторонних файлов .m4 и задается при сборке automake. Это @datadir@/aclocal/, что обычно преобразуется в ${prefix}/share/aclocal’. Для определения заданного в программе значения ACDIR служит опция —print-ac-dir (6.3.1. Опции aclocal).

В качестве примера предположим, что программа automake-1.11.2 была настроена с —prefix=/usr/local. Тогда путями поиска будут

  1. /usr/local/share/aclocal-1.11.2/
  2. /usr/local/share/aclocal/

Пути к каталогам ACDIR и ACDIR-APIVERSION могут быть изменены с помощью опций —system-acdir и —automake-acdir (6.3.1. Опции aclocal). Однако следует отметить, что эти опции предназначены для использования лишь в наборе тестов Automake или для отладки в необычных ситуациях, а не для применения конечными пользователями.

Как указано в параграфе 6.3.1. Опции aclocal, имеется несколько опций для смены или расширения путей поиска.

-I DIR

Любые дополнительные каталоги, указанные с опцией -I (6.3.1. Опции aclocal), добавляются в начало списка. Таким образом, использование опций aclocal -I /foo -I /bar создаст список поиска

  1. /foo
  2. /bar
  3. ACDIR-APIVERSION
  4. ACDIR

dirlist

При наличии файла dirlist в пути ACDIR предполагается, что этот файл содержит список шаблонов каталогов, которые добавляются в конец поискового списка. В записях dirlist обычно применяются шаблоны *, ?, […]. Предположим, что файл ACDIR/dirlist имеет вид

    /test1 
    /test2 
    /test3*

и команда aclocal была вызвана с опциями -I /foo -I /bar. Путь поиска в этом случае будет иметь вид

  1. /foo
  2. /bar
  3. ACDIR-APIVERSION
  4. ACDIR
  5. /test1
  6. /test2
  7. все каталоги, начинающиеся с /test3

При использовании опции —system-acdir=DIR программа aclocal будет искать dirlist в каталоге DIR, но следует помнить о приведенном выше предупреждении по части использования —system-acdir.

Файл dirlist полезен в следующей ситуации. Предположим, что программа automake версии 1.11.2 установлена с опцией —prefix=/usr поставщиком операционной системы. Тогда по умолчанию будет применяться путь поиска

  1. /usr/share/aclocal-1.11/
  2. /usr/share/aclocal/

Однако при установке в системе множества пакетов вручную часто используется $prefix=/usr/local. В такой ситуации многие из дополнительных файлов .m4 будут размешено в каталоге /usr/local/share/aclocal. Единственным способом заставить программу /usr/bin/aclocal находить эти файлы .m4 будет использование команды aclocal -I /usr/local/share/aclocal, что неудобно. Файл /usr/share/aclocal/dirlist с приведенной ниже строкой решает эту проблему.

    /usr/local/share/aclocal

В этом случае по умолчанию будет использоваться путь поиска

  1. /usr/share/aclocal-1.11/
  2. /usr/share/aclocal/
  3. /usr/local/share/aclocal/

без применения опций -I, которые можно применить для специфических потребностей проекта (my-source-dir/m4/). Файл dirlist может быть полезен при установке Automake в своей учетной записи (не общесистемной), если нужно видеть макросы, установленные в других местах системы.

ACLOCAL_PATH

Этот механизм является простейшим способом настройки путей поиска макро-файлов. Любой каталог, указанный в разделенном двоеточиями списке переменной среды ACLOCAL_PATH, добавляется в путь поиска и имеет преимущество перед системными каталогами (включая указанные в dirlist), за исключением ACDIR-APIVERSION и каталогов, добавленных с помощью опции -I.

Отметим, что при использовании опции —install любой файл .m4, содержащий нужный макрос и найденный в каталоге из ACLOCAL_PATH, будет установлен локально с учетом порядковых номеров в .m4 (6.3.5. Порядковые номера). В отличие от dirlist, переменная ACLOCAL_PATH полезна при использовании глобальной копии Automake и желании видеть макросы в своем домашнем каталоге.

Предполагаемые несовместимости в будущем

Порядок каталогов в пути поиска весьма запутан и может создавать путаницу и неоптимальные решения, поэтому вероятно его изменение в будущих выпусках Automake. В частности, каталоги из ACLOCAL_PATH и ACDIR могут оказаться предпочтительней каталогов ACDIR-APIVERSION, а каталоги из ACDIR/dirlist могут оказаться предпочтительней ACDIR. Это может создавать проблемы совместимости.

6.3.3. Создание макросов aclocal

Программа aclocal сама по себе не знает о каких-либо макросах и может быть легко расширена для использования любых макросов. Это может применяться библиотеками для добавления макросов Autoconf, доступных другим программам. Например, библиотека gettext включает макрос AM_GNU_GETTEXT. Который следует применять всем пакетам, использующим gettext. При установке библиотеки устанавливается этот макрос и aclocal будет видеть его.

Для макро-файлов следует использовать расширение .m4, а файлы следует помещать в каталог $(datadir)/aclocal.

    aclocaldir = $(datadir)/aclocal 
    aclocal_DATA = mymacro.m4 myothermacro.m4

Следует использовать каталог $(datadir)/aclocal, а не что-то иное на основе aclocal —print-ac-dir (27.10. Установка в жестко заданные места). Может оказаться полезным предложить пользователю добавить каталог $(datadir)/aclocal в переменную ACLOCAL_PATH, чтобы программа aclocal находила файлы .m4, автоматически установленные пакетом.

Файлу макросов следует быть последовательностью макросов AC_DEFUN с корректным использованием скобок. Программа aclocal также понимает макросы AC_REQUIRE, что позволяет помещать каждый макрос в отдельный файл. В каждом файле следует задавать определения макросов, не включая дополнительных действий. В частности, любые вызовы AC_PREREQ следует делать внутри макроса, а не в начале файла.

Начиная с Automake версии 1.8, программа aclocal выдает предупреждения при некорректных вызовах AC_DEFUN. Это может вызывать раздражение, поскольку раньше такой строгости не было и во многих сторонних макросах возможны такие вызовы. Причина более строгого контроля заключается в том, что будущие выпуски aclocal (6.3.6. Будущее aclocal) должны будут временно включать эти сторонние файлы .m4 (возможно несколько раз) даже при отсутствии реальной потребности в них. Пересмотр имеющихся макросов не требует больших усилий. Например,

    # дурной стиль
    AC_PREREQ(2.68)
    AC_DEFUN(AX_FOOBAR, 
    [AC_REQUIRE([AX_SOMETHING])dnl 
    AX_FOO 
    AX_BAR 
    ])

следует переписать в

    AC_DEFUN([AX_FOOBAR], 
    [AC_PREREQ([2.68])dnl 
    AC_REQUIRE([AX_SOMETHING])dnl 
    AX_FOO 
    AX_BAR 
    ])

Размещение вызова AC_PREREQ внутри макроса гарантирует, что Autoconf 2.68 не потребуется, если AX_FOOBAR на деле не применяется. Важно, что указание первого аргумента AC_DEFUN в скобках позволяет переопределить или дважды включить этот макрос (в ином случае этот первый аргумент будет преобразовываться во втором определении). Для согласованности в скобки помещаются даже аргументы, которым это не требуется, например 2.68.

Если предупреждение относится к стороннему макросу, следует обратиться к сопровождающему пакет. Использование свежей версии макросов поможет избавиться от избыточных предупреждений.

Другим случаем широкого использования aclocal является управление локальными макросами пакета, описанное ниже.

6.3.4. Обработка локальных макросов

Тесты, включенные в Autoconf не охватывают все потребности, поэтому для расширения часто применяют свои или сторонние макросы. Есть два способа организации в пакете дополнительных макросов.

Первый (исторический) способ заключается в указании всех макросов в файле acinclude.m4. Этот файл включается в aclocal.m4 при запуске aclocal и макросы становятся видны программе autoconf. Однако при большом числе включаемых макросов поддержка файла осложняется и становится почти невозможным совместное использование.

Второй вариант (рекомендуемый) заключается в записи каждого макроса в отдельный файл и размещение всех таких файлов в одном каталоге (обычно его называют m4/). В этом случае достаточно добавить в файл configure.ac корректный вызов AC_CONFIG_MACRO_DIRS

    AC_CONFIG_MACRO_DIRS([m4])

Программа aclocal будет автоматически добавлять каталог m4/ в путь поиска файлов m4.

При работе aclocal программа будет собирать файл aclocal.m4, где m4_include используется для каждого файла из каталога m4/, определяющего нужный макрос. Не найденные локально макросы будут отыскиваться в общесистемных каталогах (6.3.2. Путь поиска макросов).

Пользовательские макросы следует распространять на тех же основаниях, что и configure.ac, чтобы другие люди имели все исходные файлы вашего пакета, если хотят работать с ним. В реальности такое распространение происходит автоматически, поскольку распространяются все файлы из m4_include.

Однако нет согласия в части распространения сторонних макросов, которые может использовать пакет. Многие библиотеки устанавливают свои макросы в общесистемный каталог aclocal (6.3.3. Создание макросов aclocal). Например, пакет Guile включает файл guile.m4 с макросом GUILE_FLAGS. Который может применяться для установки флагов компилятора и компоновщика при использовании Guile. Применение GUILE_FLAGS в configure.ac будет заставлять aclocal копировать файл guile.m4 в aclocal.m4, но guile.m4 не является частью проекта, поэтому не будет распространяться. Технически это означает, что пользователь, которому нужно перестроить aclocal.m4, должен будет установить сначала Guile. Это нормально, если Guile требуется для сборки пакета, но если пакет может работать в архитектуре где установка Guile невозможна, это создаст препятствие. Простым решением будет копирование таких сторонних макросов в локальный каталог m4/ для их распространения. Поскольку в Automake 1.10 программа aclocal поддерживает опцию —install для копирования общесистемных сторонних макросов в локальный каталог, это тоже помогает решить проблему. В такой ситуации общесистемные макросы будут копироваться в каталог m4/ при первом запуске aclocal. После этого установленные локально макросы будут иметь предпочтение перед общесистемными при каждом запуске aclocal.

Одной из причин держать —install во флагах даже при первом запуске является то, что при последующем редактировании configure.ac и зависимости от нового макроса этот макрос будет установлен в m4/ автоматически. Другая причина заключается в том, что для обновления макросов могут применяться порядковые номера, когда в системе устанавливаются новые версии общих макросов. Порядковые номера указываются строкой вида

    #serial NNN

где NNN содержит лишь цифры и точки. Эту строку следует включать в файл M4 до определений макросов. Хорошим тоном является поддержка порядкового номера для каждого распространяемого макроса, даже если не используется опция —install — это позволит другим использовать ваши макросы.

6.3.5. Порядковые номера

Поскольку сторонние макросы из файлов *.m4 естественно обобщаются множеством проектов, некоторым людям нравится указывать их версии. Таким способом проще сказать, какой из файлов M4 является более новым. С 1996 г. стало нормой использование для этого порядковых номеров в форме

    # serial VERSION

где значение VERSION указывает номер версии (только цифры и точки). Обычно в качестве номера используют целые числа и увеличивают номер при каждом изменении макроса. Строка с номером должна предшествовать определениям. Первым символом строки должен быть диез (#), а после номера версии можно указать дополнения

    #serial VERSION GARBAGE

Обычно эти номера игнорируются программами aclocal и autoconf, как и комментарии. Однако при использовании aclocal —install эти порядковые номера будут менять способ выбора программой aclocal порядковых номеров для установки в пакет. При наличии нескольких файлов с одинаковыми номерами и наличии хотя бы в одном из них строки #serial, программ aclocal будет игнорировать файлы с более старым номером (или без него).

Отметим, что порядковый номер относится ко всему файлу M4, а не только к содержащимся в нем макросам. Файл может включать много макросов, но порядковый номер должен быть единственным.

Ниже представлен пример использования опции —install и взаимодействия с порядковыми номерами. Предположим, что поддерживается пакет MyPackage, для которого в configure.ac указан сторонний макрос AX_THIRD_PARTY, определенный в файле /usr/share/aclocal/thirdparty.m4, как показано ниже

    # serial 1 
    AC_DEFUN([AX_THIRD_PARTY], [...])

MyPackage использует каталог m4/ для хранения локальных макросов, как описано в параграфе 6.3.4. Обработка локальных макросов, и включает строку

    AC_CONFIG_MACRO_DIRS([m4])

в файле configure.ac.

Изначально каталог m4/ пуст. При первом запуске будет отмечено, что

  • configure.ac использует AX_THIRD_PARTY;

  • локальные макросы не определяют AX_THIRD_PARTY;

  • файл /usr/share/aclocal/thirdparty.m4’ определяет AX_THIRD_PARTY с порядковым номером 1.

Поскольку макрос /usr/share/aclocal/thirdparty.m4 является общесистемным и программа aclocal была запущена с опцией —install, она будет копировать этот файл в m4/thirdparty.m4 и вывод aclocal.m4 будет включать m4_include([m4/thirdparty.m4]).

При следующем запуске aclocal —install обнаруживаются некоторые изменение и aclocal скажет, что

  • configure.ac использует AX_THIRD_PARTY;

  • m4/thirdparty.m4 определяет AX_THIRD_PARTY с порядковым номером 1;

  • /usr/share/aclocal/thirdparty.m4 определяет AX_THIRD_PARTY с порядковым номером 1.

Поскольку в обоих файлах указан один порядковый номер, aclocal использует найденный первым файл (6.3.2. Путь поиска макросов). В результате aclocal игнорирует файл /usr/share/aclocal/thirdparty.m4 и выводит aclocal.m4 с m4_include([m4/thirdparty.m4]).

Локальные каталоги, указанные в опции -I, всегда просматриваются до общесистемных каталогов, поэтому локальные файлы всегда оказываются предпочтительней общесистемных при совпадении порядковых номеров.

Предположим, что внешний общесистемный макрос был изменен (например, при обновлении установившего макрос пакета). Пусть новый макрос имеет порядковый номер 2. При следующем вызове aclocal —install будет

  • configure.ac использует AX_THIRD_PARTY;

  • m4/thirdparty.m4 определяет AX_THIRD_PARTY с порядковым номером 1;

  • /usr/share/aclocal/thirdparty.m4 определяет AX_THIRD_PARTY с порядковым номером 2.

Когда aclocal видит больший порядковый номер, программа немедленно забывает все, что известно из файлов с таким же именем и меньшим порядковым номером. Поэтому при обнаружении usr/share/aclocal/thirdparty.m4 с номером 2 aclocal будет работать как будто ей ничего не известно о m4/thirdparty.m4. Это возвращает к ситуации, похожей на начало примера, где нет локального файла с определением макроса. Программа aclocal будет устанавливать новую версию макроса m4/thirdparty.m4, переопределяя прежнюю. Пакет MyPackage в результате обновляет свой макрос как побочный эффект запуска aclocal.

Если не нужно, чтобы программа aclocal обновляла локальные макросы, можно использовать aclocal —diff для просмотра изменений, которые команда aclocal —install будет вносить в эти макросы.

Опция —force для команды aclocal не оказывает влияния на файлы, устанавливаемые опцией —install. Например, при изменении локальных макросов не предполагается замена их на общесистемные по команде с опциями —install —force.  Если такая замена нужна, следует просто удалить локальные макросы и ввести команду aclocal —install.

6.3.6. Будущее aclocal

Предполагается, что использование программы aclocal будет прекращено, поскольку эту функциональность не следует предоставлять в Automake. Пакету Automake следует сосредоточиться на создании файлов Makefile, а работа с M4 относится к Autoconf. Тот факт, что некоторые люди устанавливают Automake лишь для использования aclocal, но не пользуются самой программой automake указывает, что эта функция не востребована. Новые реализации могут отличаться, например, может обеспечиваться схема в стиле m4/, рассмотренная в параграфе 6.3.4. Обработка локальных макросов. Пока не понятно, как это произойдет. Вопрос этот неоднократно обсуждался, но задача еще не решена.

С точки зрения пользователя удаление aclocal может оказаться болезненным. Есть простая мера предосторожности, позволяющая смягчить этот переход — никогда не использовать программу aclocal саму по себе. Следует использовать эту программу лишь под контролем autoreconf и правил перестройки Automake. Если же используется непосредственный вызов aclocal, придется столкнуться с проблемами при исключении программы.

Многие пакеты распространяются со сценариями bootstrap или autogen.sh, которые просто вызывают aclocal, libtoolize, gettextize или autopoint, autoconf, autoheader и automake в нужном порядке. На деле точно то, что может сделать autoreconf. Если ваш пакет включает сценарий bootstrap или autogen.sh, следует рассмотреть использование autoreconf. Это должно упростить логику и в результате сценарии станут ненужными как и вызовы aclocal напрямую.

В настоящее время сторонние пакеты продолжают устанавливать публичные макросы в каталог /usr/share/aclocal/. При замене aclocal другим инструментом каталог может быть переименован, но поддержка /usr/share/aclocal/ для совместимости со старыми версиями должна быть простой, если все макросы написаны верно (6.3.3. Создание макросов aclocal).

6.4. Макросы Autoconf из пакета Automake

Automake распространяется с несколькими макросами Autoconf, которые можно вызывать из файла configure.ac. Программа aclocal будет включать используемые макросы в файл aclocal.m4.

6.4.1. Макросы общего пользования

AM_INIT_AUTOMAKE([OPTIONS])

Запускает множество макросов, требуемых для корректной работы создаваемых файлов Makefile.

AM_INIT_AUTOMAKE вызывается с одним аргументом в виде списка разделенных пробелами опций Automake, которые следует применить к каждому файлу Makefile.am в дереве кода. Влияние каждой опции описано ниже (17. Смена поведения Automake).

Этот макрос может также вызываться в устаревшей форме AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) с двумя аргументами — пакет и номер версии, которые извлекаются с помощью макроса Autoconf AC_INIT. Однако отличие состоит в том, что для вызова AC_INIT макрос AM_INIT_AUTOMAKE поддерживает преобразование переменных среды в аргументах PACKAGE и VERSION (в ином случае по умолчанию используются PACKAGE_TARNAME и PACKAGE_VERSION, определенные через вызов AC_INIT). В некоторых случаях такой вариант может оказаться полезным. Есть надежда, что в будущих версиях Autoconf будет улучшена поддержка автоматического определения версий. Если это произойдет, поддержка вызова AM_INIT_AUTOMAKE с двумя аргументами будет исключена из Automake.

Если в вашем файле configure.ac имеются строки вида

         AC_INIT([src/foo.c]) 
         AM_INIT_AUTOMAKE([mumble], [1.5])

их следует заменить, как показано ниже

         AC_INIT([mumble], [1.5]) 
         AC_CONFIG_SRCDIR([src/foo.c]) 
         AM_INIT_AUTOMAKE

Отметим, что при обновлении configure.ac с прежних версий Automake не всегда корректен показанный выше перенос аргументов package и version из AM_INIT_AUTOMAKE в макрос AC_INIT. Первым аргументом AC_INIT должно быть имя пакета (например, GNU Automake), а не имя архива (например, automake), используемое в AM_INIT_AUTOMAKE. Autoconf пытается вывести имя архива из имени пакета и это работает в большинстве случаев. Если же это не проходит, можно использовать вызов AC_INIT с 4 аргументами для явного указания архива.

По умолчанию используются аргументы AC_DEFINE PACKAGE и VERSION, но этого можно избежать путем передачи опции no-define (17.2. Список опций Automake)

         AM_INIT_AUTOMAKE([no-define ...])

AM_PATH_LISPDIR

Ищет программу emacs и (при ее обнаружении) устанавливает в переменной lispdir полный путь к каталогу Emacs site-lisp.

Этот тест предполагает, что найденная программа emacs поддерживает Emacs Lisp (например, GNU Emacs или XEmacs). При обнаружении другого варианта emacs этот тест «зависает» (например, старая версия MicroEmacs входит в интерактивный режим, для выхода из которого нужно нажать клавиши C-x C-c3, что неочевидно для непривычных к emacs пользователей). Однако в большинстве случаев тест можно прервать клавишами C-c. Для предотвращения проблем можно установить в переменной окружения EMACS значение no или использовать опцию —with-lispdir при вызове сценария configure с явным указанием корректного пути (если имеется emacs с поддержкой Emacs Lisp).

AM_PROG_AR([ACT-IF-FAIL])

Этот макрос требуется вызывать при использовании в проекте необычного архиватора (такого как Microsoft lib). Необязательный аргумент указывает программу, выполняемую, если интерфейс архиватора не распознан. По умолчанию прерывает выполнение сценария configure с возвратом сообщения об ошибке.

AM_PROG_AS

Этот макрос применяется при наличии в проекте ассемблерного кода, указывая применяемый ассемблер (по умолчанию компилятор C) и устанавливая CCAS, а при необходимости и CCASFLAGS.

AM_PROG_CC_C_O

Этот устаревший макрос проверяет поддержку компилятором C опций -c и -o одновременно. Начиная с Automake 1.14 макрос AC_PROG_CC самостоятельно выполняет такую проверку, поэтому использовать AM_PROG_CC_C_O больше не требуется.

AM_PROG_LEX

Похож на AC_PROG_LEX, но использует сценарий missing в системах без lex (например, HP-UX 10).

AM_PROG_GCJ

Находит программу gcj или возвращает ошибку. Макрос устанавливает переменные GCJ и GCJFLAGS. Программа  gcj обеспечивает Java-интерфейс для компилятора GCC.

AM_PROG_UPC([COMPILER-SEARCH-LIST])

Находит компилятор для Unified Parallel C и устанавливает переменную UPC. По умолчанию COMPILER-SEARCH-LIST имеет значение upcc upc. Макрос прерывает сценарий configure, если компилятор Unified Parallel C не найден.

AM_MISSING_PROG(NAME, PROGRAM)

Находит инструмент поддержки PROGRAM и задает в переменной среды NAME его местоположение. Если найти PROGRAM не удалось, NAME будет задавать вызов сценария missing для получения информации об отсутствии инструмента (27.2. Сценарий missing и режим AM_MAINTAINER_MODE).

AM_SILENT_RULES

Задает менее подробный вывод в процессе сборки (21.3. Как Automake может «заглушить» make).

AM_WITH_DMALLOC

Добавляет поддержку пакета Dmalloc. Если пользователь задает configure —with-dmalloc, макрос определяет WITH_DMALLOC и добавляет -ldmalloc в LIBS.

6.4.2. Устаревший макрос

Описанный ниже макрос мог требоваться в прошлых выпусках, но его не следует применять в новом коде. Макрос будет удален из Automake и для его исключения следует воспользоваться командой autoupdate configure.ac.

AM_PROG_MKDIR_P

В Automake версий 1.8 — 1.9.6 этот макрос устанавливал для выходной переменной mkdir_p значение mkdir -p, install-sh -d или mkinstalldirs.

Сейчас Autoconf обеспечивает похожую функциональность с помощью AC_PROG_MKDIR_P, однако при этом определяется переменная MKDIR_P. Если макрос AM_PROG_MKDIR_P по прежнему используется в вашем configure.ac или переменная $(mkdir_p) присутствует в Makefile.am, рекомендуется как можно быстрей отказаться от этого, поскольку макрос и переменная будут удалены в новых выпусках Automake.

6.4.3. Приватные макросы

Перечисленные ниже макросы не следует вызывать напрямую, поскольку они предназначены для вызова из других макросов и могут быть изменены в будущем. А лучше совсем не применять их!

_AM_DEPENDENCIES

AM_SET_DEPDIR

AM_DEP_TRACKAM_OUTPUT_DEPENDENCY_COMMANDS

Служат для реализации в Automake схемы автоматического отслеживания зависимостей. Они могут автоматически вызываться Automake при необходимости.

AM_MAKE_INCLUDE

Этот макрос служит для определения способа обработки операторов include в пользовательском make. Макрос автоматически вызывается при необходимости.

AM_PROG_INSTALL_STRIP

Служит для определения версии install, которая может применяться для «вырезания» программы при установке. Макрос автоматически вызывается при необходимости.

AM_SANITY_CHECK

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

7. Каталоги

Для простых проектов, распространяющих все файлы в одном каталоге, достаточно иметь один файл Makefile.am. Более крупные проекты организуют файлы в разные каталоги дерева. Например, могут быть каталоги для исходных кодов программы, набора тестов, документации. А в очень крупных проектах могут создаваться каталоги для отдельных программ, библиотек, модулей и т. п.
традиционно каталоги организуются рекурсивно и каждый каталог содержит свой Makefile. При запуске make из каталога верхнего уровня по очереди выполняется вход в нужные каталоги и вызывается новый экземпляр make для сборки содержимого каталога.

Поскольку такой подход получил широкое распространение, Automake имеет для него встроенную поддержку. Однако рекурсивная модель имеет свои плюсы и минусы. Вполне возможно создавать пакеты с структурой каталогов, где рекурсия не применяется или используется редко, например, GNU Bison и GNU Automake ().

7.1. Рекурсия каталогов

В пакетах, использующих make recursion, файл Makefile.am на верхнем уровне должен указать Automake каталоги для сборки. Это делается с помощью переменной SUBDIRS, которая включает список каталогов, где должна выполняться та или иная сборка.

Правила для множества целей (например, all) в созданном Makefile будут выполнять команду в текущем каталоге и указанных подкаталогах. Отметим, что в каталогах SUBDIRS не требуется наличия файлов Makefile.am, достаточно Makefile (после настройки конфигурации). Это позволяет включать библиотеки из пакетов, не использующих Automake, таких как gettext (23.2. Сторонние файлы Makefile).

В пакетах с подкаталогами файл Makefile.am верхнего уровня часто бывает очень коротким. Например, Makefile.am из GNU Hello имеет вид

    EXTRA_DIST = BUGS ChangeLog.O README-alpha 
    SUBDIRS = doc intl po src tests

Когда Automake вызывает make в подкаталоге, используется та же переменная MAKE. Программа передает значение переменной AM_MAKEFLAGS при вызове make, устанавливаемую в файле Makefile., если нужно передать флаги команде make.

Каталоги, указанные в SUBDIRS, обычно являются прямыми потомками текущего каталога и каждый подкаталог содержит свой файл Makefile.am с переменной SUBDIRS, указывающей каталоги следующего уровня. Automake можно использовать для создания пакетов с произвольной глубиной вложенности.

По умолчанию Automake создает файлы Makefile, в которых обработка подкаталогов выполняется раньше обработки текущего каталога. Однако этот порядок можно поменять, помещая текущий каталог (.) в переменную SUBDIRS. Например, SUBDIRS = lib src . test приведет к обработке каталогов в порядке lib/, src/, текущий каталог и test/. Обычно каталоги с тестами обрабатываются в последнюю очередь, поскольку они предназначены для проверки собранного.

Помимо встроенной рекурсии целей Automake (all, check и т. п.), разработчик может определить свои рекурсивные цели. Это делается путем передачи имен таких целей в качестве аргументов макросу m4 AM_EXTRA_RECURSIVE_TARGETS в файле configure.ac. Automake создает правила для обработки рекурсии в таких целях и разработчик может задать реальные действия для них, определяя цели -local, как показано ниже.

    % cat configure.ac 
    AC_INIT([pkg-name], [1.0] 
    AM_INIT_AUTOMAKE 
    AM_EXTRA_RECURSIVE_TARGETS([foo]) 
    AC_CONFIG_FILES([Makefile sub/Makefile sub/src/Makefile]) 
    AC_OUTPUT 
    % cat Makefile.am 
    SUBDIRS = sub 
    foo-local: 
            @echo This will be run by "make foo". 
    % cat sub/Makefile.am 
    SUBDIRS = src 
    % cat sub/src/Makefile.am 
    foo-local: 
            @echo This too will be run by a "make foo" issued either in 
            @echo the 'sub/src/' directory, the 'sub/' directory, or the 
            @echo top-level directory.

7.2. Условные подкаталоги

Можно задать переменную SUBDIRS с условиями, если нужно собрать лишь часть пакета. Предположим, что в пакете имеются подкаталоги src/ и opt/, причем src/ следует собирать всегда, а программе configure можно указать, следует ли собирать opt/ (в примере предполагается, что для сборки opt/ нужно установить в переменной $want_opt значение yes). В результате команда make всегда должна выполняться в каталоге src/ и может выполняться также в opt/. Однако команду make dist всегда следует выполнять в обоих каталогах src/ и opt/, поскольку распространять opt/ следует в любом случае. Это означает, что создание opt/Makefile следует выполнять безусловно.

Есть два способа решения этой задачи. Можно воспользоваться условными конструкциями Automake (20. Конструкции с условием) или использовать переменные AC_SUBST. Первый способ является предпочтительным. Перед описание обоих вариантов рассмотрим переменную DIST_SUBDIRS.

7.2.1. Переменные SUBDIRS и DIST_SUBDIRS

Automake рассматривает 2 набора каталогов, заданные в переменных SUBDIRS и DIST_SUBDIRS. SUBDIRS указывает подкаталоги текущего каталога, которые нужно собрать (7.1. Рекурсия каталогов), и должна задаваться вручную. Ниже будет показано, как исключить тот или иной каталог из сборки по заданным условиям. DIST_SUBDIRS применяется в правилах, которые нужны для рекурсии во все каталоги, даже если они не используются при сборке. В приведенном выше примере каталог opt/ не используется при сборке, но включается в дистрибутив. В результате opt может не присутствовать в SUBDIRS, но должен быть указан в DIST_SUBDIRS.

Переменная DIST_SUBDIRS используется целями maintainer-clean, distclean и dist, а в остальных правилах применяется SUBDIRS. Если переменная SUBDIRS задана с использованием условной конструкции Automake, программа Automake будет автоматически определять DIST_SUBDIRS на основе возможных значений SUBDIRS и всех условий. Если SUBDIRS включает переменные AC_SUBST, DIST_SUBDIRS не удастся задать корректно, поскольку Automake не будет знать возможных значений переменных. В таких случаях нужно задавать DIST_SUBDIRS вручную.

7.2.2. Подкаталоги с AM_CONDITIONAL

Сценарию configure следует создавать Makefile для каждого каталога и определять условие сборки каталога opt/.

    ... 
    AM_CONDITIONAL([COND_OPT], [test "$want_opt" = yes]) 
    AC_CONFIG_FILES([Makefile src/Makefile opt/Makefile]) 
    ...

Переменную SUBDIRS можно определить в Makefile.am верхнего уровня, как показано ниже.

    if COND_OPT 
      MAYBE_OPT = opt 
    endif 
    SUBDIRS = src $(MAYBE_OPT)

При запуске make будет выполняться рекурсия в каталог src/ и возможно в opt/. По команде make dist будет выполняться рекурсия в src/ и opt/, поскольку make dist в отличие от make all не использует переменную SUBDIRS.

В этом случае Automake будет задавать DIST_SUBDIRS = src opt автоматически, поскольку известно, что MAYBE_OPT может при выполнении условий включать каталог opt.

7.2.3. Подкаталоги с AC_SUBST

Другим вариантом является определение MAYBE_OPT из ./configure с использованием AC_SUBST.

    ... 
    if test "$want_opt" = yes; then 
      MAYBE_OPT=opt 
    else 
      MAYBE_OPT= 
    fi 
    AC_SUBST([MAYBE_OPT]) 
    AC_CONFIG_FILES([Makefile src/Makefile opt/Makefile]) 
    ...

В этом случае файл Makefile.am на верхнем уровне будет иметь вид

    SUBDIRS = src $(MAYBE_OPT) 
    DIST_SUBDIRS = src opt

Недостаток этого варианта заключается в том, что Automake не знает всех возможных значений MAYBE_OPT, которые нужны для указания DIST_SUBDIRS.

7.2.4. Не заданные в конфигурации каталоги

Семантику DIST_SUBDIRS часто толкуют некорректно и пытаются задать условия для каталогов настройки и сборки. Здесь настройкой считается создание Makefile (это может включать запуск вложенного сценария configure).

В приведенных выше примерах предполагается создание каждого файла Makefile даже в каталогах, где сборка не выполняется. Причина этого заключается в том, что команда make dist должна распространять все каталоги (например, зависимый от платформы код), поэтому команда make dist должна выполняться рекурсивно во всех каталогах и они должны быть настроены и указаны в переменной DIST_SUBDIRS.

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

  • Переменная SUBDIRS всегда должна быть подмножеством DIST_SUBDIRS.

  • Любой каталог, указанный в DIST_SUBDIRS и SUBDIRS, должен быть настроен, т. е. включать файл Makefile, поскольку иначе рекурсивные правила make не смогут обработать каталог.

  • Все настроенные в конфигурации каталоги должны быть указаны в переменной DIST_SUBDIRS. Это нужно для того, чтобы правила очистки удаляли созданные Makefile. Разумно считать DIST_SUBDIRS переменной, в которой указаны все настроенные каталоги.

Для предотвращения рекурсии в ненастроенные каталоги нужно убедиться, что таких каталогов нет в DIST_SUBDIRS (и SUBDIRS). Например, при условном определении SUBDIRS с использованием AC_SUBST без явного задания DIST_SUBDIRS по умолчанию эта переменная будет совпадать с $(SUBDIRS). Другим вариантом является явное задание DIST_SUBDIRS = $(SUBDIRS).

Каталоги, не включенные в DIST_SUBDIRS, не будут распространяться, если для этого не приняты иные меры (например, выполнение make dist в конфигурации, где все каталоги заведомо включены в DIST_SUBDIRS, или создание цели dist-hook для распространения пропущенных каталогов).

В некоторых пакетах ненастроенные каталоги в действительности не нужно распространять. Хотя такие пакеты не требуют выполнения приведенных выше рекомендаций, есть еще одна ловушка. Если имя каталога указано в SUBDIRS или DIST_SUBDIRS, программа automake будет уверена в наличии каталога. Поэтому automake нельзя запустить в дистрибутиве с пропущенным каталогом. Одним из способов обхода такой проверки является использование метода AC_SUBST для объявления условных каталогов. Поскольку automake не знает значений переменных AC_SUBST, гарантировать их наличие программа не сможет.

7.3. Другая модель организации каталогов

Если вы читали отличную статью Peter Miller «Recursive Make Considered Harmful», предыдущие рассуждения о рекурсивном использовании make могут показаться ненужными. Для тех, кто не читал статью, отметим, что ее основной тезис заключается в том, что рекурсивные вызовы make медленны и ведут к ошибкам.

Automake обеспечивает достаточную поддержку сборки в нескольких каталогах4 с одним файлом Makefile.am для сложной структуры каталогов. По умолчанию для устанавливаемого файла из подкаталога имя каталога вырезается перед установкой. Например, приведенная ниже строка приведет к установке файла заголовков в $(includedir)/stdio.h.

    include_HEADERS = inc/stdio.h

Однако можно использовать префикс nobase_ для предотвращения этого вырезания. Приведенная ниже строка обеспечит установку заголовочного файла в $(includedir)/sys/types.h.

    nobase_include_HEADERS = sys/types.h

Префикс nobase_ следует указывать до dist_ или nodist_ при их совместном использовании (14.2. Тонкая настройка распространения). Например,

    nobase_dist_pkgdata_DATA = images/vortex.pgm sounds/whirl.ogg

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

    imagesdir = $(pkgdatadir)/images 
    soundsdir = $(pkgdatadir)/sounds 
    dist_images_DATA = images/vortex.pgm 
    dist_sounds_DATA = sounds/whirl.ogg

Этот вариант позволяет изменить один установочный каталог без изменения схемы дерева кодов. В настоящее время единственным исключением из этого правила является nobase_*_LTLIBRARIES, где нет гарантии определенного порядка установки для набора переменных без префикса nobase_.

7.4. Вложенные пакеты

В системе сборки GNU возможна произвольная глубина вложенности пакетов. Это означает, что пакет может содержать в себе другие пакеты со своим сценарием configure, файлами Makefile и т. п. Эти вложенные пакеты следует просто размещать в подкаталогах родительского пакета. Каталоги можно указать в SUBDIRS подобно другим каталогам. Однако для создания файлов Makefile в субпакетах должны использоваться свои сценарии configure, а не родительский. Это достигается с помощью макроса Autoconf AC_CONFIG_SUBDIRS.

Ниже приведен пример пакета для программы arm, которая связана с библиотекой hand в форме вложенного пакета в каталоге hand/. Файл configure.ac для arm показан ниже.

    AC_INIT([arm], [1.0]) 
    AC_CONFIG_AUX_DIR([.]) 
    AM_INIT_AUTOMAKE 
    AC_PROG_CC 
    AC_CONFIG_FILES([Makefile]) 
    # Call hand's ./configure script recursively. 
    AC_CONFIG_SUBDIRS([hand]) 
    AC_OUTPUT

Файл Makefile.am для arm имеет вид

    # Build the library in the hand subdirectory first. 
    SUBDIRS = hand 

    # Include hand's header when compiling this directory. 
    AM_CPPFLAGS = -I$(srcdir)/hand 

    bin_PROGRAMS = arm 
    arm_SOURCES = arm.c 
    # link with the hand library. 
    arm_LDADD = hand/libhand.a

Ниже приведен файл hand/configure.ac для библиотеки hand.

    AC_INIT([hand], [1.2]) 
    AC_CONFIG_AUX_DIR([.]) 
    AM_INIT_AUTOMAKE 
    AC_PROG_CC
    AM_PROG_AR 
    AC_PROG_RANLIB 
    AC_CONFIG_FILES([Makefile]) 
    AC_OUTPUT

Файл hand/Makefile.am имеет вид

    lib_LIBRARIES = libhand.a 
    libhand_a_SOURCES = hand.c

При запуске make dist из каталога верхнего уровня будет создаваться архив arm-1.0.tar.gz с кодом arm и подкаталогом hand. Этот пакет можно собрать и установить как обычные пакеты командами ./configure && make && make install (пакет hand будет собран и установлен в этом процессе).

При запуске make dist из каталога hand будет создаваться архив hand-1.2.tar.gz, содержащий лишь код вложенного пакета, который можно использовать независимо.

Целью инструкции AC_CONFIG_AUX_DIR([.]) является обеспечение поиска программами Automake и Autoconf дополнительных сценариев в текущем каталоге. Например, здесь будет два файла install-sh — в каталоге верхнего уровня пакета arm и в подкаталоге hand/ пакета hand.

Исторически по умолчанию поиск этих файлов выполняется в родительском каталоге и его родителе. Поэтому при удалении строки AC_CONFIG_AUX_DIR([.]) из файла hand/configure.ac этот субпакет будет использовать дополнительный сценарий пакета arm. Это может показаться увеличением размера (несколько килобайт), но на деле означает утрату модульности, поскольку пакет hand перестает быть самодостаточным (make dist в подкаталоге не будет работать).

Пакетам, не использующим Automake, требуются дополнительные действия для такой интеграции (23.2. Сторонние файлы Makefile).

8. Сборка программ и библиотек

Значительная часть функциональности Automake связана с упрощением сборки программ и библиотек.

8.1. Сборка программы

Для сборки программы нужно сказать Automake какие исходные коды относятся к программе и с какими библиотеками ее следует компоновать.

В этом разделе также рассматривается условная компиляция программ. Большая часть приведенных здесь описаний относится и к сборке библиотек (8.2. Сборка библиотек) и библиотекам libtool (8.3. Сборка общих библиотек).

8.1.1. Указание источников программы

В каталоге, содержащем исходный код, из которого собирается программа (в отличие от библиотеки или сценария), используется первичная переменная PROGRAMS. Программы могут устанавливаться в bindir, sbindir, libexecdir, pkglibexecdir или не устанавливаться совсем (noinst_). Они могут также собираться лишь для цели make (префикс (check_). Например,

    bin_PROGRAMS = hello

В этом простом случае результирующий файл Makefile.in будет содержать код для сборки программы hello.

С каждой программой связано несколько вспомогательных переменных, указываемых после программы. Эти переменные не обязательны и имеют разумные значения, принятые по умолчанию. Каждая переменная, ее применение и значение по умолчанию описаны ниже. Во всех случаях используется пример hello.

Переменная hello_SOURCES служит для указания исходных файлов, служащих для сборки программы

    hello_SOURCES = hello.c version.c getopt.c getopt1.c getopt.h system.h

Это ведет к компиляции каждого заданного файла .c в файл .o, затем они компонуются в hello. Если переменная hello_SOURCES не задана, используется один файл hello.c (8.5. Принятые по умолчанию значения _SOURCES).

В одном каталоге может собираться множество программ. Возможно также собрать много программ из одного файла с исходным кодом, указанного в каждом определении _SOURCES.

Файлы заголовков в определении _SOURCES будут включаться в дистрибутив, а больше нигде не нужны. Если это не очевидно, в переменную не следует включать заголовочный файл, созданный сценарием configure, поскольку его не нужно распространять. Файлы Lex (.l) и Yacc (.y) тоже могут включаться в _SOURCES (8.8. Поддержка Yacc и Lex).

8.1.2. Компоновка программ

Если нужна компоновка с библиотекой, которая не найдена сценарием configure, можно применить для этого LDADD.  Эта переменная служит для указания дополнительных объектов и библиотек с целью компоновки. Для установки флагов компоновщика переменная не подходит и для этого нужно использовать AM_LDFLAGS.

Иногда множество программ собирается в одном каталоге, но программы различаются по требованиям при компоновке. В этом случае можно использовать переменную PROG_LDADD (где PROG — имя программы, как оно указано в переменной _PROGRAMS, обычно записанное строчными буквами) для переопределения LDADD. Если эта переменная существует для данной программы, программа не будет компоноваться с использованием LDADD.

Например, в пакете GNU cpio программы pax, cpio и mt компонуются с библиотекой libcpio.a. однако rmt собирается в том же каталоге и не имеет требований к компоновке. Кроме того, mt и rmt собираются лишь в некоторых архитектурах. Ниже приведен файл src/Makefile.am для решения этой задачи (сокращенный).

    bin_PROGRAMS = cpio pax $(MT) 
    libexec_PROGRAMS = $(RMT) 
    EXTRA_PROGRAMS = mt rmt 

    LDADD = ../lib/libcpio.a $(INTLLIBS) 
    rmt_LDADD = 

    cpio_SOURCES = ... 
    pax_SOURCES = ... 
    mt_SOURCES = ... 
    rmt_SOURCES = ...

PROG_LDADD не подходит для передачи специфичных для программы флагов компоновщика (за исключением -l, -L, -dlopen и -dlpreopen), поэтому для передачи флагов применяется переменная PROG_LDFLAGS.

Иногда полезно, чтобы программа зависела от некой иной цели, которая не является частью программы. Это можно организовать с помощью переменной PROG_DEPENDENCIES или EXTRA_PROG_DEPENDENCIES. Обе переменные содержат список зависимостей, но интерпретации различаются. Поскольку эти зависимости связаны с правилом компоновки, используемым при создании программы, в переменных следует обычно перечислять файлы, используемые командой компоновки (*.$(OBJEXT), *.a, *.la). В редких случаях может потребоваться добавление иных типов файлов, таких как сценарии компоновщиков, но указание исходных файлов в _DEPENDENCIES является ошибкой. Если некоторые исходные файлы нужно собрать до того, как будут собраны все компоненты программы, следует применять переменную BUILT_SOURCES.

Если переменная PROG_DEPENDENCIES не задана, ее определяет Automake, присваивая значение переменной PROG_LDADD, из которого удалено большинство подстановок -l, -L, -dlopen и -dlpreopen. Оставшиеся конфигурационные подстановки включают лишь $(LIBOBJS) и $(ALLOCA), поскольку они заведомо не приводят к созданию недопустимых значений для PROG_DEPENDENCIES. Использование переменных _DEPENDENCIES проиллюстрировано в параграфе 8.1.3. Условная компиляция исходного кода.

Переменная EXTRA_PROG_DEPENDENCIES может быть полезна в случаях, когда нужно просто расширить созданную automake переменную PROG_DEPENDENCIES, а не заменить ее.

Рекомендуется избегать опций -l в LDADD и PROG_LDADD при ссылках на библиотеки, собираемые пакетом. Вместо этого следует явно указать имя файла библиотеки, как показано выше в примере cpio. Опцию -l следует использовать лишь для перечисления сторонних библиотек. Если следовать этому правилу, подразумеваемое значение PROG_DEPENDENCIES будет включать список локальных библиотек, не включая остальных.

8.1.3. Условная компиляция исходного кода

Можно поместить подстановку (например, @FOO@ или $(FOO), где FOO определена через AC_SUBST) в переменную _SOURCES. Это сложно объяснить, но такая подстановка не работает и Automake будет давать ошибку. К счастью есть два других способа получить нужный результат.

Подстановки _LDADD

Automake нужно знать все исходные файлы, которые могут применяться при сборке программы, даже если некоторые из них используются не всегда. Файлы, используемые лишь при определенных условиях, следует указывать в подходящих переменных EXTRA_. Например, если hello-linux.c или hello-generic.c условно включаются в hello, Makefile.am может иметь вид

    bin_PROGRAMS = hello 
    hello_SOURCES = hello-common.c 
    EXTRA_hello_SOURCES = hello-linux.c hello-generic.c 
    hello_LDADD = $(HELLO_SYSTEM) 
    hello_DEPENDENCIES = $(HELLO_SYSTEM)

Затем можно организовать подстановку $(HELLO_SYSTEM) в configure.ac

    ... 
    case $host in 
      *linux*) HELLO_SYSTEM='hello-linux.$(OBJEXT)' ;; 
      *)       HELLO_SYSTEM='hello-generic.$(OBJEXT)' ;; 
    esac 
    AC_SUBST([HELLO_SYSTEM]) 
    ...

В этом случае переменная HELLO_SYSTEM будет заменена hello-linux.o или hello-generic.o и добавлена в обе переменные hello_DEPENDENCIES и hello_LDADD для использования при сборке и компоновке.

Условные конструкции Automake

Например, можно использовать приведенную ниже конструкцию Automake в файле Makefile.am при сборке hello.

    bin_PROGRAMS = hello 
    if LINUX 
    hello_SOURCES = hello-linux.c hello-common.c 
    else 
    hello_SOURCES = hello-generic.c hello-common.c 
    endif

В этом случае в файле configure.ac следует задать условие LINUX с использованием AM_CONDITIONAL (20. Конструкции с условием). При использовании таких условных конструкций не требуется использовать переменную EXTRA_, поскольку Automake будет проверять содержимое каждой переменной для создания полного списка исходных файлов. Если программа использует много файлов, может оказаться удобным оператор +=.

    bin_PROGRAMS = hello 
    hello_SOURCES = hello-common.c 
    if LINUX 
    hello_SOURCES += hello-linux.c 
    else 
    hello_SOURCES += hello-generic.c 
    endif

8.1.4. Условная компиляция программ

Иногда полезно указать собираемые программы во время настройки конфигурации. Например, в GNU cpio компоненты mt и rmt собираются только в особых случаях. Здесь также применяются подстановки и конструкции с условиями.

Подстановки configure

В этом случае нужно уведомить Automake обо всех программах, которые могут собираться, но в то же время включать в создаваемый файл Makefile.in программы, заданные сценарием configure. Это делается путем подстановки сценарием configure значение для каждого определения _PROGRAMS с указанием необязательных программ в EXTRA_PROGRAMS.

    bin_PROGRAMS = cpio pax $(MT) 
    libexec_PROGRAMS = $(RMT) 
    EXTRA_PROGRAMS = mt rmt

Как отмечено в параграфе 8.20. Поддержка расширений исполняемых файлов, Automake будет переопределять bin_PROGRAMS, libexec_PROGRAMS и EXTRA_PROGRAMS, добавляя суффикс $(EXEEXT) к каждому двоичному файлу. Очевидно, что невозможно переопределить значения полученные путем подстановок при выполнении configure, поэтому нужно самостоятельно добавить суффиксы $(EXEEXT), как в AC_SUBST([MT], [‘mt${EXEEXT}’]).

Условные конструкции Automake

Можно также использовать условные конструкции Automake (20. Конструкции с условием) для выбора собираемых программ. В этом случае не нужно заботиться о переменных $(EXEEXT) и EXTRA_PROGRAMS.

    bin_PROGRAMS = cpio pax 
    if WANT_MT 
      bin_PROGRAMS += mt 
    endif 
    if WANT_RMT 
      libexec_PROGRAMS = rmt 
    endif

8.2. Сборка библиотек

Сборка библиотек похожа на сборку программ. В этом случае применяется первичная переменная LIBRARIES. Библиотеки могут устанавливаться в libdir или pkglibdir. В параграфе 8.3. Сборка общих библиотек рассмотрено создание библиотек общего пользования (shared) с помощью libtool и LTLIBRARIES.

Каждая переменная _LIBRARIES содержит список собираемых библиотек. Например, для создания библиотеки libcpio.a без ее установки можно задать

    noinst_LIBRARIES = libcpio.a 
    libcpio_a_SOURCES = ...

Включаемые в библиотеку источники задаются так же, как для программ, через переменные _SOURCES. Отметим, что имена библиотек преобразуются (3.5. Именование производных переменных), поэтому переменная _SOURCES для libcpio.a будет иметь вид libcpio_a_SOURCES, а не libcpio.a_SOURCES.

В библиотеку можно добавить объекты с помощью переменной LIBRARY_LIBADD, которую следует применять для объектов, заданных сценарием configure. Например, для cpio можно указать

    libcpio_a_LIBADD = $(LIBOBJS) $(ALLOCA)

источники для дополнительных объектов, которых не было во время работы configure, должны добавляться в переменную BUILT_SOURCES (9.4. Исходные файлы для сборки).

Сборка статической библиотеки выполняется путем компиляции всех объектных файлов и последующего вызова $(AR) $(ARFLAGS) с именем библиотеки и списком объектов. Завершается процесс вызовом $(RANLIB) для этой библиотеки. Следует вызвать макрос AC_PROG_RANLIB в файле configure.ac для указания RANLIB (иначе Automake будет «жаловаться»). Следует также вызвать AM_PROG_AR для задания AR, чтобы обеспечить поддержку необычных архивов, таких как Microsoft lib. По умолчанию ARFLAGS имеет значение cru, которое можно переопределить путем установки в файле Makefile.am или вызова AC_SUBST из файла configure.ac. Можно переопределить переменную AR путем задания на уровне библиотеки переменной maude_AR (8.4. Переменные для программ и библиотек).

Следует соблюдать осторожность при выборе условных компонент библиотеки. Сборка пустой библиотеки не переносима, поэтому следует убедиться в наличии в библиотеке хотя бы одного объекта.

Для использования статической библиотеки при сборке программы в эту программу добавляется переменная LDADD. В примере показана статическая компоновка cpio с библиотекой libcpio.a.

    noinst_LIBRARIES = libcpio.a 
    libcpio_a_SOURCES = ... 

    bin_PROGRAMS = cpio 
    cpio_SOURCES = cpio.c ... 
    cpio_LDADD = libcpio.a

8.3. Сборка общих библиотек

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

8.3.1. Концепции Libtool

Libtool абстрагирует разделяемые и статические библиотеки в единую концепцию библиотек libtool. Библиотеками  libtool являются файлы с расширением .la, которые могут быть статическими разделяемыми или теми и другими сразу. Точную природу библиотеки невозможно определить до запуска сценария ./configure. Не все платформы поддерживают каждый тип библиотек и пользователь может явно задать тип собираемой библиотеки, однако сопровождающие пакет разработчики могут настроить принятую по умолчанию сборку.

Поскольку объектные файлы для разделяемых и статических библиотек должны компилироваться по-разному, libtool применяется и при компиляции. Объектные файлы, собираемые libtool, называют объектами libtool и для них используется расширение .lo. Из этих объектов собираются библиотеки libtool.

Не следует принимать каких-либо допущений о структуре файлов .la и .lo, а также способах их создания программой libtool — это забота libtool. Однако наличие этих файлов имеет значение, поскольку они служат целями и зависимостями в правилах Makefile при сборке библиотек libtool. В некоторых случаях могут потребоваться ссылки на такие файлы, например, при условном задании зависимостей для сборки исходных файлов по условию (8.3.4. Библиотеки Libtool с условными источниками).

При разработке подключаемых модулей (plug-in) с динамической загрузкой следует обратить внимание на libltdl — библиотеку расширения libtool. Она обеспечивает переносимую функцию dlopen для динамической загрузки библиотек libtool и поддерживает статическую компоновку, когда она необходима.

Описание использования libtool вместе с Automake достаточно подробно рассмотрено в руководстве libtool.

8.3.2. Сборка библиотек Libtool

Automake использует libtool для сборки библиотек, объявленных в LTLIBRARIES. Каждая переменная _LTLIBRARIES содержит список собираемых библиотек libtool. Например, для создания библиотеки libgettext.la и ее установки в libdir

    lib_LTLIBRARIES = libgettext.la 
    libgettext_la_SOURCES = gettext.c gettext.h ...

Automake заранее определяет переменную pkglibdir, что позволяет использовать pkglib_LTLIBRARIES для установки библиотек в $(libdir)/@PACKAGE@/.

Если gettext.h является общедоступным заголовочным файлом, который нужно установить для использования библиотеки, его следует объявить в переменной _HEADERS, а не в libgettext_la_SOURCES. В последней переменной следует указывать файлы заголовков, которые являются внутренними и не относятся к общедоступному интерфейсу.

    lib_LTLIBRARIES = libgettext.la 
    libgettext_la_SOURCES = gettext.c ... 
    include_HEADERS = gettext.h ...

Пакет может собрать и установить такую библиотеку вместе с использующими ее программами. Эту зависимость следует указывать в LDADD. Ниже приведен пример сборки программы hello, компонуемой с libgettext.la.

    lib_LTLIBRARIES = libgettext.la 
    libgettext_la_SOURCES = gettext.c ... 

    bin_PROGRAMS = hello 
    hello_SOURCES = hello.c ... 
    hello_LDADD = libgettext.la

Выбор статической или динамической компоновки hello с libgettext.la пока не определен и будет зависеть от конфигурации libtool и возможностей хоста.

8.3.3. Условная сборка библиотек Libtool

Подобно условной сборке программ (8.1.4. Условная компиляция программ), есть два варианта сборки библиотек по условию — подстановки Autoconf AC_SUBST и условные конструкции Automake.

Для реализации важно то, что место установки библиотеки имеет значение для libtool и должно быть указано к моменту компоновки с помощью опции -rpath. Для библиотек, место установки которых известно во время работы Automake, программа Automake будет автоматически передавать libtool подходящую опцию -rpath. Это выполняется для библиотек, явно указанных в переменных _LTLIBRARIES, таких как lib_LTLIBRARIES.

Однако для библиотек, определяемых во время настройки конфигурации (упоминаемых в EXTRA_LTLIBRARIES), Automake не знает окончательный каталог установки. В таких случаях нужно вручную добавлять опцию -rpath в подходящую переменную _LDFLAGS.

В приведенных ниже примерах показаны различия между этими методами. В первом примере для WANTEDLIBS используется подстановка AC_SUBST, выполняемая сценарием ./configure, где устанавливается libfoo.la, libbar.la, обе или ни одной библиотеки. Хотя $(WANTEDLIBS) присутствует в lib_LTLIBRARIES, Automake не может знать о libfoo.la или libbar.la при создании правила компоновки библиотек. Поэтому аргумент -rpath должен быть задан явно.

    EXTRA_LTLIBRARIES = libfoo.la libbar.la 
    lib_LTLIBRARIES = $(WANTEDLIBS) 
    libfoo_la_SOURCES = foo.c ... 
    libfoo_la_LDFLAGS = -rpath '$(libdir)' 
    libbar_la_SOURCES = bar.c ... 
    libbar_la_LDFLAGS = -rpath '$(libdir)'

ниже показан файл Makefile.am при использовании условных конструкций Automake (WANT_LIBFOO и WANT_LIBBAR). Здесь Automake может определить -rpath, поскольку обе библиотеки явно попадут в $(libdir) при установке.

    lib_LTLIBRARIES = 
    if WANT_LIBFOO 
    lib_LTLIBRARIES += libfoo.la 
    endif
    if WANT_LIBBAR 
    lib_LTLIBRARIES += libbar.la 
    endif 
    libfoo_la_SOURCES = foo.c ... 
    libbar_la_SOURCES = bar.c …

8.3.4. Библиотеки Libtool с условными источниками

Условную компиляцию исходных кодов библиотек можно реализовать так же, как это делается для программ (8.1.3. Условная компиляция исходного кода). Единственным отличием является использование переменной _LIBADD вместо _LDADD и указание объектов libtool (файлы .lo).

Возвращаясь к примеру hello из параграфа 8.1.3. Условная компиляция исходного кода, можно собрать библиотеку libhello.la, используя hello-linux.c или hello-generic.c, указанных в файле Makefile.am.

    lib_LTLIBRARIES = libhello.la 
    libhello_la_SOURCES = hello-common.c 
    EXTRA_libhello_la_SOURCES = hello-linux.c hello-generic.c 
    libhello_la_LIBADD = $(HELLO_SYSTEM) 
    libhello_la_DEPENDENCIES = $(HELLO_SYSTEM)

Нужно убедиться, что configure задает в HELLO_SYSTEM значение hello-linux.lo или hello-generic.lo.

Можно также просто воспользоваться условной конструкцией Automake, показанной ниже.

    lib_LTLIBRARIES = libhello.la 
    libhello_la_SOURCES = hello-common.c 
    if LINUX 
    libhello_la_SOURCES += hello-linux.c 
    else 
    libhello_la_SOURCES += hello-generic.c 
    endif

8.3.5. Вспомогательные библиотеки Libtool

Иногда нужно собрать библиотеки libtool, которые не следует устанавливать. Их называют вспомогательными (libtool convenience libraries) и эти библиотеки обычно применяются для инкапсуляции множества суббиблиотек, которые позднее собираются в одну большую устанавливаемую библиотеку.

Вспомогательные библиотеки Libtool объявляются в переменных без каталогов, таких как noinst_LTLIBRARIES, check_LTLIBRARIES или даже EXTRA_LTLIBRARIES. В отличие от устанавливаемых библиотек libtool, им не нужна опция -rpath в момент компоновки (это единственное отличие).

Вспомогательные библиотеки из noinst_LTLIBRARIES собираются всегда, а библиотеки из check_LTLIBRARIES — лишь по команде make check. Библиотеки из переменной EXTRA_LTLIBRARIES никогда не создаются явно, Automake выводит правила для их сборки, но при отсутствии библиотеки в зависимостях Makefile она не будет собрана (именно поэтому EXTRA_LTLIBRARIES применяется для условной компиляции).

Ниже приведен пример, объединяющий вспомогательные библиотеки из подкаталогов в одну библиотеку libtop.la.

    # -- Top-level Makefile.am -- 
    SUBDIRS = sub1 sub2 ... 
    lib_LTLIBRARIES = libtop.la 
    libtop_la_SOURCES = 
    libtop_la_LIBADD = \ 
      sub1/libsub1.la \ 
      sub2/libsub2.la \ 
      ... 

    # -- sub1/Makefile.am -- 
    noinst_LTLIBRARIES = libsub1.la 
    libsub1_la_SOURCES = ... 

    # -- sub2/Makefile.am -- 
    # showing nested convenience libraries 
    SUBDIRS = sub2.1 sub2.2 ... 
    noinst_LTLIBRARIES = libsub2.la 
    libsub2_la_SOURCES = 
    libsub2_la_LIBADD = \ 
      sub21/libsub21.la \ 
      sub22/libsub22.la \ 
      ...

При таком подходе следует помнить, что automake будет предполагать, что libtop.la будет связываться с помощью компоновщика C. Это обусловлено тем, что значение libtop_la_SOURCES пусто, поэтому automake подразумевает язык C. При непустом значении libtop_la_SOURCES программа automake выберет компоновщик в соответствии с параграфом 8.14.3.1. Выбор компоновщика.

Если какая-то из суббиблиотек содержит источники, отличные от C, важно выбрать правильный компоновщик. Одним из способов является «фиктивное» указание присутствия отличного от C исходного кода, заставляющее automake выбрать подходящий компоновщик. Ниже приведен файл Makefile верхнего уровня для выбора компоновки C++.

    SUBDIRS = sub1 sub2 ... 
    lib_LTLIBRARIES = libtop.la 
    libtop_la_SOURCES = 
    # Dummy C++ source to cause C++ linking. 
    nodist_EXTRA_libtop_la_SOURCES = dummy.cxx 
    libtop_la_LIBADD = \ 
      sub1/libsub1.la \ 
      sub2/libsub2.la \ 
      ...

Переменные EXTRA_*_SOURCES служат для отслеживания исходных файлов, которые могут компилироваться (полезно при условной компиляции с использованием AC_SUBST, 8.1.3. Условная компиляция исходного кода), а префикс nodist_ указывает источники, которые не нужно распространять (8.4. Переменные для программ и библиотек).  Файл dummy.cxx не обязан присутствовать в дереве исходного кода. Если же есть какой-то реальный файл, отличный от C, в libtop_la_SOURCES, нет смысла указывать фиктивный в nodist_EXTRA_libtop_la_SOURCES.

8.3.6. Модули Libtool

Библиотеки libtool для динамической загрузки (dlopen), указываемые libtool параметром -module при компоновке.

    pkglib_LTLIBRARIES = mymodule.la 
    mymodule_la_SOURCES = doit.c 
    mymodule_la_LDFLAGS = -module

Обычно Automake требует, чтобы имена библиотек начинались с lib, однако при сборке динамически загружаемого модуля может возникнуть желание воспользоваться «нестандартным» именем. Automake не будет «жаловаться» на такие имена, если известно, что собираемая библиотека является модулем libtool, т. е. -module явно присутствует в переменной библиотеки _LDFLAGS (или в общей переменной AM_LDFLAGS, если для библиотеки не задается _LDFLAGS).

Как обычно, переменные AC_SUBST неведомы Automake, поскольку их значение не известны во время работы automake. Поэтому при установке -module через такие переменные Automake не заметит их и будет работать как с обычной библиотекой libtool, строго относясь к именам.

Если переменная mymodule_la_SOURCES не задана, подразумевается один файл mymodule.c (8.5. Принятые по умолчанию значения _SOURCES).

8.3.7. _LIBADD, _LDFLAGS, _LIBTOOLFLAGS

Как отмечено в предыдущем параграфе, следует применять переменную LIBRARY_LIBADD для указания списка дополнительных объектов (.lo) или библиотек libtool (.la), добавляемых в LIBRARY. В переменной LIBRARY_LDFLAGS указываются дополнительные флаги компоновки libtool, такие как -version-info, -static и т. п.

Команда libtool поддерживает два типа опций — базовые и зависящие от режима. Специфические для режима опции, такие как отмеченные выше флаги компоновки, следует объединять с другими флагами, передаваемыми инструментам при вызове из libtool (отсюда использование LIBRARY_LDFLAGS для флагов компоновки libtool). Базовые опции включают —tag=TAG и —silent и их следует указывать до выбора режиме в строке команды, а в Makefile.am их следует перечислять в переменной LIBRARY_LIBTOOLFLAGS. Если переменная LIBRARY_LIBTOOLFLAGS не задана, взамен применяется AM_LIBTOOLFLAGS.

Эти флаги передаются libtool после опции —tag=TAG, найденной Automake (если она есть), поэтому для переопределения или дополнения опции —tag=TAG следует использовать LIBRARY_LIBTOOLFLAGS (или AM_LIBTOOLFLAGS).

Правила libtool используют также переменную LIBTOOLFLAGS, которую не следует устанавливать Makefile.am, она является пользовательской (27.6. Порядок переменных флагов). Это позволяет пользователю применять, например, команды make LIBTOOLFLAGS=—silent. Отметим, что на уровень информативности вывода libtool может также влиять поддержка в Automake «тихих» правил (21.3. Как Automake может «заглушить» make).

8.3.8. LTLIBOBJS и LTALLOCA

Там, где обычные библиотеки могут включать $(LIBOBJS) или $(ALLOCA) (8.6. Особая обработка LIBOBJS и ALLOCA), библиотека libtool должна использовать $(LTLIBOBJS) или $(LTALLOCA). Это обусловлено тем, что объектные файлы, с которыми работает libtool не обязательно используют расширение .o.

В настоящее время определение LTLIBOBJS из LIBOBJS выполняется автоматически программой Autoconf.

8.3.9. Проблемы, связанные с использованием Libtool

8.3.9.1. Error: ‘required file `./ltmain.sh’ not found’

Libtool распространяется с инструментом libtoolize, который устанавливает файлы поддержки libtool для пакета. При запуске этой команды создается и устанавливается сценарий ltmain.sh, который следует выполнить до запуска aclocal и automake.

При обновлении старых пакетов эта проблема может возникать в результате того, что, начиная с Automake 1.6, запуск libtoolize был исключен из действий Automake и функциональность перенесена в команду autoreconf. Замена прежних сценариев bootstrap и autogen.sh вызовом autoreconf должна устранить в будущем эту несовместимость.

8.3.9.2. Объекты ‘created with both libtool and without’

Иной раз один и тот же файл с исходным кодом используется для сборки библиотеки libtool и другой цели (программа или библиотека). Рассмотрим файл Makefile.am

    bin_PROGRAMS = prog 
    prog_SOURCES = prog.c foo.c ... 

    lib_LTLIBRARIES = libfoo.la 
    libfoo_la_SOURCES = foo.c ...

В описанном тривиальном случае проблемы можно избежать, компонуя libfoo.la с prog вместо указания foo.c в prog_SOURCES. Но в реальности может потребоваться разделение prog и libfoo.la. Технически это означает, что нужно собрать foo.$(OBJEXT) для prog и foo.lo для libfoo.la. Проблема заключается в том, что в процессе создания foo.lo libtool может удалить (или заменить foo.$(OBJEXT), а этого следует избегать. Поэтому Automake при обнаружении такой ситуации будет выдавать сообщение вида

    object 'foo.$(OBJEXT)' created both with libtool and without

Обход этой проблемы заключается в обеспечении разных базовых имен объектов. Как отмечено в параграфе 27.7. Переименование объектных файлов, это выполняется автоматически при установке флагов на уровне каждой цели.

    bin_PROGRAMS = prog 
    prog_SOURCES = prog.c foo.c ... 
    prog_CFLAGS = $(AM_CFLAGS) 

    lib_LTLIBRARIES = libfoo.la 
    libfoo_la_SOURCES = foo.c ...

Добавление prog_CFLAGS = $(AM_CFLAGS) почти ничего не делает (no-op), поскольку при наличии переменной prog_CFLAGS она используется вместо AM_CFLAGS. Однако побочным эффектом этого является компиляция prog.c и foo.c как prog-prog.$(OBJEXT) и prog-foo.$(OBJEXT) для решения упомянутой проблемы.

8.4. Переменные для программ и библиотек

С каждой программой связан набор переменных, которые можно использовать для управления сборкой программы. Подобный список имеется и для каждой библиотеки. В качестве базы для именования таких переменных служит каноническое имя программы или библиотеки, которое в приведенном ниже списке обозначено maude. В своих файлах Makefile.am вы должны будете указать реальные имена. В приведенном списке maude считается программой, но практически те же правила применимы для статических и динамических библиотек, а различия отмечены в тексте.

maude_SOURCES

При наличии этой переменной она содержит список исходных файлов, компилируемых для сборки программы. Эти файлы по умолчанию добавляются в дистрибутив программы. При сборке программы Automake будет обеспечивать компиляцию каждого источника в один файл .o (.lo при использовании libtool). Обычно эти объектные файлы именуются по источнику, но это может быть изменено. Если файл в переменной _SOURCES имеет нераспознанное расширение, Automake может выбрать для него один из двух вариантов. При наличии правила для суффиксов для преобразования файлов с нераспознанным расширением в файлы .o, automake будет использовать файл как другие источники (8.18. Поддержка других языков). В противном случае файл игнорируется, как будто это заголовочный файл.

Префиксы dist_ и nodist_ могут служить для контроля распространения файлов из _SOURCES. Префикс dist_ является избыточным, поскольку источники распространяются по умолчанию, но он может применяться для четкости понимания. Можно иметь варианты dist_ и nodist_ для данной переменной _SOURCES, что позволяет распространять одни файлы, не распространяя другие. Например,

         nodist_maude_SOURCES = nodist.c 
         dist_maude_SOURCES = dist-me.c

По умолчанию выходной (в Unix-системах .o) будет помещаться в текущий каталог сборки. Однако при действии в текущем каталоге опции subdir-objects файл .o будет помещаться в подкаталог, названный по исходному файлу. Например, при включенной опции subdir-objects файл sub/dir/file.c будет компилироваться в sub/dir/file.o. некоторые люди предпочитают такой режим работы. Опцию subdir-objects можно задать в файле AUTOMAKE_OPTIONS.

EXTRA_maude_SOURCES

Automake нужно знать список предназначенных для компиляции файлов статически. Во-первых, это единственный способ указать Automake языки, поддержка которых требуется в данной файле Makefile.in5. Это означает, например, что можно использовать подстановку вида @my_sources@ для переменной _SOURCES’. Если нужна компиляция источников по условию и configure применяется для подстановки имен соответствующих объектов (например, _LDADD), нужно указать соответствующие исходные файлы в переменной EXTRA_. Переменная также поддерживает префиксы dist_ и nodist_. Например, nodist_EXTRA_maude_SOURCES будет перечислять дополнительные исходные файлы, которые нужно собрать, но не распространять.

maude_AR

Статическая библиотека по умолчанию создается вызовом $(AR) $(ARFLAGS), за которым следует имя библиотеки и помещаемые в нее объекты. Это можно переопределить установкой переменной _AR. Обычно это применяется для C++, поскольку некоторые компиляторы C++ требуют специального вызова для создания экземпляров всех шаблонов, которые следует включить в библиотеку. Например, для компилятора SGI C++ нужно установить переменную вида

         libmaude_a_AR = $(CXX) -ar -o

maude_LIBADD

Дополнительные объекты можно добавлять в библиотеку с помощью переменной _LIBADD. Например, это следует использовать для объектов, заданных configure (8.2. Сборка библиотек). Для библиотек libtool переменная maude_LIBADD может указывать также другие объекты libtool.

maude_LDADD

Дополнительные объекты (*.$(OBJEXT)) и библиотеки (*.a, *.la) можно добавить в в программу, указав их в переменной _LDADD. Например, это можно использовать для объектов, заданных configure (8.1.2. Компоновка программ).

Переменные _LDADD и _LIBADD не подходят для передачи зависящих от программы флагов компоновщика (за исключением -l, -L, -dlopen и -dlpreopen). Для этого служит переменная _LDFLAGS. Например, если файл configure.ac использует AC_PATH_XTRA, можно скомпоновать программу с библиотекой X

         maude_LDADD = $(X_PRE_LIBS) $(X_LIBS) $(X_EXTRA_LIBS)

Рекомендуется применять опции -l и -L лишь при ссылках на сторонние библиотеки и указывать явно имена файлов для всех библиотек, создаваемых пакетом. Это гарантирует корректную установку переменной maude_DEPENDENCIES по умолчанию.

maude_LDFLAGS

Эта переменная служит для передачи дополнительных флагов этапу компоновки программы или общей библиотеки, переопределяя переменную AM_LDFLAGS.

maude_LIBTOOLFLAGS

Эта переменная служит для передачи дополнительных опций программе libtool, переопределяя переменную AM_LIBTOOLFLAGS. Опции выводятся перед опцией libtool —mode=MODE, поэтому им не следует зависеть от режима (они относятся к флагам компилятора или компоновщика) (9.4. Исходные файлы для сборки).

maude_DEPENDENCIES

EXTRA_maude_DEPENDENCIES

Иногда нужна зависимость цели (программа или библиотека) он файла, который не является ее частью. Это можно задать с помощью переменной _DEPENDENCIES, от которой зависит каждая цель без дальнейшей интерпретации.      Поскольку эти зависимости связаны с правилом компоновки, используемым для создания цели, переменная обычно должна указывать файлы, используемые командой компоновки. Это файлы *.$(OBJEXT), *.a или *.la для программ, *.lo и *.la для библиотек Libtool и *.$(OBJEXT) для статических библиотек. В редких случаях может потребоваться добавление других файлов, таких как сценарии компоновки, но включение исходных файлов в _DEPENDENCIES является ошибкой. Если какой-то исходный файл нужно скомпилировать до остальных компонент собираемой программы, следует использовать переменную BUILT_SOURCES (9.4. Исходные файлы для сборки).

Если переменная _DEPENDENCIES не задана, Automake создает ее. Автоматически заданное значение содержит переменную _LDADD или _LIBADD, откуда удалено большинство подстановок configure (-l, -L, -dlopen, -dlpreopen). Остаются лишь подстановки $(LIBOBJS) и $(ALLOCA), поскольку они заведомо не ведут к созданию непригодных значений _DEPENDENCIES.

_DEPENDENCIES чаще применяется для условной компиляции с переменной AC_SUBST, содержащей список объектов (8.1.3. Условная компиляция исходного кода). Переменная EXTRA_*_DEPENDENCIES может быть полезна, когда нужно просто дополнить созданную автоматически переменную _DEPENDENCIES без ее замены.

maude_LINK

Можно переопределить компоновщик на уровне программы. По умолчанию компоновщик выбирается на основе языка, используемого программой. Например, для программы с исходным кодом C++ будет использоваться в качестве компоновщика компилятор C++. Переменная _LINK содержит имя программы, которой могут быть переданы в качестве аргументов имена всех файлов .o и библиотек для сборки. Отметим, что имя базовой программы не передается в _LINK и обычно используется $@, как показано ниже

         maude_LINK = $(CCLD) -magic -o $@

Если переменная _LINK не задана, она может быть создана и использована Automake, благодаря установленным на уровне цели флагам, таким как _CFLAGS, _LDFLAGS и _LIBTOOLFLAGS, если они применяются.

maude_CCASFLAGS

maude_CFLAGS

maude_CPPFLAGS

maude_CXXFLAGS

maude_FFLAGS

maude_GCJFLAGS

maude_LFLAGS

maude_OBJCFLAGS

maude_OBJCXXFLAGS

maude_RFLAGS

maude_UPCFLAGS

maude_YFLAGS

Automake позволяет установить флаги компиляции на уровне программы или библиотеки. Один файл исходного кода можно включить в несколько программ и он может компилироваться для каждой со своими флагами. Это работает с любым языком, напрямую поддерживаемым Automake. Такими флагами являются _CCASFLAGS, _CFLAGS, _CPPFLAGS, _CXXFLAGS, _FFLAGS, _GCJFLAGS, _LFLAGS, _OBJCFLAGS, _OBJCXXFLAGS,     _RFLAGS, _UPCFLAGS и _YFLAGS. При использовании таких флагов Automake будет выбирать разные имена для промежуточных объектных файлов. Обычно файл, например, sample.c будет компилироваться в sample.o, однако при установке для программы переменной _CFLAGS, объект может быть назван, например, maude-sample.o (27.7. Переименование объектных файлов).

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

         maude_CFLAGS = ... ваши флаги ... $(AM_CFLAGS)

В параграфе 27.6. Порядок переменных флагов рассмотрено взаимодействие пользовательских переменных, теневых переменных AM_ и переменных на уровне цели.

maude_SHORTNAME

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

         bin_PROGRAMS = maude 
         maude_CPPFLAGS = -DSOMEFLAG 
         maude_SHORTNAME = m 
         maude_SOURCES = sample.c ...

объектный файл будет назван m-sample.o вместо maude-sample.o. Это свойство редко применяется на практике и рекомендуется не применять его без необходимости.

8.5. Принятые по умолчанию значения _SOURCES

Переменные _SOURCES служат для задания исходных файлов программ (8.1. Сборка программы), библиотек (8.2. Сборка библиотек) и библиотек Libtool (8.3. Сборка общих библиотек). Если такая переменная не задана для цели, Automake будет определять ее. По умолчанию компилируется один файл C, чье базовое имя является именем цели с заменой расширения значением переменной AM_DEFAULT_SOURCE_EXT (по умолчанию .c). Например, при наличии в файле Makefile.am приведенной ниже строки без соответствующей переменной libfoo_a_SOURCES

    lib_LIBRARIES = libfoo.a sub/libc++.a

будет собрана библиотека libfoo.a с использованием подразумеваемого имени файла libfoo.c и библиотека sub/libc++.a из sub/libc++.c. В старой версии библиотека sub/libc++.a была бы собрана из файла sub_libc___a.c, т. е. по умолчанию имя исходного файла канонизировалось по имени цели, а затем добавлялось расширение .c. Новое поведение представляется более осмысленным, но для совместимости с прежними версиями automake будет использовать старое имя, если есть файл или правило с таким именем, а переменная AM_DEFAULT_SOURCE_EXT не задана.)

Принятые по умолчанию источники полезны в основном для тестовых наборов при сборке множества тестов из одного источника, например, переменные

    check_PROGRAMS = test1 test2 test3 
    AM_DEFAULT_SOURCE_EXT = .cpp

задают сборку программ test1, test2, test3 из файлов test1.cpp, test2.cpp и test3.cpp. Без последней строки для сборки применялись бы файлы test1.c, test2.c, test3.c.

Другим вариантом является сборка множества модулей Libtool (moduleN.la), каждый из которых определен в своем файле (moduleN.c).

    AM_LDFLAGS = -module 
    lib_LTLIBRARIES = module1.la module2.la module3.la

Есть ситуации, в которых следует избегать компиляции подразумеваемых источников, — когда цель не следует собирать из источников. Выше был рассмотрен пример (4.2 Сборка двух программ из одного файла), где все цели уже скомпилированы и нужно просто собрать их с помощью переменной _LDADD. В этом случае нужно указать пустую переменную _SOURCES, чтобы программа automake не задавала компиляцию принятых по умолчанию источников.

    bin_PROGRAMS = target 
    target_SOURCES = 
    target_LDADD = libmain.a libmisc.a

8.6. Особая обработка LIBOBJS и ALLOCA

В переменных $(LIBOBJS) и $(ALLOCA) перечисляются объектные файлы, которые следует скомпилировать в проект для реализации функций, которые отсутствуют или не работают в системе. Переменные задает сценарий configure.

Эти переменные определяются макросами Autoconf, такими как AC_LIBOBJ, AC_REPLACE_FUNCS или AC_FUNC_ALLOCA. Многие макросы Autoconf вызывают AC_LIBOBJ или AC_REPLACE_FUNCS для заполнения списка $(LIBOBJS).

Работа с этими переменными очень похожа на условную компиляцию с использованием переменных AC_SUBST (8.1.3. Условная компиляция исходного кода). Т. е. при сборке программы переменные $(LIBOBJS) и $(ALLOCA) следует добавить в связанную переменную *_LDADD или в переменную *_LIBADD при сборке библиотеки. Однако нет необходимости перечислять соответствующие источники в EXTRA_*_SOURCES или задавать *_DEPENDENCIES.  Automake автоматически добавляет $(LIBOBJS) и $(ALLOCA) в зависимости и автоматически находит соответствующие исходные файлы (путем отслеживания вызовов макроса Autoconf AC_LIBSOURCE ). Если переменные *_DEPENDENCIES уже определены явно по какой-либо причине, нужно добавить их вручную или использовать EXTRA_*_DEPENDENCIES вместо *_DEPENDENCIES.

Эти переменные обычно применяются для сборки переносимых библиотек, которые компонуются со всеми программами проекта. Ниже приведен простой пример, где файл configure.ac включает некоторые проверки, влияющие на LIBOBJS или ALLOCA.

    # configure.ac 
    ... 
    AC_CONFIG_LIBOBJ_DIR([lib]) 
    ... 
    AC_FUNC_MALLOC             dnl May add malloc.$(OBJEXT) to LIBOBJS 
    AC_FUNC_MEMCMP             dnl May add memcmp.$(OBJEXT) to LIBOBJS 
    AC_REPLACE_FUNCS([strdup]) dnl May add strdup.$(OBJEXT) to LIBOBJS 
    AC_FUNC_ALLOCA             dnl May add alloca.$(OBJEXT) to ALLOCA 
    ... 
    AC_CONFIG_FILES([ 
      lib/Makefile 
      src/Makefile 
    ]) 
    AC_OUTPUT

AC_CONFIG_LIBOBJ_DIR сообщает Autoconf, что исходные файлы объектов найдены в каталоге lib/. Automake может использовать эту информацию, а в ином случае ожидает, что эти файлы размещены в каталоге, где применяются переменные $(LIBOBJS) и $(ALLOCA). В каталоге lib/ должны присутствовать файлы malloc.c, memcmp.c, strdup.c, alloca.c. Ниже приведен файл Makefile.am из этого каталога

    # lib/Makefile.am 

    noinst_LIBRARIES = libcompat.a 
    libcompat_a_SOURCES = 
    libcompat_a_LIBADD = $(LIBOBJS) $(ALLOCA)

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

Здесь есть небольшая ловушка — переменные $(LIBOBJS) и $(ALLOCA) могут быть пустыми, а пустые библиотеки не переносимы. Следует убедиться, что в libcompat.a всегда что-то включено. Большинство проектов добавляет некоторые утилиты в этот каталог и указывают их в libcompat_a_SOURCES, поэтому libcompat.a не будет пустой.

Ниже показано, как можно использовать библиотеку из каталога src/.

    # src/Makefile.am 

    # Link all programs in this directory with libcompat.a 
    LDADD = ../lib/libcompat.a 

    bin_PROGRAMS = tool1 tool2 ... 
    tool1_SOURCES = ... 
    tool2_SOURCES = ...

Когда опция subdir-objects не используется, как в приведенном выше примере, переменную $(LIBOBJS) или $(ALLOCA) можно использовать лишь в каталоге, где размещены источники. Например, здесь будет ошибкой использование переменной $(LIBOBJS) или $(ALLOCA) в src/Makefile.am. Однако при использовании subdir-objects и AC_CONFIG_LIBOBJ_DIR не будет ошибки при использовании этих переменных в других каталогах. Например, src/Makefile.am можно изменить, как показано ниже.

    # src/Makefile.am 

    AUTOMAKE_OPTIONS = subdir-objects 
    LDADD = $(LIBOBJS) $(ALLOCA) 

    bin_PROGRAMS = tool1 tool2 ... 
    tool1_SOURCES = ... 
    tool2_SOURCES = ...

Поскольку $(LIBOBJS) и $(ALLOCA) содержат имена объектных файлов, заканчивающиеся на .$(OBJEXT), они не подходят для библиотек Libtool (где ожидается расширение .lo). Взамен следует применять LTLIBOBJS и LTALLOCA. Переменная LTLIBOBJS задается автоматически программой Autoconf и ее не следует задавать вручную, однако пока еще для создания LTALLOCA нужно задавать ALLOCA вручную.

8.7. Переменные, используемые при сборке программ

Иногда полезно знать, какие из переменных Makefile использует программа Automake и в каком порядке (27.6. Порядок переменных флагов). Некоторые переменные наследуются от Autoconf (CC, CFLAGS, CPPFLAGS, DEFS, LDFLAGS, LIBS). Ниже перечислены переменные, которые Automake задает самостоятельно.

AM_CPPFLAGS

Значение этой переменной передается каждой компиляции, вызывающей препроцессор C, Переменная содержит список аргументов препроцессора. В ней следует указывать, например, опции -I и -D. Automake уже автоматически представляет некоторые опции -I в отдельной переменной, которая представляется каждой компиляции с вызовом препроцессора C. В частности, создаются опции -I., -I$(srcdir) и -I с указанием каталога, содержащего файл config.h (если применяется AC_CONFIG_HEADERS). Это можно отключить с помощью опции nostdinc.

Когда включаемый файл создается при сборке и не является частью дистрибутива, его расположение задает переменная $(builddir), а не $(srcdir). Это особенно важно для пакетов, использующих файлы заголовков, размещенные в подкаталогах, и разрешающих сборки за пределами дерева исходных кодов (2.2.6. Параллельная сборка (VPATH)). В таких случаях рекомендуется использовать пару опций -I, таких, например, как -Isome/subdir -I$(srcdir)/some/subdir или -I$(top_builddir)/some/subdir -I$(top_srcdir)/some/subdir. Отметим, что ссылка на дерево сборки должна предшествовать ссылке на дерево кода, поэтому случайно созданные в дереве кода файлы игнорируются.

AM_CPPFLAGS игнорируется при наличии переменной _CPPFLAGS на уровне программы или библиотеки.

INCLUDES

Выполняет ту же работу, что и переменная AM_CPPFLAGS’ (или любая используемая на уровне цели переменная _CPPFLAGS). Это просто старое имя, переменную не следует применять, используя взамен AM_CPPFLAGS или _CPPFLAGS на уровне цели.

AM_CFLAGS

Эту переменную автор Makefile.am может использовать для передачи дополнительных аргументов компилятору C. В некоторых случаях вместо нее применяется переменная _CFLAGS на уровне программы или библиотеки.

COMPILE

Команда, применяемая для компиляции файлов C. Имя файла добавляется в конце для завершения команды.

AM_LDFLAGS

Эту переменную автор Makefile.am может использовать для передачи дополнительных флагов компоновщику. В некоторых случаях вместо нее применяется переменная _LDFLAGS на уровне программы или библиотеки.

LINK

Команда, используемая для компоновки программы C. Она уже включает -o $@ и обычные ссылки на переменные (например, CFLAGS). Переменная содержит также имена объектных файлов и библиотек для компоновки. Переменная не используется, если компоновщик переопределен переменной _LINK на уровне цели или флаги на уровне цели позволяют Automake задать такую переменную _LINK.

8.8. Поддержка Yacc и Lex

В Automake поддержка Yacc и Lex имеет ряд странностей.

Automake предполагает, что файлы .c, созданные yacc (или lex) должны именоваться на основе базового имени входного файла. Т. е. для исходного файла yacc с именем foo.y программа Automake будет создавать промежуточный файл foo.c (в отличие от более традиционного y.tab.c).

Расширение исходного файла yacc служит для определения полученных в результате файлов C, C++ или заголовков. Отметим, что файлы заголовков создаются лишь при использовании опции Yacc -d (см. ниже). Файлы с расширением .y будут преобразованы в файлы .c и заголовки .h, .yy преобразуются в .cc и .hh, .y++ — в c++ и h++, .yxx — в .cxx и .hxx, а .ypp — в .cpp и .hpp.

Точно так же исходные файлы lex могут служить для создания файлов C или C++, при этом распознаются расширения .l, .ll, .l++, .lxx и .lpp.

Не следует явно указывать промежуточные файлы (C или C++) в переменных SOURCES, указываются лишь источники. Промежуточные файлы, созданные yacc или lex, будут включаться в любой создаваемый дистрибутив и пользователю не потребуется yacc и lex.

Если найден файл yacc, в configure.ac должна быть определена переменная YACC. Это проще всего решить вызовом макроса AC_PROG_YACC. При вызове yacc программе передаются переменные AM_YFLAGS и YFLAGS. Последняя переменная является пользовательской, а первая предназначена для создателя файла Makefile.am.

AM_YFLAGS обычно служит для передачи опции -d программе yacc. Automake понимает это и автоматически настраивает правила обновления и распространения заголовочных файлов, созданных yacc -d6. Однако Automake не может знать, где будет применяться заголовок и нужно обеспечить его сборку до попытки использования. Обычно это требуется, чтобы работало отслеживание зависимостей, когда заголовок включен в другой файл. Решением является включение заголовочного файла в BUILT_SOURCES (9.4. Исходные файлы для сборки), как показано ниже.

    BUILT_SOURCES = parser.h 
    AM_YFLAGS = -d 
    bin_PROGRAMS = foo 
    foo_SOURCES = ... parser.y ...

Если найден файл lex, в configure.ac должна быть определена переменная LEX, для чего можно использовать макрос AC_PROG_LEX, но рекомендуется применять AM_PROG_LEX (6.4. Макросы Autoconf из пакета Automake). При вызове lex программе передаются переменные AM_LFLAGS и LFLAGS. Первая предназначена для создателей Makefile.am, вторая — для пользователей.

При использовании режима AM_MAINTAINER_MODE (27.2. Сценарий missing и режим AM_MAINTAINER_MODE) правило повторной сборки для распространяемых источников Yacc и Lex применяется лишь при включении maintainer-mode или после удаления этих файлов.

При использовании источников lex или yacc команда automake -a автоматически устанавливает в пакет программу ylwrap (3.7. Программы, которые могут быть нужны automake). Эта программа применяется правилами сборки для переименования выходных файлов этих инструментов и позволяет включать множество источников yacc или lex в один каталог (выходное имя yacc зафиксировано, а при параллельной работе make может вызваться несколько экземпляров yacc одновременно). Для yacc простой блокировки недостаточно, поскольку в выводе yacc всегда применяются одни и те же символы, поэтому невозможно связать два анализатора yacc в одном исполняемом файле. Рекомендуется применять приведенные ниже правила переименования, заимствованные из gdb.

    #define yymaxdepth c_maxdepth 
    #define yyparse c_parse 
    #define yylex   c_lex 
    #define yyerror c_error 
    #define yylval  c_lval 
    #define yychar  c_char 
    #define yydebug c_debug 
    #define yypact  c_pact 
    #define yyr1    c_r1 
    #define yyr2    c_r2 
    #define yydef   c_def 
    #define yychk   c_chk 
    #define yypgo   c_pgo 
    #define yyact   c_act 
    #define yyexca  c_exca
    #define yyerrflag c_errflag 
    #define yynerrs c_nerrs 
    #define yyps    c_ps 
    #define yypv    c_pv 
    #define yys     c_s 
    #define yy_yys  c_yys 
    #define yystate c_state 
    #define yytmp   c_tmp 
    #define yyv     c_v 
    #define yy_yyv  c_yyv 
    #define yyval   c_val 
    #define yylloc  c_lloc 
    #define yyreds  c_reds 
    #define yytoks  c_toks 
    #define yylhs   c_yylhs 
    #define yylen   c_yylen 
    #define yydefred c_yydefred 
    #define yydgoto  c_yydgoto 
    #define yysindex c_yysindex 
    #define yyrindex c_yyrindex 
    #define yygindex c_yygindex 
    #define yytable  c_yytable 
    #define yycheck  c_yycheck 
    #define yyname   c_yyname 
    #define yyrule   c_yyrule

В каждом определении следует заменить префикс c_ удобным для вас префиксом. Эти определения работают с bison, byacc и традиционной программой yacc.

8.9. Поддержка C++

Automake полностью поддерживает C++. Любой пакет с кодом C++ должен задавать выходную переменную CXX в файле configure.ac. Простейшим вариантом является использование макроса AC_PROG_CXX. При наличии исходных файлов C++ определяется ряд дополнительных переменных.

CXX

Имя компилятора C++.

CXXFLAGS

Все флаги, передаваемые компилятору C++.

AM_CXXFLAGS

Вариант CXXFLAGS для сопровождающих пакет.

CXXCOMPILE

Команда, используемая для реальной компиляции исходного файла C++. Имя файла добавляется в конце.

CXXLINK

Команда, используемая для реальной компоновки исходного файла C++.

8.10. Поддержка Objective C

Automake включает некоторую поддержку Objective C. Любой пакет с кодом Objective C должен задавать выходную переменную OBJC в файле configure.ac. Простейшим вариантом является использование макроса AC_PROG_OBJC. При наличии исходных файлов Objective C определяется ряд дополнительных переменных.

OBJC

Имя компилятора Objective C.

OBJCFLAGS

Все флаги, передаваемые компилятору Objective C.

AM_OBJCFLAGS

Вариант OBJCFLAGS для сопровождающих пакет.

OBJCCOMPILE

Команда, используемая для реальной компиляции исходного файла Objective C. Имя файла добавляется в конце.

OBJCLINK

Команда, используемая для реальной компоновки исходного файла Objective C.

8.11. Поддержка Objective C++

Automake включает некоторую поддержку Objective C++. Любой пакет с кодом Objective C++ должен задавать выходную переменную OBJCXX в файле configure.ac. Простейшим вариантом является использование макроса AC_PROG_OBJCXX. При наличии исходных файлов Objective C++ определяется ряд дополнительных переменных.

OBJCXX

Имя компилятора Objective C++.

OBJCXXFLAGS

Все флаги, передаваемые компилятору Objective C++.

AM_OBJCXXFLAGS

Вариант OBJCXXFLAGS для сопровождающих пакет.

OBJCXXCOMPILE

Команда, используемая для реальной компиляции исходного файла Objective C++. Имя файла добавляется в конце.

OBJCXXLINK

Команда, используемая для реальной компоновки исходного файла Objective C++.

8.12. Поддержка Unified Parallel C

Automake включает некоторую поддержку Unified Parallel C. Любой пакет с кодом Unified Parallel C должен задавать выходную переменную UPC в файле configure.ac. Простейшим вариантом является использование макроса AM_PROG_UPC. При наличии исходных файлов Unified Parallel C определяется ряд дополнительных переменных.

UPC

Имя компилятора Unified Parallel C.

UPCFLAGS

Все флаги, передаваемые компилятору Unified Parallel C.

AM_UPCFLAGS

Вариант UPCFLAGS для сопровождающих пакет.

UPCCOMPILE

Команда, используемая для реальной компиляции исходного файла Unified Parallel C. Имя файла добавляется в конце.

UPCLINK

Команда, используемая для реальной компоновки исходного файла Unified Parallel C.

8.13. Поддержка ассемблера

Automake включает некоторую поддержку ассемблерного кода для двух форм файлов — обычные (*.s) и после препроцессора CPP (*.S или *.sx).

В переменной CCAS указывается имя компилятора для ассемблерного кода. Этот компилятор должен работать подобно компилятору C и, в частности, должен воспринимать опции -c и -o. Значения переменных CCASFLAGS и AM_CCASFLAGS (или определение флагов на уровне цели) передаются компилятору. Для файлов после препроцессора используются еще переменные DEFS, DEFAULT_INCLUDES, INCLUDES, CPPFLAGS, AM_CPPFLAGS.

Макрос autoconf AM_PROG_AS определяет переменные CCAS и CCASFLAGS (если они не заданы, в CCAS указывается компилятор C, а в CCASFLAGS — флаги компилятора C), но можно задать их самостоятельно.

Программа automake считает ассемблерным кодом лишь файлы .s, .S и .sx.

8.14. Поддержка Fortran 77

Automake полностью поддерживает Fortran 77. При наличии в пакете кода Fortran 77 должна включаться переменная F77 в файл configure.ac. Проще всего это сделать с помощью макроса AC_PROG_F77. При наличии исходных файлов Fortran 77 определяется ряд дополнительных переменных.

F77

Имя компилятора Fortran 77.

FFLAGS

Все флаги, передаваемые компилятору Fortran 77.

AM_FFLAGS

Вариант FFLAGS для сопровождающих пакет.

RFLAGS

Все флаги, передаваемые компилятору Ratfor.

AM_RFLAGS

Вариант RFLAGS для сопровождающих пакет.

F77COMPILE

Команда, используемая для реальной компиляции исходного файла Fortran 77. Имя файла добавляется в конце.

FLINK

Команда, используемая для реальной компоновки программы или библиотеки Fortran 77.

Automake может обрабатывать файлы после препроцессора Fortran 77 и Ratfor для их компиляции7. Имеется также некоторая поддержка создания программ и общих библиотек, где используется код Fortran 77 вместе с другими языками (8.14.3. Совмещение Fortran 77 с C и C++).

8.14.1. Предварительная обработка Fortran 77

N.f выполняется автоматически из N.F или N.r. Это правило лишь запускает препроцессор для преобразования исходного файла Fortran 77 или Ratfor в строгий код Fortran 77. Использование команды показано ниже.

.F

$(F77) -F $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_FFLAGS) $(FFLAGS)

.r

$(F77) -F $(AM_FFLAGS) $(FFLAGS) $(AM_RFLAGS) $(RFLAGS)

8.14.2. Компиляция файлов Fortran 77

N.o выполняется автоматически из N.f, N.F или N.r путем запуска компилятора Fortran 77. Использование команды показано ниже.

.f

$(F77) -c $(AM_FFLAGS) $(FFLAGS)

.F

$(F77) -c $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_FFLAGS) $(FFLAGS)

.r

$(F77) -c $(AM_FFLAGS) $(FFLAGS) $(AM_RFLAGS) $(RFLAGS)

8.14.3. Совмещение Fortran 77 с C и C++

Automake обеспечивает ограниченную поддержку создания программ и общих библиотек, в которых Fortran 77 применяется вместе с C и/или C++. Однако существует много вопросов, связанных со смешанным использованием Fortran 77 и других языков, которые не решаются в Automake, но обрабатываются другими пакетами8.

Automake может помочь в решении двух задач.

  1. Автоматический выбор компоновщика в зависимости от комбинации исходных кодов.

  2. Автоматический выбор подходящих флагов компоновщика (например, -L и -l) для передачи выбранному автоматически компоновщику для привязки нужных библиотек Fortran 77.

    Эти дополнительные флаги компоновщика Fortran 77 передаются в выходной переменной FLIBS макросом Autoconf AC_F77_LIBRARY_LDFLAGS.

Если Automake видит, что программа или общая библиотека (из _PROGRAMS или _LTLIBRARIES) содержит исходный код Fortran 77 в комбинации с C и/или C++, требуется вызвать макрос AC_F77_LIBRARY_LDFLAGS из файла configure.ac, после чего появится $(FLIBS) в подходящей переменной _LDADD (для программ) или _LIBADD (для общих библиотек). Создатель файла Makefile.am должен убедиться, что $(FLIBS) присутствует в _LDADD или _LIBADD. Рассмотрим, например, приведенный ниже файл Makefile.am.

    bin_PROGRAMS = foo 
    foo_SOURCES  = main.cc foo.f 
    foo_LDADD    = libfoo.la $(FLIBS) 

    pkglib_LTLIBRARIES = libfoo.la 
    libfoo_la_SOURCES  = bar.f baz.c zardoz.cc 
    libfoo_la_LIBADD   = $(FLIBS)

Здесь Automake будет настаивать на включении AC_F77_LIBRARY_LDFLAGS в configure.ac, а также выдавать предупреждение, если $(FLIBS) не указана в foo_LDADD и libfoo_la_LIBADD.

8.14.3.1. Выбор компоновщика

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

  1. Native Java (‘GCJLINK’)

  2. Objective C++ (‘OBJCXXLINK’)

  3. C++ (‘CXXLINK’)

  4. Fortran 77 (‘F77LINK’)

  5. Fortran (‘FCLINK’)

  6. Objective C (‘OBJCLINK’)

  7. Unified Parallel C (‘UPCLINK’)

  8. C (‘LINK’)

Например, при компиляции кода Fortran 77, C и C++ в программу будет использован компоновщик C++. В таких случаях, если компоновщику C или Fortran 77 нужны какие-либо специальные библиотеки, не включенные в компоновщик C++, они должны быть добавлены в _LDADD и/или _LIBADD при создании файла Makefile.am.

Automake при выборе компоновщика просматривает лишь имена файлов, указанные в переменных _SOURCES, а по умолчанию применяется компоновщик C. Иногда это неудобно, поскольку компоновка выполняется с библиотекой, написанной на другом языке, и нужно точнее выбрать компоновщик. В параграфе 8.3.5. Вспомогательные библиотеки Libtool приведены рекомендации по использованию nodist_EXTRA_…_SOURCES.

Переменная _LINK на уровне цели переопределяет описанный выше выбор, а заданные для цели флаги заставляют Automake создать переменную _LINK для цели в соответствии с выбранным языком, как показано выше.

8.15. Поддержка Fortran 9x

Automake включает поддержку Fortran 9x. Любой пакет с кодом Fortran 9x должен определять выходную переменную FC в файле configure.ac. Сделать это проще всего с помощью макроса AC_PROG_FC. При наличии источников Fortran 9x определяется ряд переменных, перечисленных ниже.

FC

Имя компилятора Fortran 9x.

FCFLAGS

Все флаги, передаваемые компилятору Fortran 9x.

AM_FCFLAGS

Вариант FCFLAGS для сопровождающих пакет.

FCCOMPILE

Команда, используемая для реальной компиляции файла Fortran 9x. Имя файла добавляется в конце.

FCLINK

Команда для реальной компоновки программы или общей библиотеки на Fortran 9x.

8.16. Компиляция файлов Java с помощью gcj

Automake поддерживает естественно скомпилированный код Java (с использованием gcj — внешнего интерфейса Java для GCC. Имеется также слабая поддержка компиляции Java в байт-код с использованием компилятора javac, но она будет прекращена (10.4. Компиляция байт-кода Java (устарела)).

Любой пакет, включающий код Java для компиляции, должен задавать переменную GCJ в файле configure.ac, а иногда нужно определить еще переменную GCJFLAGS (в configure.ac или Makefile.am). Проще всего это сделать с помощью макроса AM_PROG_GCJ. По умолчанию программы с исходным кодом Java компонуются с помощью gcj.

Как обычно, содержимое AM_GCJFLAGS передается всякой компиляции, вызывающей gcj (в роли опережающего коммутатора при вызове для создания файлов .class используется AM_JAVACFLAGS). Если нужно передать gcj опции из Makefile.am, следует применять эту переменную, а не пользовательскую переменную GCJFLAGS.

Компилятор gcj можно использовать для файлов .java, .class, .zip или .jar.

При компоновке gcj требует, чтобы основной класс был задан с помощью опции —main=. Проще всего это сделать, используя для программы переменную _LDFLAGS.

8.17. Поддержка Vala

Automake обеспечивает начальную поддержку для Vala. Требуется valac версии 0.7.0 или выше, а пользователь должен применять GNU make.

    foo_SOURCES = foo.vala bar.vala zardoc.c

Любой файл .vala, указанный в переменной _SOURCES будет компилироваться в код C компилятором Vala. Созданные файлы .c включаются в дистрибутив, поэтому конечному пользователю не требуется иметь компилятор Vala.

Automake распространяется с макросом Autoconf AM_PROG_VALAC, который находит компилятор Vala и может проверять номер версии.

AM_PROG_VALAC ([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])

Поиск компилятора Vala в путях PATH. При обнаружении компилятора переменная VALAC будет указывать на него (см. ниже). Макрос имеет 3 необязательных аргумента. Первый (при наличии) указывает минимальную версию компилятора Vala, требуемую для этого пакета. Если компилятор найден и удовлетворяет MINIMUM-VERSION, выполняется ACTION-IF-FOUND (по умолчанию ничего не делает), иначе выполняется ACTION-IF-NOT-FOUND (по умолчанию выводит предупреждение, если компилятор не найден или слишком старый.

При компиляции файлов Vala используется несколько дополнительных переменных, приведенных ниже.

VALAC

Абсолютный путь к компилятору Vala или просто valac, если при работе configure не найден подходящий компилятор Vala.

VALAFLAGS

Дополнительные аргументы для компилятора Vala.

AM_VALAFLAGS

Вариант ‘VALAFLAGS для сопровождающих пакет.

         lib_LTLIBRARIES = libfoo.la 
         libfoo_la_SOURCES = foo.vala

Отметим, что в настоящее время нет возможности задать *_VALAFLAGS на уровне цели (27.7. Переименование объектных файлов) для создания разных файлов C из одного файла Vala.

8.18. Поддержка других языков

Automake в настоящее время полностью поддерживает лишь языки C, C++ (8.9. Поддержка C++), Objective C (8.10. Поддержка Objective C), Objective C++ (8.11. Поддержка Objective C++), Fortran 77 (8.14. Поддержка Fortran 77), Fortran 9x (8.15. Поддержка Fortran 9x) и Java (8.16. Компиляция файлов Java с помощью gcj). для других языков имеется лишь начальная поддержка. Ограниченная возможность добавить свою поддержку языков обеспечивается за счет правил обработки суффиксов (18.2. Обработка новых расширений имен файлов).

8.19. Автоматическое отслеживание зависимостей

Разработчикам зачастую сложно обновлять Makefile.am при каждом изменении зависимостей включаемых файлов и Automake поддерживает автоматическое отслеживание зависимостей (2.2.12. Автоматическая проверка зависимостей).

Automake всегда использует для компиляции полные зависимости, включая системные заголовки. В модели Automake определение зависимостей должно быть побочным эффектом сборки. Поэтому зависимости определяются запуском всех компиляций через специальную оболочку depcomp, которая умеет заставить разные компиляторы C и C++ генерировать сведения о зависимостях в нужно формате. Команда automake -a устанавливает depcomp в каталог исходного кода. Если depcomp не понимает, как корректно вызвать компилятор, отслеживание зависимостей для сборки будет просто отключено.

Опыт работы с ранними версиями Automake показал, что генерировать зависимости только в системе сопровождающего ненадежно, поскольку конфигурации могут сильно различаться. Поэтому сейчас Automake определяет зависимости в процессе сборки. Автоматическое определение зависимостей можно отключить, поместив значение no-dependencies в переменную AUTOMAKE_OPTIONS или передав его в качестве аргумента AM_INIT_AUTOMAKE (предпочтительно). Можно также запустить automake с опцией -i. По умолчанию отслеживание зависимостей включено.

Можно также отключить отслеживание зависимостей опцией —disable-dependency-tracking в команде ./configure.

8.20. Поддержка расширений исполняемых файлов

На некоторых платформах (таких как Windows) для исполняемых файлов предполагается определенное расширение (.exe). Поэтому на таких платформах некоторые компиляторы (включая GCC) будут автоматически создавать foo.exe при запросе генерации foo. Automake обеспечивает поддержку такого подхода, но, к сожалению, не полную.

Следует понимать, что Automake переопределяет переменные вида

    bin_PROGRAMS = liver

в

    bin_PROGRAMS = liver$(EXEEXT)

Цели, создаваемые Automake получают расширение $(EXEEXT).

Переменные TESTS и XFAIL_TESTS (15.2. Простые тесты) также переопределяются, если они содержат имена файлов, объявленные как программы в том же Makefile (это полезно при указании некоторых программ из check_PROGRAMS в переменной TESTS).

Однако Automake не может применить эти переопределения к подстановкам configure. Это означает, что при условной сборке программ с такими подстановками файл configure.ac должен позаботиться о добавлении $(EXEEXT) при создании выходной переменной.

Иногда сопровождающие пакет создают явные правила компоновки для своих программ. Без поддержки расширений исполняемых файлов это просто — достаточно создать правило, целью которого является имя программы. Однако при поддержки расширений имен исполняемых файлов вместо этого требуется добавлять суффикс $(EXEEXT).

Это может быть неудобно для сопровождающих, которые знают, что их пакет никогда не будет работать на платформе с расширением имени исполняемых файлов. Для них имеется опция no-exeext (17. Смена поведения Automake), которая отключает это свойство. Работает это достаточно коряво — при наличии опции no-exeext правило для цели foo в Makefile.am переопределит созданное automake правило для foo$(EXEEXT).

9. Другие производные объекты

Automake может работать с производными объектами, не являющимися программами C. Иногда поддержка сборки таких объектов должна задаваться явно, но Automake автоматически обрабатывает их установку и распространение.

9.1. Исполняемые сценарии

Можно задать и установить программы, которые являются сценариями. Такие программы указываются с использованием первичной переменной SCRIPTS. Распространение финальной, устанавливаемой формы сценария обычно выглядит в Makefile как

    # Install my_script in $(bindir) and distribute it. 
    dist_bin_SCRIPTS = my_script

По умолчанию сценарии не распространяются и, как показано выше, для включения в дистрибутив можно использовать префикс dist_. Сценарии могут устанавливаться в bindir, sbindir, libexecdir, pkglibexecdir или pkgdatadir. Сценарии, которые не нужно устанавливать, могут быть указаны в переменной noinst_SCRIPTS, а нужные лишь для make check следует указывать в check_SCRIPTS.

Для сборки сценария в Makefile.am нужно включить подходящие правила. Например, сама программа automake является сценарием Perl, который создается из automake.in. Обработка показана ниже.

    bin_SCRIPTS = automake 
    CLEANFILES = $(bin_SCRIPTS) 
    EXTRA_DIST = automake.in 

    do_subst = sed -e 's,[@]datadir[@],$(datadir),g' \ 
                -e 's,[@]PERL[@],$(PERL),g' \ 
                -e 's,[@]PACKAGE[@],$(PACKAGE),g' \ 
                -e 's,[@]VERSION[@],$(VERSION),g' \ 
                ... 

    automake: automake.in Makefile 
            $(do_subst) < $(srcdir)/automake.in > automake 
            chmod +x automake

Сценарии, для которых было представлено правило сборки, нужно удалить явно с помощью CLEANFILES (13. Очистка), а их исходные файлы следует распространять обычно с помощью EXTRA_DIST (14.1. Основы дистрибутивов).

Другим способом сборки сценариев является из обработка из configure с макросом AC_CONFIG_FILES. В этом случае Automake знает, какие файлы следует очищать и распространять и как должны выглядеть правила пересборки. Например, если configure.ac включает строку

    AC_CONFIG_FILES([src/my_script], [chmod +x src/my_script])

для сборки src/my_script из src/my_script.in, то src/Makefile.am для установки этого сценария в $(bindir) может иметь вид

   bin_SCRIPTS = my_script 
    CLEANFILES = $(bin_SCRIPTS)

Здесь не нужна переменная EXTRA_DIST или правило для сборки, Automake выводит их из AC_CONFIG_FILES (6.1. Конфигурационные требования). Переменная CLEANFILES остается полезной, поскольку Automake по умолчанию очищает цели AC_CONFIG_FILES в distclean, а не в clean.

Хотя это выглядит проще, сборка сценария таким способом имеет один недостаток — переменные каталогов (такие как $(datadir)) не полностью преобразуются и могут указывать на другие переменные каталогов.

9.2. Заголовочные файлы

Заголовочные файлы, которые нужно установить, задаются переменными HEADERS. Заголовки могут устанавливаться в includedir, oldincludedir, pkgincludedir или любой другой заданный каталог (3.3. Схема именования). Например,

    include_HEADERS = foo.h bar/bar.h

будет устанавливать два файла $(includedir)/foo.h и $(includedir)/bar.h. Поддерживаются также префиксы nobase_ (7.3. Другая модель организации каталогов).

    nobase_include_HEADERS = foo.h bar/bar.h

Обычно требуется устанавливать лишь файлы, сопровождающие устанавливаемые библиотеки. Заголовки, используемые программами или дополнительными библиотеками, не устанавливаются. Для таких заголовков можно использовать переменную noinst_HEADERS. Однако, если заголовок относится к одной дополнительной библиотеке или программе, рекомендуется указывать его в переменной _SOURCES этой библиотеки или программы (8.1.1. Указание источников программы) вместо noinst_HEADERS. Это будет более понятно при рассмотрении файла Makefile.am. Переменная noinst_HEADERS подходит для использования в каталоге, содержащем лишь заголовки без связанной с ними программы или библиотеки.

Все файлы заголовков должны быть указаны в переменной _SOURCES или _HEADERS, а пропущенные файлы не будут включены в дистрибутив.

Для заголовочных файлов, которые собраны и не должны распространяться, используется префикс nodist_ в форме nodist_include_HEADERS или nodist_prog_SOURCES. Если создаваемые заголовки нужны при сборке, они должны быть подготовлены заранее (9.4. Исходные файлы для сборки).

9.3. Независимые от архитектуры файлы

Automake поддерживает установку файлов данных с помощью переменных DATA. Данные могут помещаться в datadir, sysconfdir, sharedstatedir, localstatedir или pkgdatadir. По умолчанию файлы данных не включаются в дистрибутив. Можно использовать префикс dist_ для настройки этого на уровне переменных. Ниже показано, как Automake объявляет дополнительные файлы данных.

    dist_pkgdata_DATA = clean-kr.am clean.am ...

9.4. Исходные файлы для сборки

Поскольку автоматическое отслеживание зависимостей в Automake является побочным результатом компиляции (8.19. Автоматическое отслеживание зависимостей), возникает вопрос начальной настройки (bootstrap) — цель не следует собирать до выполнения зависимостей, но эти зависимости неизвестны, пока цель не скомпилирована.

Обычно проблемы не возникает, поскольку зависимости являются распространяемыми источниками и не нуждаются в сборке. Предположим, что файл foo.c включает foo.h. При первой компиляции foo.o, программа make знает лишь, о зависимости foo.o от foo.c. В качестве побочного результатом этой компиляции depcomp записывает зависимость от foo.h, чтобы учесть ее при последующем вызове make. Очевидно, что в этом случае проблемы не возникает — или файла не существует и его нужно собрать (безотносительно к зависимостям), или имеются точные зависимости и ими можно воспользоваться для решения вопроса о повторной сборке foo.o.

Другая ситуация возникает при отсутствии foo.h на момент первого запуска make. Например, может быть правило для сборки foo.h. На этот раз собрать file.o не получится, поскольку компилятор не найдет foo.h. Программе make не удастся выполнить правило сборки foo.h в первую очередь из-за отсутствия данных о зависимостях. Обойти эту проблему позволяет переменная BUILT_SOURCES. Исходный файл из BUILT_SOURCES создается по команде make all или make check (или даже make install) до обработки других целей. Однако такой исходный файл не будет скомпилирован, пока это не будет запрошено явно в той или иной переменной _SOURCES.

Для завершения примера можно было бы использовать BUILT_SOURCES = foo.h, чтобы обеспечить сборку foo.h до всех других целей (включая foo.o) по команде make all или make check. Имя переменной BUILT_SOURCES не совсем точно, поскольку любой файл, который должен быть создан с начале процесса сборки, может быть указан в этой переменной. Более того, все источники сборки не обязательно перечисляются в BUILT_SOURCES. Например, созданный файл .c не присутствует в BUILT_SOURCES (пока не включен другим источником), поскольку он не известен как зависимость связанного объекта.

Подчеркнем, что BUILT_SOURCES учитывается лишь командами make all, make check и make install. Это означает, что нельзя собрать конкретную цель (например, make foo) в чистом дереве, если она зависит от сборки источников. Однако предшествующий запуск make all решает эту задачу, поскольку зависимости становятся доступными.

9.4.1. Пример сборки источников

Предположим, что файл foo.c включает заголовок bindir.h, который зависит от установки и не распространяется (нужно собирать). Этот файл bindir.h определяет макрос препроцессора bindir для назначения переменной make bindir (наследуется от configure). Ниже рассмотрено несколько вариантов сборки. Этот список не учитывает всех вариантов работы со сборкой источников, но представляет некоторые идеи по решению задачи.

Первая сборка

Здесь рассматривается вопрос начальной настройки (bootstrap), упомянутая выше (9.4. Исходные файлы для сборки).

Ниже приведен предварительный файл Makefile.am.

    # This won't work. 
    bin_PROGRAMS = foo 
    foo_SOURCES = foo.c 
    nodist_foo_SOURCES = bindir.h 
    CLEANFILES = bindir.h 
    bindir.h: Makefile 
            echo '#define bindir "$(bindir)"' >$@

Это не будет работать, поскольку Automake не знает, что foo.c включает bindir.h. Напомним, что автоматическое отслеживание зависимостей является побочным результатом компиляции, поэтому зависимости foo.o будут известны лишь после компиляции foo.o (8.19. Автоматическое отслеживание зависимостей). Симптомы показаны ниже.

    % make 
    source='foo.c' object='foo.o' libtool=no \ 
    depfile='.deps/foo.Po' tmpdepfile='.deps/foo.TPo' \ 
    depmode=gcc /bin/sh ./depcomp \ 
    gcc -I. -I. -g -O2 -c `test -f 'foo.c' || echo './'`foo.c 
    foo.c:2: bindir.h: No such file or directory 
    make: *** [foo.o] Error 1

В этом примере bindir.h не распространяется, не устанавливается и даже вовремя не создается. Можно задаться вопросом осмысленности строки nodist_foo_SOURCES = bindir.h. Она просто указывает, что bindir.h является источником для foo, поэтому, например, его следует проверять при генерации тегов (18.1. Взаимодействие с etags).

Использование BUILT_SOURCES

Решение состоит в создании файла bindir.h заранее с использованием предназначенной для этого переменной BUILT_SOURCES (9.4. Исходные файлы для сборки).

    bin_PROGRAMS = foo 
    foo_SOURCES = foo.c 
    nodist_foo_SOURCES = bindir.h 
    BUILT_SOURCES = bindir.h 
    CLEANFILES = bindir.h 
    bindir.h: Makefile 
            echo '#define bindir "$(bindir)"' >$@

Посмотрим сначала, как создается bindir.h

    % make 
    echo '#define bindir "/usr/local/bin"' >bindir.h 
    make  all-am 
    make[1]: Entering directory `/home/adl/tmp' 
    source='foo.c' object='foo.o' libtool=no \ 
    depfile='.deps/foo.Po' tmpdepfile='.deps/foo.TPo' \ 
    depmode=gcc /bin/sh ./depcomp \ 
    gcc -I. -I. -g -O2 -c `test -f 'foo.c' || echo './'`foo.c 
    gcc  -g -O2   -o foo  foo.o 
    make[1]: Leaving directory `/home/adl/tmp'

Однако, как было отмечено выше, BUILT_SOURCES применяется только к целям all, check и installи по-прежнему не будет работать для цели make foo

    % make clean 
    test -z "bindir.h" || rm -f bindir.h 
    test -z "foo" || rm -f foo 
    rm -f *.o 
    % : > .deps/foo.Po # Suppress previously recorded dependencies 
    % make foo 
    source='foo.c' object='foo.o' libtool=no \ 
    depfile='.deps/foo.Po' tmpdepfile='.deps/foo.TPo' \ 
    depmode=gcc /bin/sh ./depcomp \ 
    gcc -I. -I. -g -O2 -c `test -f 'foo.c' || echo './'`foo.c 
    foo.c:2: bindir.h: No such file or directory 
    make: *** [foo.o] Error 1

Запись зависимостей вручную

Обычно BUILT_SOURCES устраивает, поскольку команды вида make foo просто не используются до make all, как в предыдущем примере. Однако при необходимости можно отказаться от использования BUILT_SOURCES и записать зависимости явно в Makefile.am.

    bin_PROGRAMS = foo 
    foo_SOURCES = foo.c 
    nodist_foo_SOURCES = bindir.h 
    foo.$(OBJEXT): bindir.h 
    CLEANFILES = bindir.h 
    bindir.h: Makefile 
            echo '#define bindir "$(bindir)"' >$@

Нет нужды явно указывать все зависимости foo.o, достаточно тех, которые требуется выполнить. Если зависимость уже  выполнена, это не помешает первой компиляции и будет записано обычным кодом отслеживания зависимостей. Отметим, что после первой компиляции код отслеживания запишет также зависимость между foo.o и bindir.h, поэтому заданная явно зависимость полезна лишь при первой сборке.

Добавление таких явных зависимостей может создавать опасность, связанную с тем, что Automake не пытается переопределять правила. Правило foo.$(OBJEXT): bindir.h заменяет любое правило, которое Automake может пожелать вывести для сборки foo.$(OBJEXT). В данном случае это работает, поскольку Automake не имеет вывода для какой-либо цели foo.$(OBJEXT):, полагаясь на правило суффиксов (т. е., .c.$(OBJEXT):). При явном задании зависимостей всегда следует проверять файл Makefile.in.

Сборка bindir.h из configure

Можно задать макрос препроцессора из сценария configure с помощью файла config.h или путем обработки файла bindir.h.in с помощью макроса AC_CONFIG_FILES. На этом этапе должно быть ясно, что сборка bindir.h из configure работает для данного примера. Файл bindir.h будет создан до сборки какой-либо цели, поэтому проблемы с зависимостями не возникнет. Сжатая форма Makefile представлена ниже и в ней даже не упоминается bindir.h.

    bin_PROGRAMS = foo 
    foo_SOURCES = foo.c

Однако не всегда возможно собрать источник из configure, особенно при генерации такого источника инструментом, который также нужно собрать.

Сборка bindir.c без bindir.h

Другая идея заключается в определении bindir как переменной или функции, экспортируемой из bindir.o, и сборка bindir.c вместо bindir.h.

    noinst_PROGRAMS = foo 
    foo_SOURCES = foo.c bindir.h 
    nodist_foo_SOURCES = bindir.c 
    CLEANFILES = bindir.c 
    bindir.c: Makefile 
            echo 'const char bindir[] = "$(bindir)";' >$@

Файл bindir.h содержит просто определения переменных и его сборка не требуется, поэтому ошибок возникать не должно. Файл bindir.o всегда зависит от bindir.c, поэтому bindir.c будет собран первым.

Что лучше?

Здесь нет панацеи и каждый из вариантов имеет свои плюсы и минусы.

Переменную BUILT_SOURCES нельзя использовать для сборки make foo в «чистом» дереве.

Явное добавление зависимостей может приводить к ошибочным переопределениям правил Automake.

Сборка файлов из ./configure возможна не всегда, равно как и преобразование файлов .h в .c.

10. Другие инструменты GNU

Поскольку основной задачей Automake является создание файлов Makefile.in для использования программами GNU, этот инструмент использует другие средства GNU.

10.1. Emacs Lisp

Automake обеспечивает некоторую поддержку Emacs Lisp. Основным применением переменной LISP является хранение списка файлов .el. Возможными префиксами для основного имени являются lisp_ и noinst_. При задании lisp_LISP требуется вызвать макрос AM_PATH_LISPDIR (6.4. Макросы Autoconf из пакета Automake) из configure.ac.

Источники Lisp по умолчанию не распространяются и для контроля распространения можно использовать переменную LISP с префиксом dist_ (dist_lisp_LISP или dist_noinst_LISP).

Automake будет выполнять байтовую компиляцию для всех исходных файлов Emacs Lisp с использованием Emacs, найденного макросом AM_PATH_LISPDIR 9если программа найдена). При компиляции флаги, заданные разработчиком в AM_ELCFLAGS и пользователем в ELCFLAGS, передаются Emacs. Результаты байтовой компиляции файлов Emacs Lisp не переносимы между разными версиями Emacs, поэтому не следует ее использовать при наличии в системе нескольких версий Emacs. Кроме того, для многих пакетов байтовая компиляция не дает преимуществ. Тем не менее, ее использование рекомендуется для источников Emacs Lisp.

Есть для способа избежать байтовой компиляции и рекомендуется приведенная ниже конструкция.

    lisp_LISP = file1.el file2.el 
    ELCFILES =

ELCFILES является внутренней переменной Automake, в которой обычно указаны все файлы .elc, для которых нужна байтовая компиляция. Automake задает ELCFILES автоматически из lisp_LISP. Сброс этой переменной автоматически отключает байтовую компиляцию.

Начиная с Automake 1.8 рекомендуется использовать lisp_DATA, как показано ниже.

    lisp_DATA = file1.el file2.el

Отметим, что эти конструкции не эквивалентны. _LISP не будет устанавливать файлы при отсутствии Emacs, а _DATA устанавливает эти файлы всегда.

10.2. Gettext

При наличии макроса AM_GNU_GETTEXT в файле configure.ac Automake включает поддержку GNU gettext для работы с разными естественными языками. Для поддержки gettext в Automake требуется добавление в пакет одного или двух подкаталогов — po и возможно intl. Последний нужен, если AM_GNU_GETTEXT не вызывается с аргументом external или применяется AM_GNU_GETTEXT_INTL_SUBDIR. Automake обеспечивает наличие этих каталогов и их включение в SUBDIRS.

10.3. Libtool

Automake поддерживает GNU Libtool с помощью переменной LTLIBRARIES (8.3. Сборка общих библиотек).

10.4. Компиляция байт-кода Java (устарела)

Automake обеспечивает минимальную поддержку компиляции байт-кода Java с использованием переменной JAVA (в дополнение к поддержке компиляции Java в машинный код, 8.16. Компиляция файлов Java с помощью gcj). Большинство описанных в этом параграфе возможностей и сам интерфейс устарели. В новых выпусках Automake будут предприняты попытки обеспечить более эффективный интерфейс, но вероятность его совместимости с прежними версиями весьма мала и описанные здесь возможности могут быть удалены через некоторое время после внедрения нового интерфейса (если он будет). В любом случае возможности переменной JAVA больше не развиваются даже для исправления ошибок.

Любой файл .java, указанный в переменной _JAVA, будет компилироваться с помощью JAVAC в процессе сборки. По умолчанию файлы .java не включаются в дистрибутив и для распространения их следует использовать префикс dist_.

Ниже приведен пример распространения файлов .java и установки файлов .class, полученных при компиляции.

    javadir = $(datadir)/java 
    dist_java_JAVA = a.java b.java ...

В настоящее время Automake применяет ограничение, в соответствии с которым можно использовать лишь одну переменную _JAVA в данном файле Makefile.am. Это обусловлено тем, что обычно невозможно узнать, какой файл .class из какого .java, поэтому нет возможности узнать, какие файлы куда устанавливать. Например, файл .java может определять множество классов и имена файлов .class невозможно определить без анализа файла .java.

При компиляции источников Java используется несколько дополнительных переменных.

JAVAC

Имя компилятора Java (по умолчанию javac).

JAVACFLAGS

Флаги для передачи компилятору (3.6. Пользовательские переменные).

AM_JAVACFLAGS

Дополнительные флаги, передаваемые компилятору Java. Эту переменную (а не JAVACFLAGS) следует использовать при необходимости включения флагов компилятора Java в Makefile.am.

JAVAROOT

Значение этой переменной передается в опции -d компилятору javac (по умолчанию $(top_builddir)).

CLASSPATH_ENV

Выражение оболочки (shell), используемое для установки переменной среды CLASSPATH в командной строке javac. В будущем обработка пути к классам может измениться.

10.5. Python

Automake поддерживает компиляцию Python с помощью переменной PYTHON, которая обычно устанавливается вызовом макроса AM_PATH_PYTHON в configure.ac, и используется, как в приведенном ниже фрагменте Makefile.am.

    python_PYTHON = tree.py leave.py

Для всех файлов, указанных в переменной _PYTHON будет выполнена байт-компиляция с помощью py-compile в процессе установки. Программа py-compile на деле создает стандартные (.pyc) и оптимизированные (.pyo) варианты байт-кода исходных файлов. Отметим, что в результате компиляции в процессе установки файлы, перечисленные   в noinst_PYTHON, компилироваться не будут. Исходные файлы Python по умолчанию включаются в дистрибутив и для их исключения следует применять префикс nodist_ (как nodist_python_PYTHON).

Automake распространяется с макросом Autoconf AM_PATH_PYTHON, который определяет некоторые связанные с Python переменные каталогов (см. ниже). При вызове AM_PATH_PYTHON из файла configure.ac можно использовать переменные python_PYTHON или pkgpython_PYTHON для перечисления исходных файлов Python в Makefile.am в зависимости от того, следует ли их устанавливать (см. pythondir и pkgpythondir ниже).

Макрос AM_PATH_PYTHON ([VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])

Отыскивает в системе интерпретатор Python. Макрос принимает 3 необязательных параметра. При наличии первого аргумента он указывает минимальную версию Python, требуемую для пакета и AM_PATH_PYTHON будет пропускать интерпретаторы Python с версией ниже VERSION. Если интерпретатор найден и соответствует VERSION, выполняется ACTION-IF-FOUND. В остальных случаях применяется ACTION-IF-NOT-FOUND. Если действие ACTION-IF-NOT-FOUND не задано, как в приведенном ниже примере, по умолчанию работа сценария configure прерывается.

         AM_PATH_PYTHON([2.2])

Это нормально, когда Python является абсолютным требованием для пакета. Если же Python 2.5 является лишь опцией для пакета, AM_PATH_PYTHON можно вызвать в форме

         AM_PATH_PYTHON([2.5],, [:])

Если переменная PYTHON установлена при вызове AM_PATH_PYTHON, проверяться будет лишь заданный интерпретатор Python.

Макрос AM_PATH_PYTHON создает приведенные ниже переменные на основе установки Python, найденной при настройке.

PYTHON

Имя исполняемого файла Python или :, если подходящий интерпретатор не найден. В предположении использования ACTION-IF-NOT-FOUND (иначе ./configure прервет работу при отсутствии Python), значение PYTHON можно использовать для задания условий отключения сборки соответствующих частей, как показано ниже.

         AM_PATH_PYTHON(,, [:]) 
         AM_CONDITIONAL([HAVE_PYTHON], [test "$PYTHON" != :])

PYTHON_VERSION

Номер версии интерпретатора Python в форме MAJOR.MINOR (например, 2.5) — текущее значение sys.version[:3].

PYTHON_PREFIX

Строка ${prefix}, которая может использоваться в будущей работе, где потребуется содержимое Python sys.prefix, но общепринято всегда использовать значение из configure.

PYTHON_EXEC_PREFIX

Строка ‘${exec_prefix}, которая может использоваться в будущей работе, где потребуется содержимое Python sys.exec_prefix, но общепринято всегда использовать значение из configure.

PYTHON_PLATFORM

Каноническое имя, используемое Python для описания операционной системы, как указано в sys.platform. Это значение иногда требуется при сборке расширений Python.

pythondir

Имя каталога для размещения site-packages в стандартном дереве установки Python.

pkgpythondir

Каталог в pythondir, который указывается после пакета, т. е. $(pythondir)/$(PACKAGE).

pyexecdir

Каталог, в который следует устанавливать расширения Python (общие библиотеки). Модули расширения, написанные на C, можно объявлять Automake как показано ниже.

         pyexec_LTLIBRARIES = quaternion.la 
         quaternion_la_SOURCES = quaternion.c support.c support.h 
         quaternion_la_LDFLAGS = -avoid-version -module

pkgpyexecdir

Вспомогательная переменная, задаваемая в форме $(pyexecdir)/$(PACKAGE).

Все указанные переменные каталогов, которые начинаются с префикса ${prefix} или ${exec_prefix}, не преобразуются. Это хорошо работает в Makefiles, но осложняет использование переменных в configure. Такой подход диктуют стандарты кодирования GNU и пользователь может запустить make prefix=/foo install. В руководстве Autoconf этот вопрос рассмотрен более подробно. См. также параграф 27.10. Установка в жестко заданные места.

11. Сборка документации

В настоящее время Automake поддерживает Texinfo и страницы man.

11.1. Texinfo

Если в текущем каталоге есть исходные файлы Texinfo, нужно объявить их в переменной TEXINFOS. Обычно файлы Texinfo преобразуются в info, поэтому здесь как правило должна применяться переменная info_TEXINFOS. Источники Texinfo должны иметь расширение .texi. Automake воспринимает также расширения .txi и .texinfo, но их использование не рекомендуется и будет вызывать предупреждения.

Automake генерирует файлы для сборки .info, .dvi, .ps, .pdf и .html из источников Texinfo. В соответствии со стандартами кодирования GNU лишь файлы .info собираются по команде make all и устанавливаются по команде make install (если не задано no-installinfo). Файлы .info распространяются автоматически, поэтому наличие Texinfo не требуется для установки пакета. Следует отметить, что созданные файлы .info в отличие от других по умолчанию помещаются в srcdir, а не в builddir. Это можно изменить с помощью опции info-in-builddir.

Документация в других форматах может быть создана командами make dvi, make ps, make pdf, make html и явно установлена командами make install-dvi, make install-ps, make install-pdf и make install-html. Команда make uninstall удаляет все — установленную по умолчанию документацию и результаты приведенных выше команд. Все эти цели могут быть преобразованы с помощью правил -local (23.1. Расширение правил Automake).

Если файл .texi включает (@include) version.texi, этот файл будет создан автоматически. Файл version.texi определяет 4 флага Texinfo, которые можно использовать — @value{EDITION}, @value{VERSION}, @value{UPDATED} и @value{UPDATED-MONTH}.

EDITION
VERSION

Обе эти переменные содержат номер версии программы.

UPDATED

Дата изменения основного файла .texi.

UPDATED-MONTH

Название месяца, когда был изменен основной файл .texi.

Для поддержки version.texi нужен сценарий mdate-sh, который предоставляется Automake и включается автоматически при вызове automake с опцией —add-missing.

Когда имеется множество файлов Texinfo и нужно использовать свойство version.texi, требуется отдельный файл версии для каждого файла Texinfo. Automake будет обрабатывать любое включение в файл Texinfo, соответствующее vers*.texi, как автоматически созданный файл версии. Иногда файл info реально зависит от нескольких файлов .texi. Например, в GNU Hello файл hello.texi включает fdl.texi. Можно указать Automake такую зависимость с помощью переменной TEXI_TEXINFOS. Например, для GNU это имеет вид

    info_TEXINFOS = hello.texi 
    hello_TEXINFOS = fdl.texi

По умолчанию Automake требует наличия файла texinfo.tex в одном каталоге с Makefile.am, где указаны файлы .texi. При использовании AC_CONFIG_AUX_DIR в configure.ac, файл texinfo.tex отыскивается. В обоих случаях automake затем представляет texinfo.tex, если задана опция —add-missing и пытается распространить этот файл. Однако при установке переменной TEXINFO_TEX (см. ниже) она переопределяет местоположение файла и отключает его установку в источники, а также распространение.

Опция no-texinfo.tex позволяет обойти требование наличия texinfo.tex. Использование переменной TEXINFO_TEX предпочтительно, однако данные опция позволяет работать с целями dvi, ps и pdf, поэтому сохранена.

Automake создает правило install-info и некоторые люди его используют. По умолчанию страницы info устанавливаются командой make install, поэтому команда make install-info смысла не имеет. Установку можно отключить опцией no-installinfo и файлы .info не будут устанавливаться по умолчанию и пользователю придется явно задать make install-info.

По умолчанию команды make install-info и make uninstall-info пытаются запустить install-info (если программа доступна) для обновления (или создания/удаления) индекса ${infodir}/dir. Если это не нужно, можно экспортировать переменную AM_UPDATE_INFO_DIR со значением no.

Ниже приведены переменные, используемые правилами сборки Texinfo.

MAKEINFO

Имя программы, вызываемой для сборки файлов .info, которая задается Automake. Если программа makeinfo найдена в системе, она будет использоваться по умолчанию. В противном случае применяется missing.

MAKEINFOHTML

Команда для сборки файлов .html, которую Automake задает как $(MAKEINFO) —html.

MAKEINFOFLAGS

Пользовательские флаги, передаваемые при каждом вызове $(MAKEINFO) и $(MAKEINFOHTML). Эта пользовательская переменная (3.6. Пользовательские переменные) не предполагается в файлах Makefile и может применяться пользователем для указания дополнительных флагов.

AM_MAKEINFOFLAGS
AM_MAKEINFOHTMLFLAGS

Флаги сопровождающего, передаваемые при каждом вызове makeinfo. В отличие от MAKEINFOFLAGS, эти переменные задаются сопровождающим в файле Makefile.am. $(AM_MAKEINFOFLAGS) передается makeinfo при сборке файлов .info, $(AM_MAKEINFOHTMLFLAGS) применяется при сборке .html. Например, приведенная ниже строка задает создание одного файла .html для руководства без разделителей тем (node).

	AM_MAKEINFOHTMLFLAGS = --no-headers --no-split

AM_MAKEINFOHTMLFLAGS по умолчанию имеет значение $(AM_MAKEINFOFLAGS), это означает, что задание AM_MAKEINFOFLAGS без указания AM_MAKEINFOHTMLFLAGS будет влиять на сборку .info и .html.

TEXI2DVI

Имя команды преобразования .texi в .dvi. По умолчанию это texi2dvi (сценарий из пакета Texinfo).

TEXI2PDF

Имя команды преобразования .texi в .pdf’ file. По умолчанию это $(TEXI2DVI) —pdf —batch.

DVIPS

Имя команды для сборки файла .ps из .dvi (по умолчанию dvips).

TEXINFO_TEX

Если пакет содержит файлы Texinfo в разных каталогах, эта переменная говорит Automake, где находится канонический файл texinfo.tex для пакета. В переменной должен указываться относительный путь от текущего файла Makefile.am к texinfo.tex, например,

         TEXINFO_TEX = ../doc/texinfo.tex

11.2. Страницы Man

Пакет может также включать страницы man, которые объявляются в переменной MANS (обычно используется man_MANS). Man-страницы автоматически устанавливаются в корректный каталог mandir на основе расширения. Расширения, подобные .1c, обрабатываются путем поиска и использования найденного расширения для выбора нужного каталога mandir. Имена разделов обозначаются цифрами от 0 до 9, а также буквами l и n.

Иногда разработчики предпочитают именовать страницы руководства в форме foo.man внутри дерева кода, а затем переименовывать их с подходящим суффиксом (например, foo.1) при установке. Automake поддерживает такой режим. Для корректного раздела SECTION имеется соответствующий каталог manSECTIONdir и переменная _MANS. Указанные в такой переменной файлы устанавливаются в соответствующий раздел. Если файл уже имеет корректный суффикс, он устанавливается как есть, в остальных случаях суффикс устанавливается в соответствии с разделом. Например, в случае

    man1_MANS = rename.man thesame.1 alsothesame.1c

файл rename.man будет переименован при установке в rename.1, а имена остальных сохранятся.

По умолчанию man-страницы устанавливаются командой make install. Однако проект GNU не требует страниц man и многие сопровождающие не тратят сил на поддержку их актуальности. В таких случаях опция no-installman будет отключать установку страниц man по умолчанию. Пользователь может явно установить их командой make install-man.

Для быстрой установки с большим числом файлов предпочтительно использовать manSECTION_MANS, а не man_MANS и файлы, которые не нужно переименовывать.

Man-страницы в настоящее время не считаются источниками, поскольку автоматическая их генерация не является общепринятой. В результате они не включаются в дистрибутив автоматически . Однако это можно изменить с помощью префикса dist_. Ниже показано, как распространить и установить две страницы man пакета GNU cpio, который включает документацию Texinfo и страницы man.

    dist_man_MANS = cpio.1 mt.1

Префикс nobase_ для страниц man не имеет смысл, поэтому он не разрешен.

Исполняемые файлы и страницы man можно переименовать после установки (2.2.9. Переименование программ при установке). Для страниц man это можно предотвратить с помощью префикса notrans_. Например, для исполняемого файла foo, разрешающего доступ к библиотеке foo из командной строки можно избежать переименования foo.3 в виде

    man_MANS = foo.1 
    notrans_man_MANS = foo.3

Префикс notrans_ должен быть задан впереди при использовании с dist_ или nodist_ (14.2. Тонкая настройка распространения). Например,

    notrans_dist_man3_MANS = bar.3

12. Установка

Automake обрабатывает детали реальной установки программы после ее сборки. Все файлы, указанные первичными переменными, устанавливаются в нужные места по команде make install.

12.1. Основы инсталляции

Файл, названный в первичной переменной, устанавливается путем копирования результата сборки в подходящий каталог с использованием базового имени

    bin_PROGRAMS = hello subdir/goodbye

В этом примере hello и goodbye устанавливаются в $(bindir).

Иногда полезно избегать этапа базового имени при установке. Например, может присутствовать множество заголовочных файлов в подкаталогах дерева источников, расположенных точно так, как их нужно установить. В таких случаях можно использовать префикс nobase_ для исключения этапа базового имени. Например,

    nobase_include_HEADERS = stdio.h sys/types.h

будет устанавливать stdio.h в $(includedir) а types.h в $(includedir)/sys.

Для большинства типов файлов Automake будет устанавливать множество файлов в один прием, избегая в то же время слишком длинных команд (3.4. Ограничение размера команд). Поскольку некоторые программы install не устанавливают один и тот же файл дважды за вызов, может потребоваться проверка уникальности имен файлов, указанных в одной переменной (например, nobase_include_HEADERS).

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

12.2. Две части установки

Automake создает отдельные правила install-data и install-exec, если установщик инсталлирует программу на разные машины в общей структурой каталогов. Это позволяет установить независимые от машины компоненты в один прием. Цель install-exec служит для установки зависимых от платформы файлов, а install-data — для независимых. Цель install зависит от обеих отмеченных целей. Хотя Automake пытается автоматически разделить объекты по категориям, автор Makefile.am в конечном итоге отвечает за корректность этого деления.

Переменные со стандартными префиксами data, info, man, include, oldinclude, pkgdata и pkginclude помещаются в цель install-data. Переменные со стандартными префиксами bin, sbin, libexec, sysconf, localstate, lib, pkglib устанавливаются целью install-exec. Например, файлы data_DATA устанавливаются целью install-data, а bin_PROGRAMS — install-exec. Любая переменная с пользовательским префиксом, включающим exec (например, myexecbin_PROGRAMS) будет отнесена к install-exec, а все прочие — к install-data.

12.3. Расширение установки

Можно расширить механизм, задав правило install-exec-local или install-data-local, которое будет выполняться по команде make install. Эти правила могут делать почти все и нужна осторожность.

Automake поддерживает также две «ловушки» (hook) — install-exec-hook и install-data-hook, которые работают после всех остальных правил установки соответствующего типа (exec или data). Например, можно внести изменения после установки с помощью таких ловушек (23.1. Расширение правил Automake).

12.4. Двухэтапная установка

Automake поддерживает переменную DESTDIR для всех правил установки, которая применяется при выполнении команды make install для перемещения объектов в промежуточную область (staging). Для каждого объекта и пути используется префикс, заданный значением DESTDIR, перед копированием в место установки. Например,

    mkdir /tmp/staging && 
    make DESTDIR=/tmp/staging install

Команда mkdir позволяет избежать проблем с безопасностью, когда злоумышленник создает символьную ссылку из /tmp/staging на область жертвы. Программа make затем устанавливает объекты в каталог /tmp/staging. При установке /gnu/bin/foo и /gnu/share/aclocal/foo.m4 показанная выше команда будет создавать /tmp/staging/gnu/bin/foo и /tmp/staging/gnu/share/aclocal/foo.m4. Это свойство обычно применяется для сборки установочных образов и пакетов (2.2.10. Сборка двоичных пакетов с использованием DESTDIR).

Поддержка DESTDIR реализована путем прямого включения в правила установки. Если Makefile.am использует правило локальной установки (например, install-exec-local) или установочную ловушку, нужно включать в них DESTDIR.

12.5. Правила установки для пользователя

Automake также создает правила для целей uninstall, installdirs и install-strip, а также поддерживает uninstall-local и uninstall-hook. Нет отдельных деинсталляций для exec и data, поскольку эти функции не имеют дополнительной функциональности. Отметим, что цель uninstall не является заменой инструментов для работы с пакетами.

13. Очистка

GNU Makefile Standards задает множество разных правил очистки.

Обычно файлы для очистки Automake выбирает автоматически. При этом используются переменные, которые можно задать для управления очисткой, — MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, MAINTAINERCLEANFILES.

Если очистка включает не только удаление жестко заданного набора файлов, можно задать дополнительные правила со своими командами. Это делается путем задания правил для цели mostlyclean-local, clean-local, distclean-local или maintainer-clean-local (23.1. Расширение правил Automake). Распространенным случаем является удаление каталога, например, созданного тестами.

    clean-local: 
            -rm -rf testSubDir

Поскольку make принимает лишь один набор правил для данной цели, можно расширить создание правил за счет указания отдельной цели в качестве зависимости.

    clean-local: clean-local-check 
    .PHONY: clean-local-check 
    clean-local-check: 
            -rm -rf testSubDir

Поскольку стандарты GNU не всегда четко указывают, какие файлы следует удалять по какому правилу, здесь приведены некоторые рекомендации, которые изначально сформулировал Франсуа Пинар (François Pinard).

  • Если команда make создала что-то перестраиваемое (например, файл .o), правилу mostlyclean следует удалять такой объект.

  • В остальных случаях созданное командой make следует удалять цели clean.

  • Если команда configure создала объект, цели distclean следует удалять его.

  • Если сопровождающий создал объект (например, файл .info), цели maintainer-clean следует удалять его. Однако этой цели не следует удалять ничего, что нужно для выполнения команд ./configure && make.

Рекомендуется следовать этим правилам в файлах Makefile.am.

14. Распространение

14.1. Основы дистрибутивов

Правило dist в созданном файле Makefile.in можно использовать для создания сжатого (gzip) архива tar и других архивов. Имя файла выбирается автоматически на основе переменных PACKAGE и VERSION, заданных вызовом AC_INIT или (устаревшего) макроса AM_INIT_AUTOMAKE с двумя аргументами (6.4.1. Макросы общего пользования).  Точнее говоря, архив называется ${PACKAGE}-${VERSION}.tar.gz. Для управления работой gzip можно использовать переменную make GZIP_ENV, в которой по умолчанию установлено значение —best.

В большинстве случаев файлы для распространения автоматически выбирает Automake. Все файлы с исходным кодом, а также Makefile.am и Makefile.in автоматически включаются в архив. Automake также включает список часто используемых файлов, включаемых при их обнаружении в текущем каталоге (реально или как цель в правиле Makefile.am). Этот список выводится по команде automake —help. Отметим, что некоторые файлы из этого списка на деле распространяются лишь при выполнении некоторых условий (например, файлы config.h.top и config.h.bot распространяются лишь при использовании AC_CONFIG_HEADERS([config.h]) в configure.ac). Файлы, читаемые сценарием configure (исходные файлы, соответствующие файлам, указанным в разных макросах Autoconf, таких как AC_CONFIG_FILES и его «братья»), распространяются автоматически. Файлы, включенные в Makefile.am (оператор include) или configure.ac (m4_include), а также вспомогательные сценарии, установленные командой automake —add-missing, также распространяются.

Тем не менее, иногда остаются файлы, которые нужно распространить, но они не включаются автоматически. Эти файлы следует указывать в переменной EXTRA_DIST, куда можно включать файлы из подкаталогов. Можно также указать в переменной каталог и он будет целиком рекурсивно скопирован в дистрибутив. Отметим, что при этом копируются все файлы, включая, например, каталоги Subversion (.svn) а также версии CVS/RCS управляющих файлов, поэтому такую возможность следует использовать с осторожностью. Однако можно воспользоваться свойством dist-hook для смягчения проблемы (14.3. «Ловушка» dist).

При указании SUBDIRS программа Automake будет рекурсивно включать подкаталоги в дистрибутив. Если переменная задается условно (20. Конструкции с условием), Automake обычно будет включать все каталоги, которые могут появиться в SUBDIRS. Если нужно задать включение некоторых каталогов по условию, можно задать переменную DIST_SUBDIRS с точным списком каталогов для включения в дистрибутив (7.2. Условные подкаталоги).

14.2. Тонкая настройка распространения

Иногда нужна тонкая настройка исключаемых из дистрибутива файлов, например, некоторые создаваемые автоматически файлы не нужно распространять. Для таких случаев Automake предоставляет префиксы тонкой настройки dist и nodist. Любая первичная переменная или _SOURCES может указываться с префиксом dist_ для включения в дистрибутив и nodist_ — для исключения. Ниже показано, как включить данные и исключить один файл.

    dist_data_DATA = distribute-this 
    bin_PROGRAMS = foo 
    nodist_foo_SOURCES = do-not-distribute.c

14.3. «Ловушка» dist

Иногда полезна возможность изменить дистрибутив перед его упаковкой. При наличии правила dist-hook оно выполняется после заполнения каталога дистрибутива, но до создания архива. Одним из вариантов применения этого правила является удаление ненужных файлов с рекурсией в каталоге, заданном переменной EXTRA_DIST.

    EXTRA_DIST = doc 
    dist-hook: 
            rm -rf `find $(distdir)/doc -type d -name .svn`

Отметим, что заданию dist-hook не следует считать, что обычные файлы в дистрибутиве открыты для записи. Это может не выполняться при создании пакетов из дерева кода, доступного лишь для чтения, или при выполнении команды make distcheck. По тем же причинам заданию не следует предполагать, что подкаталоги, включенные в каталог дистрибутива в результате их включения в EXTRA_DIST, открыты для записи. Если задание dist-hook хочет изменить содержимое имеющегося файла (или каталога EXTRA_DIST) в каталоге дистрибутива, нужно сначала явно разрешить запись в него, как показано ниже.

    EXTRA_DIST = README doc 
    dist-hook: 
            chmod u+w $(distdir)/README $(distdir)/doc 
            echo "Distribution date: `date`" >> README 
            rm -f $(distdir)/doc/HACKING

Для создания правил dist-hook пригодятся переменные $(distdir) и $(top_distdir). Переменная $(distdir) указывает каталог, куда правило dist будет копировать файлы из текущего каталога перед созданием архива. При работе из каталога верхнего уровня distdir = $(PACKAGE)-$(VERSION), а при работе из foo/ в корневом каталоге distdir = ../$(PACKAGE)-$(VERSION)/foo. Переменная $(distdir) может указывать абсолютный или относительный путь.

Переменная $(top_distdir) всегда указывает на корневой каталог дистрибутива. Для каталога верхнего уровня она совпадает с $(distdir), а в подкаталоге foo/ top_distdir = ../$(PACKAGE)-$(VERSION)’. Переменная $(top_distdir) может указывать абсолютный или относительный путь.

Отметим, что для пакетов, вложенных с использованием AC_CONFIG_SUBDIRS (7.4. Вложенные пакеты), переменные $(distdir) и $(top_distdir) указываются относительно пакета, где используется команда make dist.

14.4. Проверка дистрибутива

Automake создает правило distcheck, которое может помочь в обеспечении корректной работы создаваемого дистрибутива. Если немного упростить, можно сказать, что сначала это правило создает дистрибутив, затем, работая с ним, выполняет перечисленные ниже действия:

  • попытка сборки VPATH (2.2.6. Параллельная сборка (VPATH)) с srcdir и всем содержимым в режиме read-only;

  • запуск тестов (make check) на свежей сборке;

  • установка пакета во временный каталог (make install) и запуск тестов в нем (make installcheck);

  • проверка корректности удаления (make uninstall) и очистки (make distclean) пакета;

  • создание еще одного архива для проверки самодостаточности дистрибутива.

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

DISTCHECK_CONFIGURE_FLAGS

Сборка пакета включает запуск сценария ./configure. Если нужно передать сценарию дополнительные флаги, следует задать их в переменной AM_DISTCHECK_CONFIGURE_FLAGS в файле Makefile.am на верхнем уровне. Пользователь может добавить или переопределить эти флаги указанием переменной DISTCHECK_CONFIGURE_FLAGS в строке команды make. Следует отметить, что команде make distcheck требуется полный контроль над опциями сценария configure —srcdir и —prefix, поэтому их нельзя переопределить в переменной AM_DISTCHECK_CONFIGURE_FLAGS или DISTCHECK_CONFIGURE_FLAGS.

Отметим также, что разработчикам рекомендуется делать свой код собираемым без специальных опций configure, т. е., обычно от пользователя не должно требоваться указание переменной AM_DISTCHECK_CONFIGURE_FLAGS. Однако в некоторых случаях использование этой переменной оправдано. Пример этого представлен в GNU m4, где по умолчанию отключено экспериментальное и редко используемое свойство changeword. Для использования этой функции перед использованием make distcheck запустить configure с параметром —with-changeword для корректной компиляции кода changeword. GNU m4 также использует переменную AM_DISTCHECK_CONFIGURE_FLAGS для нагрузочных тестов использования —program-prefix=g, поскольку в какой-то момент в системе сборки m4 возникла ошибка, в результате которой команда make installcheck ошибочно предполагала возможность проверки m4 вслепую, а не свежеустановленную программу gm4.

distcheck-hook

Если правило distcheck-hook определено в файле Makefile.am верхнего уровня, оно будет вызываться целью distcheck после распаковки нового дистрибутива, но до настройки конфигурации и сборки пакета. Правило distcheck-hook может делать почти все, но нужна некоторая осторожность. Обычно правило применяется для проверки потенциальных ошибок дистрибутива, не замеченных стандартным механизмом. Отметим, что distcheck-hook, как и AM_DISTCHECK_CONFIGURE_FLAGS, DISTCHECK_CONFIGURE_FLAGS не используются в Makefile.am субпакетов, но флаги из AM_DISTCHECK_CONFIGURE_FLAGS и DISTCHECK_CONFIGURE_FLAGS передаются сценариям configure в субпакетах.

distcleancheck

В части возможных ошибок дистрибутива distcheck гарантирует, что правило distclean на деле удаляет все собранные файлы. Это выполняется командой make distcleancheck в конце сборки VPATH. По умолчанию distcleancheck запускает distclean, а затем проверяет очистку дерева сборки запуском $(distcleancheck_listfiles). Обычно эта проверка находит созданные файлы, которые не были включены в DISTCLEANFILES (13. Очистка).

Для большинства пакетов distcleancheck работает корректно, в остальных случаях нужно переопределить правило distcleancheck или переменную $(distcleancheck_listfiles). Например, для полного запрета distcleancheck можно добавить в Makefile.am верхнего уровня правило, показанное ниже.

    distcleancheck: 
            @:

Если цель distcleancheck должна игнорировать собранные файлы, которые не были удалены по причине включения в дистрибутив, следует добавить приведенное ниже определение.

    distcleancheck_listfiles = \ 
      find . -type f -exec sh -c 'test -f $(srcdir)/$$1 || echo $$1' \ 
           sh '{}' ';'

Это определение не применяется по умолчанию, поскольку требование Makefile повторно собирать некоторые файлы, собранные пользователем, обычно является ошибкой (27.5. Ошибки distclean).

distuninstallcheck

Цель distcheck также проверяет корректность работы правила uninstall для обычной и DESTDIR-сборки. Это выполняется вызовом make uninstall и последующей проверки дерева установки на предмет оставшихся файлов. По умолчанию проверка выполняется правилом distuninstallcheck, а список файлов дерева установки создается командой $(distuninstallcheck_listfiles). Правила и переменная позволяют изменить поведение distcheck. Например, для полного отключения проверки можно задать

    distuninstallcheck: 
            @:

14.5. Типы распространения

Automake создает правила для создания архивов дистрибутива в перечисленных ниже форматах.

dist-gzip

Создает архив gzip из файла tar. Это единственный формат, включенныый по умолчанию.

dist-bzip2

Создает архив bzip2 из файла tar. Формат bzip2 зачастую снижает размер архива по сравнению с gzip. По умолчанию это правило создает архив bzip2 с опцией сжатия -9, для изменения можно установить переменную среды BZIP2, например, make dist-bzip2 BZIP2=-7.

dist-lzip

Создает архив lzip из файла tar. Формат lzip зачастую снижает размер архива по сравнению bzip2.

dist-xz

Создает архив xz из файла tar. Формат xz зачастую снижает размер архива по сравнению bzip2. По умолчанию это правило создает архив xz с опцией сжатия -e, для изменения можно установить переменную среды XZ_OPT, например, make dist-xz XZ_OPT=-ve.

dist-zip

Создает архив zip.

dist-tarZ

Создает архив tar, сжатый устаревшей программой compress. Опция устарела и будет удалена из Automake 2.0.

dist-shar

Создает архив shar. Формат устарел и применять его не рекомендуется, опция будет удалена из Automake 2.0.

Правило dist (и его прежний синоним dist-all) создает архивы во всех включенных форматах (17.2. Список опций Automake). По умолчанию используется лишь формат dist-gzip.

15. Поддержка тестов

Automake может генерировать код для обработки двух типов тестов, один из которых основан на интеграции со схемой dejagnu, другой (используется чаще) — на использовании базовых тестовых сценариев с активацией их заданием специальной переменной TESTS. Второй вариант поддерживает разные варианты сложности и настройки, позволяя выполнять тесты одновременно, используя такие протоколы тестирования как TAP, а также задание пользовательских драйверов и исполнителей тестов. Тестирование запускается командой make check.

15.1. Общие вопросы тестирования

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

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

Тест считается успешным (pass), когда поведение соответствует ожидаемому и неудачным (fail) в противном случае. Иногда тесты могут основываться на непереносимых инструментах или предварительных условиях, а также могут просто не иметь смысла в той или иной системе (например, проверка связанных с Windows свойств в GNU/Linux). В таких случаях тесты пропускаются (skip) без запуска, а результат не учитывается. Пропуски обычно указываются в отчете, чтобы пользователь знал о реально выполненных проверках.

Нередко, особенно на начальных этапах разработки, отказы части тестов происходят по известным причинам и разработчик пока не хочет устранять их. В таких случаях лучше указать такие отказы как ожидаемые (xfail). Если такой тест проходит, в отчете он отмечается как неожиданной успешный (xpass).

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

15.2. Простые тесты

15.2.1. Тесты на основе сценариев

При задании специальной переменной TESTS ее значение считается списком программ или сценариев, используемых для тестирования. При соответствующих обстоятельствах TESTS может также содержать список файлов данных для передачи одному или нескольким тестовым сценариям, заданных другими способами (так называемые «компиляторы журналов», 15.2.3. Параллельные тесты). Тестовые сценарии могут выполняться последовательно или одновременно, по умолчанию применяется параллельное тестирование, обеспечиваемое реализацией make (если оно поддерживается).

По умолчанию в качестве результатов набора тестов рассматривается лишь статус завершения тестовых сценариев. Однако Automake поддерживает более сложные протоколы тестирования, как стандартные (15.4. Использование протокола TAP), так и пользовательские (15.3. Драйверы тестов). Отметим, что эти протоколы невозможно включить для последовательного тестирования. Далее рассматриваются в основном тесты без протокола, поскольку протоколам тестирования посвящен отдельный параграф 15.3. Драйверы тестов. Когда протокол тестирования не применяется, статус 0 на выходе сценария означает успешный результат, 77 — пропуск теста, 99 — серьезную ошибку, а все остальные значения — отказ.

Можно задать переменную XFAIL_TESTS для списка тестов (обычно подмножество TESTS), в которых заранее предполагается отказ. Это будет инвертировать результаты указанных тестов (при условии, что пропуски и серьезные ошибки не будут затронуты). Можно также задать трактовку серьезных ошибок как обычных отказов, задав непустое значение в переменной DISABLE_HARD_ERRORS. Однако следует отметить, что для тестов на основе более сложных протоколов точное влияние переменных XFAIL_TESTS и DISABLE_HARD_ERRORS может меняться и даже возможно их полное игнорирование (например, в тестах с использованием TAP нет возможности отключить серьезные ошибки и переменная DISABLE_HARD_ERRORS просто не работает).

Результат каждого теста, запускаемого сценариями из TESTS, будет выводиться на стандартное устройство stdout вместе с именем теста. Для протоколов тестирования, которые поддерживают множество тестов в сценарии (например, TAP) в дополнение к имени тестового сценария ожидается номер, идентификатор и краткое описание отдельных тестов. Возможные результаты (15.1. Общие вопросы тестирования) могут иметь значение PASS, FAIL, SKIP, XFAIL, XPASS, ERROR. Ниже приведен пример вывода набора тестов, использующего обычные сценарии и TAP.

    PASS: foo.sh 
    PASS: zardoz.tap 1 - Daemon started 
    PASS: zardoz.tap 2 - Daemon responding 
    SKIP: zardoz.tap 3 - Daemon uses /proc # SKIP /proc is not mounted 
    PASS: zardoz.tap 4 - Daemon stopped 
    SKIP: bar.sh 
    PASS: mu.tap 1 
    XFAIL: mu.tap 2 # TODO frobnication not yet implemented

Сводка по набору тестов (по крайней мере число запущенных, пройденных и отказавших тестов) выводится по завершении работы. Если стандартное устройство вывода подключено к подходящему терминалу, результаты теста и сводка выводятся с цветовым выделением. Разработчик и пользователь могут отключить цветовой вывод, передав в команде make переменную AM_COLOR_TESTS=no. Пользователь может задать цветовой вывод даже без подключения терминала, задав AM_COLOR_TESTS=always. Следует также отметить, что некоторые реализации make при работе в параллельном режиме могут различаться по семантике, что может препятствовать автоматическому определению возможностей подключенного терминала. В этом случае пользователю придется задать переменную AM_COLOR_TESTS=always для получения цветового вывода.

Тестовые программы, которым нужны файлы данных, должны искать их в srcdir (переменная make и переменная среды, доступной для тестов), чтобы тесты работали даже при сборке в отдельном каталоге и, в частности, для правила distcheck (14.4. Проверка дистрибутива).

Переменные AM_TESTS_ENVIRONMENT и TESTS_ENVIRONMENT можно использовать для запуска кода инициализации и настройки окружения для тестовых сценариев. Первая переменная предназначена для разработчиков и может быть задана в файле Makefile.am. Другая переменная предназначена для пользователя, который может с ее помощью расширить или переопределить первую переменную, однако при этом для переносимости требуется, чтобы непустая переменная AM_TESTS_ENVIRONMENT завершалась символом двоеточия.

Переменную AM_TESTS_FD_REDIRECT можно использовать для задания перенаправлений файловых дескрипторов в тестовых сценариях. Можно подумать, что для этого подходит переменная AM_TESTS_ENVIRONMENT, но опыт показывает, что это препятствует переносимости. Основным препятствием являются оболочки Korn, которые обычно устанавливают флаг close-on-exec для файловых дескрипторов, открытых внутренней функцией exec, что делает конструкции вида AM_TESTS_ENVIRONMENT = exec 9>&2; неэффективными. Проблема возникает также в некоторых оболочках Bourne, например, HP-UX /bin/sh.

    AM_TESTS_ENVIRONMENT = \ 
    ## Some environment initializations are kept in a separate shell 
    ## file 'tests-env.sh', which can make it easier to also run tests 
    ## from the command line. 
      . $(srcdir)/tests-env.sh; \ 
    ## On Solaris, prefer more POSIX-compliant versions of the standard 
    ## tools by default. 
      if test -d /usr/xpg4/bin; then \ 
        PATH=/usr/xpg4/bin:$$PATH; export PATH; \ 
      fi; 
    ## With this, the test scripts will be able to print diagnostic 
    ## messages to the original standard error stream, even if the test 
    ## driver redirects the stderr of the test scripts to a log file 
    ## before executing them. 
    AM_TESTS_FD_REDIRECT = 9>&2

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

Automake гарантирует создание каждого файла, указанного в переменной TESTS, до его запуска. В переменной можно указать исходные файлы и производные программы (или сценарии), созданное правило будет видно в каталогах srcdir и ‘.’. Например, может возникнуть потребность выполнения в качестве теста программы C. Для этого нужно включить имя теста в TESTS и check_PROGRAMS, а затем указать его как любую другую программу. Программы, перечисленные в check_PROGRAMS (а также в check_LIBRARIES, check_LTLIBRARIES…) собираются только по команде make check, а не make all. Здесь следует указывать любые программы, требуемые для теста, которые не нужно собирать по команде make all. Отметим, что check_PROGRAMS не включается автоматически в TESTS, поскольку в check_PROGRAMS обычно задаются используемые тестами программы, а не сами тесты. Можно задать TESTS = $(check_PROGRAMS), если все программы являются контрольными примерами.

15.2.2. Последовательные тесты (устарели)

Отметим сначала, что сегодня настоятельно не рекомендуется применять такие тесты, используя взамен параллельные, которые описаны ниже. Тем не менее возникают ситуации, когда преимущества параллельных тестов не важны и одновременное тестирование может даже вызывать проблемы. В таких случаях можно использовать простые и надежные последовательные тесты. Этот режим включается опцией Automake serial-tests и тесты просто запускаются один за другим с записью результатов, если это нужно.

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

    TESTS_ENVIRONMENT = $(PERL) -Mstrict -w 
    TESTS = foo.pl bar.pl baz.pl

Важно отметить, что описанное здесь применение TESTS_ENVIRONMENT непригодно для параллельных тестов, где обеспечивается более изящное решение, дающее такой же эффект и дополнительное преимущество за счет освобождения переменной TESTS_ENVIRONMENT для пользователя. Менее серьезным ограничением последовательного тестирования является неразличимость простых отказов и серьезных ошибок. Это обусловлено историческими причинами и может быть исправлено в будущих версиях Automake.

15.2.3. Параллельные тесты

По умолчанию Automake генерирует параллельные (одновременные) тесты. Это включает автоматический сбор результатов тестовых сценариев в журнал (.log), одновременное выполнение тестов с опцией make -j, заданием зависимостей между тестами, возврат к незавершенным тестам и фиксацию серьезных ошибок. Параллельное тестирование выполняется путем создания набора правил make для запуска тестовых сценариев, указанных в переменной TESTS и сохранение вывода каждого сценария в журнальном файле .log, а также результатов (и других «метаданных», 15.3.3. API для сторонних драйверов) в файлах .trs. Файл .log содержит весь вывод теста, выдаваемый на устройства stdoutput и stderr. Файл .trs содержит результаты контрольных примеров, выполняемых сценарием. Для параллельных тестов создается также общий журнал, указанный переменной TEST_SUITE_LOG (по умолчанию test-suite.log) и имеющий расширение .log. Этот файл зависит от всех файлов .log и .trs, указанных в переменной TESTS.

Как и для описанных выше последовательных тестов, по умолчанию выводится 1 строка результата каждого выполненного теста и краткая сводка по завершении тестирования. Однако вывод stdout и stderr перенаправляется ф журнальный файл на уровне теста, поэтому вывод параллельных тестов не перемешивается. Вывод отказавших тестов собирается в общий файл test-suite.log. Если установлена переменная VERBOSE, содержимое этого файла выводится после сводки.

Каждая пара файлов .log и .trs создается по завершении соответствующего теста. Набор log-файлов указывается в доступной лишь для чтения переменной TEST_LOGS, которая по умолчанию совпадает с TESTS. Расширения исполняемых файлов, если они имеется (8.20. Поддержка расширений исполняемых файлов), а также любые суффиксы, указанные в TEST_EXTENSIONS, удаляются и добавляется суффикс .log. Результат становится неопределенным, если имя файла завершается объединением нескольких суффиксов. По умолчанию TEST_EXTENSIONS имеет значение .test, которое может переопределить пользователь. В этом случае расширение должно начинаться с точки, за которой следует буква и может следовать любое число букв и цифр. Например, расширения .sh, .T и .t1 являются корректными, а .x-y, .6c и .t.1 недопустимы. Важно отметить, что имеющиеся ограничения (которые вряд ли будут отменены) ведут к тому, что подстановки configure в определение TESTS могут работать лишь при преобразовании в список тестов, имеющих суффиксы из переменной TEST_EXTENSIONS.

Для тестов, соответствующих расширению .EXT, указанному в TEST_EXTENSIONS, можно предоставить пользовательский «исполнитель тестов», используя переменную EXT_LOG_COMPILER (расширение указывается заглавными буквами), передавая опции в переменной AM_EXT_LOG_FLAGS и разрешая пользователю передавать опции в EXT_LOG_FLAGS. В результате все тесты с указанным расширением будут вызваны указанным «исполнителем». Для тестов без зарегистрированного расширения можно применять переменные LOG_COMPILER, AM_LOG_FLAGS и LOG_FLAGS. Например,

    TESTS = foo.pl bar.py baz 
    TEST_EXTENSIONS = .pl .py 
    PL_LOG_COMPILER = $(PERL) 
    AM_PL_LOG_FLAGS = -w 
    PY_LOG_COMPILER = $(PYTHON) 
    AM_PY_LOG_FLAGS = -v 
    LOG_COMPILER = ./wrapper-script 
    AM_LOG_FLAGS = -d

будет вызывать $(PERL) -w foo.pl, $(PYTHON) -v bar.py и ./wrapper-script -d baz для создания файлов foo.log, bar.log и baz.log, соответственно. Файлы foo.trs, bar.trs и baz.trs создаются автоматически, как побочный результат.

Важно отметить, что в отличие от последовательных тестов, переменные AM_TESTS_ENVIRONMENT и TESTS_ENVIRONMENT не могут применяться для определения пользовательского «исполнителя» и взамен должны применяться переменные LOG_COMPILER и LOG_FLAGS (или их аналоги для расширений).

    ## This is WRONG! 
    AM_TESTS_ENVIRONMENT = PERL5LIB='$(srcdir)/lib' $(PERL) -Mstrict -w 

    ## Do this instead. 
    AM_TESTS_ENVIRONMENT = PERL5LIB='$(srcdir)/lib'; export PERL5LIB; 
    LOG_COMPILER = $(PERL) 
    AM_LOG_FLAGS = -Mstrict -w

По умолчанию запускаются все тесты набора, но имеется несколько способов ограничить номенклатуру тестов.

  • Переменная TESTS позволяет задать подмножество выполняемых тестов, как показано ниже.

             env TESTS="foo.test bar.test" make -e check

    Однако эта команда будет безусловно переписывать файл test-suite.log, уничтожая результаты предыдущих тестов, что может оказаться нежелательным для пакетов с продолжительным тестированием. К счастью эту проблему легко решить переопределением переменной TEST_SUITE_LOG по время теста. Например,

             env TEST_SUITE_LOG=partial.log TESTS="..." make -e check

    обеспечит запись результатов отдельных тестов в файл partial.log, не трогая test-suite.log.

  • Можно установить переменную TEST_LOGS. По умолчанию эта переменная определяется при работе make из значения TESTS, как описано выше. Можно задать, например

             set x subset*.log; shift 
             env TEST_LOGS="foo.log $*" make -e check

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

             env RECHECK_LOGS= make -e check
  • Можно повторить все тесты, которые закончились отказом или неожиданно прошли, введя команду make recheck из каталога тестов. Эта условная цель должным образом устанавливает RECHECK_LOGS перед запуском.

Для гарантии упорядоченного тестирования даже по команде make -jN, нужно указать зависимости между файлами .log как делается с обычными зависимостями make. Например зависимость foo-execute.test от foo-compile.test можно указать в виде

    TESTS = foo-compile.test foo-execute.test 
    foo-execute.log: foo-compile.log

Заданный порядок гарантирует результаты требуемых тестов, поэтому foo-execute.test будет запущен даже при отказе или пропуске foo-compile.test. Кроме того, следует отметить, что такое задание зависимостей в настоящее время работает лишь для тестов, имена которых имеют суффиксы, указанные в TEST_EXTENSIONS.

Тесты без указанных зависимостей могут выполняться одновременно по команде make -jN, поэтому следует убедиться в том, что они готовы к параллельному выполнению.

Комбинация отложенного выполнения и корректных зависимостей между тестами и их источниками может применяться для эффективного тестирования модулей в процессе разработки. Для дальнейшего ускорения цикла разработки и тестирования может быть полезно задать компилируемые программы в переменной EXTRA_PROGRAMS вместо check_PROGRAMS, поскольку первый вариант допускает чередование компиляции с выполнением тестов (однако EXTRA_PROGRAMS не очищается автоматически, см. 3.3. Схема именования).

Переменные TESTS и XFAIL_TESTS могут включать условные части, а также подстановки configure. В последнем случае имеются некоторые ограничения — подставленные имена тестов должны иметь непустой суффикс (например, .test), чтобы можно было применить одно из правил выведения, созданных automake. Для литеральных имен тестов automake может создавать правила на уровне цели, чтобы преодолеть это ограничение.

Отметим, что в настоящее время невозможно использовать $(srcdir)/ или $(top_srcdir)/ в переменной TESTS. Это техническое ограничение нужно для предотвращения генерации журналов тестирования в дереве источников и его следствием является невозможность задать распределенные тесты с самогенерацией на основе явных правил переносимым между реализациями make способом (конфликт с семантикой FreeBSD и OpenBSD). При сомнениях можно воспользоваться GNU make или обойти эту проблему с помощью правил выведения при генерации тестов.

15.3. Драйверы тестов

15.3.1. Обзор поддержки сторонних драйверов тестов

В Automake, начиная с версии 1.12, параллельные тесты позволяют авторам применять сторонние драйверы тестов, если стандартные драйверы не подходят или не поддерживают нужный протокол тестирования.

Ожидается, что сторонний драйвер будет корректно выполнять переданную уму тестовую программу (включая переданные для нее аргументы, если они имеются) для анализа ее работы и результата, создавать файлы .log и .trs, связанные с запуском теста, и выводить результат теста на консоль. Ответственность за выполнение и корректность перечисленных функций ложится на автора драйвера, Automake здесь ничем не поможет. Тем не менее, код Automake для создания отчетов поддерживает драйверы тестов с множеством результатов одного сценария, если эти результаты регистрируются корректно (15.3.3.2. Создание журнала и запись результатов).

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

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

  • список тестовых сценариев в TESTS с возможностью переопределения в TESTS или TEST_LOGS;

  • одновременное выполнение тестов по команде make -j;

  • поддержка файлов .log и .trs для каждого файла и создание на их основе отчета о тесте (.log);

  • перепроверка целей (RECHECK_LOGS) и отложенный повтор тестов;

  • зависимости между тестами;

  • поддержка переменных check_* (check_PROGRAMS, check_LIBRARIES, …);

  • использование переменной среды VERBOSE для расширенного вывода информации об отказах тестов;

  • задание и исполнение TESTS_ENVIRONMENT, AM_TESTS_ENVIRONMENT, AM_TESTS_FD_REDIRECT;

  • задание базовых и связанных с расширениями переменных LOG_COMPILER и LOG_FLAGS.

Точная семантика цветового вывода, XFAIL_TESTS и серьезных ошибок определяется драйверами тестов.

15.3.2. Объявление сторонних драйверов тестов

Сторонние драйверы объявляются в переменных make LOG_DRIVER или EXT_LOG_DRIVER (расширение EXT должно быть TEST_EXTENSIONS), которые должны быть заданы для программ и сценариев, использующих драйверы для запуска тестов, записи результатов и создания отчетов о тестах с указанным расширением (или без расширения в случае LOG_DRIVER). В одном файле Makefile.am можно объявить разные драйверы тестов. Отметим также, что переменные LOG_DRIVER не заменяют LOG_COMPILER и два набора тестов могут существовать совместно.

Переменные AM_LOG_DRIVER_FLAGS (для разработчиков) и LOG_DRIVER_FLAGS (для пользователей) могут служить для задания флагов, передаваемых при каждом вызове LOG_DRIVER. При этом пользовательские флаги будут иметь более высокий приоритет. Аналогично, для каждого расширения EXT из TEST_EXTENSIONS флаги, указанные в AM_EXT_LOG_DRIVER_FLAGS и EXT_LOG_DRIVER_FLAGS, передаются при вызовах EXT_LOG_DRIVER.

15.3.3. API для сторонних драйверов

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

15.3.3.1. Аргументы команд для сторонних драйверов

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

—test-name=NAME

Имя теста с префиксом VPATH (при наличии) удаляется. Имя может включать суффикс и каталог (например, sub/foo.test) и предназначено в основном для вывода отчета на консоль (15.3.3.3. Вывод в процессе тестирования).

—log-file=PATH.log

Файл .log, который драйвер должен создавать (15.2.3. Параллельные тесты). При включении каталога (например, sub/foo.log) набор тестов будет обеспечивать наличие такого каталога до вызова драйвера теста.

—trs-file=PATH.trs

Файл .trs, который драйвер должен создавать (15.2.3. Параллельные тесты). При включении каталога (например, sub/foo.log) набор тестов будет обеспечивать наличие такого каталога до вызова драйвера теста.

—color-tests={yes|no}

Управляет цветовым представлением консольного вывода (15.2.1. Тесты на основе сценариев).

—expect-failure={yes|no}

Указывает, ожидается ли отказ тестируемой программы.

—enable-hard-errors={yes|no}

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

Явное завершение списка опций.

Первый аргумент, не являющийся опцией, переданный драйверу теста, указывает программу для запуска, а последующие задают опции и аргументы, передаваемые этой программе.

Отметим, что семантика опций —color-tests, —expect-failure и —enable-hard-errors определяется драйвером. Тем не менее, следует обеспечивать поведение, похожее или совместимое с поведением принятого по умолчанию драйвера.

15.3.3.2. Создание журнала и запись результатов

Драйвер должен корректно генерировать файлы, заданные опциями —log-file и —trs-file (даже при отказе или аварийном завершении программы). Файлы .log в идеале должны включать весь вывод тестируемой программы, а также могут содержать дополнительную информацию, которая может помочь в отладке или отчете об ошибках. В остальном формат файла остается свободным. Файлы .trs служат для регистрации некоторых метаданных с помощью полей reStructuredText. Предполагается использование этих метаданных разными способами в параллельных тестах, например, для подсчета результатов и вывода отчета о тесте при выполнении команды make recheck. Нераспознанные метаданные в .trs сейчас игнорируются, но это может измениться в будущем. Список метаданных приведен ниже.

:test-result:

Драйвер должен использовать это поле для регистрации результатов каждого контрольного примера, запускаемого сценарием. В одном файле .trs может присутствовать несколько полей :test-result:. Это позволяет протоколам тестирования включать в один сценарий множество тестов. Распознаваемыми результатами тестов являются PASS, XFAIL, SKIP, FAIL, XPASS и ERROR. За этими результатами при указании с :test-result: может следовать текст с именем и/или кратким описанием соответствующего теста. Этот текст игнорируется при создании файла test-suite.log и генерации отчета о тесте.

:recheck:

Если это поле присутствует и содержит значение no, соответствующий тест не будет выполняться по команде make recheck. Наличие нескольких полей :recheck: в файле .trs может сделать поведение неопределенным.

:copy-in-global-log:

Если это поле присутствует и содержит значение no, содержимое файла .log не будет копироваться в test-suite.log.  Возможность отказа от копирования обусловлена тем, что несмотря на полезность этих данных при отладке и анализе ошибок, это может приводить к пустому расходу места в обычной ситуации (например, при успешном тестировании). Наличие нескольких полей :copy-in-global-log: в одном файле .trs может вести к неопределенности.

:test-global-result:

Служит для объявления «глобального» результата набора тестов. Сейчас значение поля применяется лишь для отчета в журнале $(TEST_SUITE_LOG) и разрешает достаточно свободную форму. Например, сценарий с 10 тестами, из которых 6 прошли успешно, а 4 были пропущены, может использовать в этом поле значения PASS/SKIP, а сценарий с 19 тестами и 1 отказом может указать значение ALMOST PASSED. Наличие нескольких полей :test-global-result: в одном файле .trs может вести к неопределенному поведению.

Предположим, например, что файл .trs содержит приведенные ниже строки.

    :test-result: PASS server starts 
    :global-log-copy: no 
    :test-result: PASS HTTP/1.1 request 
    :test-result: FAIL HTTP/1.0 request 
    :recheck: yes 
    :test-result: SKIP HTTPS request (TLS library wasn't available) 
    :test-result: PASS server stops

Соответствующий сценарий будет повторен командой make check, добавит 5 результатов в сводку (3 успешных теста, 1 отказ и 1 пропуск), а содержимое соответствующего файла .log не будет копироваться в test-suite.log.

15.3.3.3. Вывод в процессе тестирования

Сторонние драйверы также заинтересованы в выводе на stdout информации информации о результатах теста, когда она становится доступной. В зависимости от применяемого протокола могут также выводиться причины отказов и пропусков, а также полезные данные диагностики (следует помнить, что строк на экране не так много и избыточный вывод будет захламлять экран). Точный формат вывода остается за драйвером и тот может даже не выводить ничего, оставляя все тестовому сценарию (неразумно и упомянуто лишь для разъяснения предоставляемой гибкости).

Отметим, что согласованность вывода важна и к ней следует стремиться, не отходя далеко от вывода встроенных драйверов Automake. В частности, вывод тестового набора следует окрашивать, передавая драйверу опцию —color-tests. С другой стороны, при использовании широко распространенного протокола тестирования разумно обеспечивать согласованный с этим протоколом вывод.

15.4. Использование протокола TAP

15.4.1. Введение в TAP

TAP (Test Anything Protocol) обеспечивает простой текстовый интерфейс между тестирующими модулями или программами и набором тестов. Тесты (процедуры TAP) записывают результат в простом формате на устройство stdout, а сценарий теста (потребитель TAP) анализирует и интерпретирует эти результаты, представляет их пользователю и/или регистрирует для дальнейшего анализа. Детали могут зависеть от тестового набора. Тесты Automake выводят результаты на консоль (15.2.1. Тесты на основе сценариев) и используют файлы .trs (15.2.3. Параллельные тесты) для хранения результатов и связанных метаданных. Кроме того, тесты пытаются обеспечить совместимость с имеющимися и распространенными утилитами (например, prove) хотя бы в простых случаях.

Протокол TAP начинался как тестовый набор для Perl, но сегодня стандартизован (в основном) и имеет независимые реализации в разных языках, включая C, C++, Perl, Python, PHP, Java. Полуофициальная спецификация протокола TAP доступна в документации Test::Harness::TAP. Очевидно, что наиболее подходящие примеры использования TAP следует искать в тестах perl и модулей perl. Есть и сторонние пакеты, применяющие тесты TAP, например, git.

15.4.2. Использование TAP с тестами Automake

Драйвер TAP из комплекта Automake требует от разработчика некоторых действий (возможно этого не потребуется в будущих версиях). Нужно вручную извлечь сценарий tap-driver.sh из дистрибутива Automake и скопировать его в дерево кода, а также использовать в Automake поддержку сторонних драйверов, чтобы тесты использовали сценарий tap-driver.sh и программу awk, найденную макросом AM_INIT_AUTOMAKE для запуска созданных TAP тестов. Помимо параметров, общих для всех драйверов тестов Automake (15.3.3.1. Аргументы команд для сторонних драйверов), tap-driver.sh поддерживает перечисленные ниже опции, имена которых выбраны с учетом совместимости с prove.

—ignore-exit

Заставляет драйвер теста игнорировать статус завершения тестовых сценариев. По умолчанию драйвер будет сообщать об ошибке при возврате ненулевого кода. Опция влияет также на обработку кода, связанного с прерыванием теста по сигналу.

—comments

Задает драйверу вывод диагностики TAP (строки, начинающиеся с #) в процессе работы теста. По умолчанию диагностика TAP копируется только в файл .log.

—no-comments

Отменяет опцию —comments.

—merge

Указывает драйверу объединять вывод stderr и stdout. Это нужно для отображения диагностики теста в корректном порядке относительно результатов, особенно при отладке, когда тестовый shell-сценарий запускается с трассировкой. Однако опция может приводить к путанице в тесте, если вывод stderr похож на результат теста.

—no-merge

Отменяет опцию —merge.

—diagnostic-string=STRING

Меняет строку указания диагностики TAP с принятого по умолчанию # на STRING. Это может быть полезно в основанных на TAP сценариях тестов с ограниченным контролем и подробным выводом (например, в результате получения вывода от вовлеченных в тест других инструментов), который может ошибочно считаться диагностикой TAP. Проблему можно решить путем переопределения строки, указывающей диагностику TAP и невозможной в выводе теста. Следует отметить отсутствие функции в «официальном» протоколе TAP.

Ниже приведен пример настройки и использования драйвера TAP.

    % cat configure.ac 
    AC_INIT([GNU Try Tap], [1.0], [bug-automake@gnu.org]) 
    AC_CONFIG_AUX_DIR([build-aux]) 
    AM_INIT_AUTOMAKE([foreign -Wall -Werror]) 
    AC_CONFIG_FILES([Makefile]) 
    AC_REQUIRE_AUX_FILE([tap-driver.sh]) 
    AC_OUTPUT 

    % cat Makefile.am 
    TEST_LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \ 
                      $(top_srcdir)/build-aux/tap-driver.sh 
    TESTS = foo.test bar.test baz.test 
    EXTRA_DIST = $(TESTS) 

    % cat foo.test 
    #!/bin/sh 
    echo 1..4 # Number of tests to be executed. 
    echo 'ok 1 - Swallows fly' 
    echo 'not ok 2 - Caterpillars fly # TODO metamorphosis in progress' 
    echo 'ok 3 - Pigs fly # SKIP not enough acid' 
    echo '# I just love word plays ...' 
    echo 'ok 4 - Flies fly too :-)' 

    % cat bar.test 
    #!/bin/sh 
    echo 1..3 
    echo 'not ok 1 - Bummer, this test has failed.' 
    echo 'ok 2 - This passed though.' 
    echo 'Bail out! Ennui kicking in, sorry...' 
    echo 'ok 3 - This will not be seen.' 

    % cat baz.test 
    #!/bin/sh 
    echo 1..1 
    echo ok 1 
    # Exit with error, even if all the tests have been successful. 
    exit 7 

    % cp PREFIX/share/automake-APIVERSION/tap-driver.sh . 
    % autoreconf -vi && ./configure && make check
    ... 
    PASS: foo.test 1 - Swallows fly 
    XFAIL: foo.test 2 - Caterpillars fly # TODO metamorphosis in progress 
    SKIP: foo.test 3 - Pigs fly # SKIP not enough acid 
    PASS: foo.test 4 - Flies fly too :-) 
    FAIL: bar.test 1 - Bummer, this test has failed. 
    PASS: bar.test 2 - This passed though. 
    ERROR: bar.test - Bail out! Ennui kicking in, sorry... 
    PASS: baz.test 1 
    ERROR: baz.test - exited with status 7 
    ... 
    Please report to bug-automake@gnu.org 
    ... 
    % echo exit status: $? 
    exit status: 1 

    % env TEST_LOG_DRIVER_FLAGS='--comments --ignore-exit' \ 
          TESTS='foo.test baz.test' make -e check 
    ... 
    PASS: foo.test 1 - Swallows fly 
    XFAIL: foo.test 2 - Caterpillars fly # TODO metamorphosis in progress 
    SKIP: foo.test 3 - Pigs fly # SKIP not enough acid 
    # foo.test: I just love word plays... 
    PASS: foo.test 4 - Flies fly too :-) 
    PASS: baz.test 1 
    ... 
    % echo exit status: $? 
    exit status: 0

15.4.3. Несовместимости с другими анализаторами и драйверами TAP

Драйвер TAP и набор тестов в составе Automake имеют некоторые несовместимости, о которых следует знать.

  • Директива Bail out! не останавливает весь набор тестов, прерывая лишь работающий сценарий. Это не соответствует спецификации TAP, но обеспечивает совместимость (и общий код) с концепцией серьезных ошибок в принятом по умолчанию драйвере тестов.

  • Не поддерживаются директивы version и pragma.

  • Опция —diagnostic-string позволяет менять строку маркировки диагностики TAP (по умолчанию #). Стандартный протокол TAP не разрешает это, поэтому при использовании этой опции диагностика может быть потеряна для других инструментов, таких как prove и Test::Harness.

  • Возможны и другие, пока не замеченные несовместимости.

15.4.4. Ссылки на информацию о протоколе TAP

  • Test::Harness::TAP — официальная (в основном документация для формата и протокола TAP.

  • proveнаиболее известный консольный драйвер тестов TAP из состава perl и Test::Harness.

  • TAP wiki.

  • Test::Tutorial — введение в тестирование для программистов perl.

  • Стандартные библиотеки perl Test::Simple и Test::More на основе TAP.

  • C TAP Harnessпроект на основе C с реализацией TAP producer и TAP consumer.

  • tap4jпроект на основе Java с реализацией TAP producer и TAP consumer.

15.5. Тесты DejaGnu

Если dejagnu присутствует в AUTOMAKE_OPTIONS, предполагается основанный на dejagnu набор тестов. Переменная DEJATOOL содержит список имен, передаваемых по одному как аргумент —tool при вызовах runtest. По умолчанию это имена пакетов. В переменной RUNTESTDEFAULTFLAGS задаются флаги —tool и —srcdir, передаваемые dejagnu по умолчанию (их можно переопределить). Переменные EXPECT и RUNTEST также можно переопределить для задания специфических значений проекта. Например, это может потребоваться при тестировании инструментов компиляции, поскольку принятые по умолчанию значения не учитывают имен host и target.

Содержимое пользовательской переменной (3.6. Пользовательские переменные) RUNTESTFLAGS передается вызову runtest. Если флаги runtest нужно установить в Makefile.am, следует использовать AM_RUNTESTFLAGS.

Automake будет генерировать правила для создания локального файла site.exp с различными переменными, найденными сценарием configure. Файл автоматически считывается программой DejaGnu. Пользователь пакета может редактировать файл для настройки набора тестов. Однако это не то место, где автору пакета следует задавать новые переменные, они должны определяться в реальном коде набора тестов, а файл site.exp не следует распространять.

Тем не менее, при наличии у автора пакета оснований обработать файл site.exp в процессе работы make, это можно сделать через переменную EXTRA_DEJAGNU_SITE_CONFIG, файлы из которой будут считаться предварительными условиями для site.exp, а их содержимое будет добавлено в конец этого файла (в порядке указания в переменной EXTRA_DEJAGNU_SITE_CONFIG). Отметим, что эти файлы не распространяются по умолчанию.

15.6. Тесты установки

Цель installcheck доступна пользователю как способ запуска любых тестов после установки пакета. Тесты можно добавить, написав правило installcheck-local.

16. Повторная сборка Makefile

Automake генерирует правила автоматической пересборки Makefile, configure и других производных файлов, подобных Makefile.in. При использовании AM_MAINTAINER_MODE в файле configure.ac эти правила автоматической пересборки включаются только в режиме сопровождающего (maintainer).

Иногда удобно добавлять в правила пересборки для configure или config.status дополнительные зависимости с помощью переменных CONFIGURE_DEPENDENCIES и CONFIG_STATUS_DEPENDENCIES. Переменные следует указывать во всех Makefile дерева (эти два правила пересборки выводятся во все эти файлы), поэтому проще и безопасней использовать макрос AC_SUBST из configure.ac. Например, приведенное ниже выражение обеспечит повторный запуск configure при каждом изменении version.sh.

    AC_SUBST([CONFIG_STATUS_DEPENDENCIES], ['$(top_srcdir)/version.sh'])

Отметим $(top_srcdir)/ в имени. Переменная применяется во всех файлах Makefile, поэтому ее значение должно иметь смысл на любом уровне иерархии сборки.

Не следует путать переменные CONFIGURE_DEPENDENCIES и CONFIG_STATUS_DEPENDENCIES. Первая добавляет зависимости в правило configure, результатом чего является запуск autoconf. Это переменную не следует применять часто, поскольку automake отслеживает файлы m4_include. Однако она может быть полезна при работе с m4_esyscmd и аналогичными макросами, дающими побочные эффекты. Следует помнить, что взаимодействия с кэш-переменной autom4te достаточно сложны и могут приводить к поломкам. Можно отключить кэш при работе с CONFIGURE_DEPENDENCIES. Переменная CONFIG_STATUS_DEPENDENCIES добавляет зависимости в правило config.status, что ведет к запуску configure. Поэтому в данную переменную следует включать любой источник, чтение которого может быть побочным результатом запуска configure (например, version.sh из приведенного примера).

Сценарии version.sh сегодня использовать не рекомендуется. Они применяются в основном при автоматическом обновлении версии пакета сценарием. Ниже приведен пример файла configure.ac в «старом стиле».

    AC_INIT 
    . $srcdir/version.sh 
    AM_INIT_AUTOMAKE([name], $VERSION_NUMBER) 
    ...

Здесь version.sh является фрагментом оболочки, устанавливающим VERSION_NUMBER. Проблема этого примера заключается в том, что automake не может отследить зависимости (указанные version.sh в переменной CONFIG_STATUS_DEPENDENCIES, и распространение файла зависит от пользователя) и используется устаревшая форма AC_INIT и AM_INIT_AUTOMAKE. Переход к новому синтаксису не прост, поскольку переменные оболочки не разрешены в аргументах AC_INIT. Рекомендуется заменить version.sh файлом M4, включаемым в configure.ac

    m4_include([version.m4]) 
    AC_INIT([name], VERSION_NUMBER) 
    AM_INIT_AUTOMAKE 
    ...

Файл version.m4 может содержать что-то вроде m4_define([VERSION_NUMBER], [1.2]). В результате automake позаботится о зависимостях при задании правила пересборки и автоматически распространит файл. Однако autoconf будет перезапускаться при каждом роста номера версии, тогда как в старом вариант нужно повторить лишь configure.

17. Смена поведения Automake

17.1 Опции

Свойства Automake можно задавать с помощью опций. За исключением указанных случаев, опции могут задаваться одним из нескольких способов. Большинство опций применяется на уровне файла Makefile при указании в специальной переменной AUTOMAKE_OPTIONS в таких файлах. Некоторые из таких опций имеют смысл лишь в файле Makefile.am верхнего уровня. Опции применяются глобально ко всем обрабатываемым Makefile, указанным в первом аргументе AM_INIT_AUTOMAKE в файле configure.ac, а некоторые опции, которые требуют менять сценарий configure, могут быть указаны только здесь. Обычно опции, заданные в AUTOMAKE_OPTIONS имеют преимущество перед заданными в AM_INIT_AUTOMAKE, которые имеют преимущество перед опциями командной строки.

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

    AUTOMAKE_OPTIONS = -Wportability foreign

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

    AM_INIT_AUTOMAKE([-Wportability])

а в Makefile.am

    AUTOMAKE_OPTIONS = foreign

предупреждения portability будут отключены файлом Makefile.am.

17.2. Список опций Automake

gnits

gnu

foreign

Задает уровень строгости. Опция gnits предполагает readme-alpha и check-news.

check-news

Ведет к отказу make dist, если текущий номер версии не задан в нескольких первых строках файла NEWS.

dejagnu

Задает генерацию правил, связанных с dejagnu (15.5. Тесты DejaGnu).

dist-bzip2

Переводить dist-bzip2 в dist.

dist-lzip

Переводить dist-lzip в dist.

dist-xz

Переводить dist-xz в dist.

dist-zip

Переводить dist-zip в dist.

dist-shar

Переводить dist-shar в dist. Опция устарела, поскольку формат shar считается устаревшим и проблематичным. Поддержка опции будет исключена в Automake 2.0.

dist-tarZ

Переводить dist-tarZ в dist. Опция устарела, поскольку формат compress, поскольку формат shar считается устаревшим. Поддержка опции будет исключена в Automake 2.0.

filename-length-max=99

Прерывать работу, если обнаружены имена длиннее 99 символов в процессе выполнения make dist. Такие имена файлов в общем случае считаются не переносимыми в архивах. (см. опции tar-v7 и tar-ustar). Опцию следует применять в Makefile.am верхнего уровня или как аргумент AM_INIT_AUTOMAKE в configure.ac, в иных случаях она игнорируется, равно как игнорируется во вложенных пакетах (7.4. Вложенные пакеты).

info-in-builddir

Указывает Automake размещать созданные файлы .info в builddir, а не srcdir. Это может сделать сборки VPATH с некоторыми не-GNU реализациями make нестабильными.

no-define

Опция имеет смысл лишь при передаче в качестве аргумента AM_INIT_AUTOMAKE и предотвращает включение переменных PACKAGE и VERSION в AC_DEFINE. Отметим, что переменные останутся в переменных оболочки, заданных configure и переменных make в Makefile. Это сделано для совместимости с прежними версиями.

no-dependencies

Похожа на опцию командной строки —ignore-deps, но полезна в случаях отсутствия необходимых битов для автоматического отслеживания зависимостей (8.19. Автоматическое отслеживание зависимостей). В этом случае эффект заключается в отключении отслеживания зависимостей.

no-dist

Отключает создание кода для цели dist и полезна при использовании пакетом своих методов распространения.

no-dist-gzip

Не переводить dist-gzip в dist.

no-exeext

Если Makefile.am задает правило для цели foo, будет переопределено правило для foo$(EXEEXT). Это требуется в случае пустого значения EXEEXT и по умолчанию automake выдает ошибку, которую данная опция блокирует. Опция предназначена лишь для случаев, когда заранее известно, что пакет не будет переноситься в Windows или иную ОС, использующую расширения имен исполняемых файлов.

no-installinfo

Созданный файл Makefile.in не будет задавать сборку и установку страниц info по умолчанию. Цели info и install-info остаются доступными. Опция отключена при уровне строгости gnu и выше.

no-installman

Созданный файл Makefile.in не будет задавать сборку и установку страниц man по умолчанию. Цель install-man сохраняется. Опция отключена при уровне строгости gnu и выше.

nostdinc

Может применяться для запрета стандартных опций -I, которые обычно представляются Automake автоматически.

no-texinfo.tex

Не требовать texinfo.tex даже при наличии в каталоге файлов texinfo.

serial-tests

Разрешает старые последовательные тесты для переменной TESTS (15.2.2. Последовательные тесты (устарели)).

parallel-tests

Включает в переменную TESTS параллельные тесты (15.2.3. Параллельные тесты) и нужна лишь для совместимости со старыми версиями, поскольку эти тесты включены по умолчанию.

readme-alpha

Если выпуск является предварительным (alpha) и есть файл README-alpha, он добавляется в дистрибутив. При наличии опции предполагается, что номера опций имеют одну из двух форм — MAJOR.MINOR.ALPHA, где все элементы являются цифрами (последняя точка и число опускаются в финальном выпуске) или MAJOR.MINORALPHA, где ALPHA — символ, опускаемый в финальном выпуске.

std-options

Задает правилу installcheck проверять поддержку установленными сценариями и программами опций —help и —version, а также обеспечивает базовую проверку зависимостей при работе после установки. В некоторых случаях программы (сценарии) следует исключать из проверки. Например, false (из GNU coreutils) никогда не возвращает успех даже с опцией —help или —version. Такие программы можно указать в переменной AM_INSTALLCHECK_STD_OPTIONS_EXEMPT. Указанные программы (не сценарии) должны иметь расширение $(EXEEXT) для Windows и OS/2. Например, при сборке false как программы и true.sh как сценария (оба не поддерживают опции —help и —version) можно задать

         AUTOMAKE_OPTIONS = std-options 
         bin_PROGRAMS = false ... 
         bin_SCRIPTS = true.sh ... 
         AM_INSTALLCHECK_STD_OPTIONS_EXEMPT = false$(EXEEXT) true.sh

subdir-objects

Задает размещение объектов в подкаталоге каталога сборки, соответствующем подкаталогу исходного файла. Например, для файла subdir/file.cxx объектным файлом будет subdir/file.o.

tar-v7

tar-ustar

tar-pax

Эти взаимоисключающие опции задают формат архива для make dist (архив tar сжимается в зависимости от опций no-dist-gzip, dist-bzip2, dist-lzip, dist-xz, dist-tarZ). Опции должны передаваться как аргументы AM_INIT_AUTOMAKE (6.4. Макросы Autoconf из пакета Automake), поскольку они могут требовать дополнительных проверок конфигурации. Automake будет сообщать о таких опциях в переменной AUTOMAKE_OPTIONS.

Опция tar-v7 выбирает старый формат tar V7 и принята по умолчанию. Этот формат понимают все реализации tar и для него поддерживаются имена длиной до 99 символов. Более длинные имена могут вызывать проблемы в некоторых реализациях tar, а другие могут создавать архивы с ошибками или использовать непереносимые расширения. Кроме того, формат V7 не позволяет сохранять пустые каталоги. При использовании этого формата следует задавать опцию filename-length-max=99 для перехвата длинных имен.

Опция tar-ustar задает формат ustar, определенный в POSIX 1003.1-1988. Формат достаточно стар и малопереносим. В 2018 г. он поддерживался командой tar в GNU, FreeBSD, NetBSD, OpenBSD, AIX, HP-UX, Solaris. Формат поддерживает пустые каталоги и может сохранять файлы с именами размером до 256 символов при условии, что имя можно по границе каталога разделить на две части, первая из которых не больше 155 байтов. Поэтому во многих случаях максимальный размер имен будет меньше 256 символов.

Опция tar-pax задает новый формат pax, определенный в POSIX 1003.1-2001 и не ограничивающий размер имен. Однако этот формат новый и его применение следует ограничивать лишь современными платформами. В 2018 г. формат поддерживался командой tar в GNU, FreeBSD, OpenBSD и не работал в NetBSD, AIX, HP-UX, Solaris.

Сценарий configure знает несколько способов создания указанных форматов. Сценарий не будет прерван при отсутствии архиватора (пакет можно собрать), но задача make dist завершится отказом.

VERSION

Позволяет задать номер версии (например, 0.30). Если Automake не новее указанной версии, создание файлов Makefile.in будет заблокировано.

-WCATEGORY

—warnings=CATEGORY

Эти опции ведут себя как их варианты для командной строки (5. Создание Makefile.in) и позволяют контролировать вывод предупреждений на уровне файлов. Можно также настроить некоторые предупреждения для проекта в целом, например, задав AM_INIT_AUTOMAKE([-Wall]) в файле configure.ac.

Нераспознанные опции указываются программой automake. Если нужно применить опцию во всем дереве, можно задать макрос AM_INIT_AUTOMAKE в файле (6.4. Макросы Autoconf из пакета Automake).

18. Прочие правила

18.1. Взаимодействие с etags

В некоторых случаях Automake создает правила генерации файлов TAGS для использования с GNU Emacs. При наличии исходного кода или заголовков C, C++ или Fortran 77 для каталога создаются правила tags и TAGS с использованием всех файлов из первичных переменных _SOURCES, _HEADERS и _LISP. Отметим, что создаваемые исходные файлы, которые не нужно распространять, должны быть указаны в переменных вида nodist_noinst_HEADERS или nodist_PROG_SOURCES, иначе они игнорируются.

Правило tags будет выводиться в верхний каталог дерева и при запуске из этого каталога команда make tags будет создавать файл TAGS, включающий ссылки на все файлы TAGS в подкаталогах. Правило tags создается также при задании переменной ETAGS_ARGS, которая предназначена для использования в каталогах, содержащих помечаемые исходные файлы, которые etags не понимает. Пользователь может задать ETAGSFLAGS для передачи etags дополнительных флагов. Доступен также макрос AM_ETAGSFLAGS для файлов Makefile.am.

Ниже показано, как Automake генерирует теги для источников и узлов в файле Texinfo.

    ETAGS_ARGS = automake.in --lang=none \ 
     --regex='/^@node[ \t]+\([^,]+\)/\1/' automake.texi

При добавлении имен файлов в ETAGS_ARGS может возникнуть желание задать TAGS_DEPENDENCIES. Содержимое этой переменной напрямую добавляется в зависимости для правила tags.

Automake также генерирует правило ctags, которое может служить для сборки файлов tags в стиле vi. Переменная CTAGS указывает имя вызываемой программы (по умолчанию ctags), а CTAGSFLAGS может применяться пользователем для задания дополнительных флагов. Доступен также макрос AM_CTAGSFLAGS в Makefile.am.

Automake создает правило ID, которое будет запускать для источника. Это поддерживается лишь на уровне каталогов.

Правило cscope создает список исходных файлов и запускает cscope для сборки базы данных с инвертированным индексом. Переменная CSCOPE задает имя вызываемой программы (по умолчанию cscope), а CSCOPEFLAGS и CSCOPE_ARGS могут служить для передачи дополнительных флагов и имен. Можно также использовать макрос AM_CSCOPEFLAGS в Makefile.am. Отметим, что поддержка cscope в Automake в настоящее время может не работать корректно при сборке VPATH с отличными от GNU реализациями make implementations.

Automake создает правила для поддержки программы GNU Global Tags program. Правило GTAGS запускает gtags и помещает результат в каталог сборки на верхний уровень. Переменная GTAGS_ARGS содержит аргументы gtags.

18.2. Обработка новых расширений имен файлов

Иногда полезно задать новое неявное правило для обработки неизвестных Automake типов файлов.

Предположим, что компилятор создает из файлов .foo объектные файлы .o. Можно задать правило для суффикса

    .foo.o: 
            foocc -c -o $@ $<

Тогда можно напрямую указывать файлы .foo в переменной _SOURCES и предполагать корректный результат.

    bin_PROGRAMS = doit 
    doit_SOURCES = doit.foo

Это был наиболее простой и распространенный случай. В других случаях нужно будет помочь Automake выяснить суффиксы для включения в правило. Обычно это связано с расширениями, не начинающимися с точки. Затем нужно лишь поместить список новых суффиксов в переменную SUFFIXES перед созданием неявного правила. Например, приведенное ниже определение предотвращает в Automake ошибочную интерпретацию правила .idlC.cpp: как попытки преобразовать файлы .idlC в .cpp.

    SUFFIXES = .idl C.cpp 
    .idlC.cpp: 
            # whatever

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

19. Директива include

Automake поддерживает директиву include для включения других фрагментов Makefile при работе automake. Фрагменты считываются и обрабатываются automake, а не make. Как и у условных выражениях, make не знает об include.

include $(srcdir)/file

Включить фрагмент, заданный относительно текущего каталога источников.

include $(top_srcdir)/file

Включить фрагмент, заданный относительно верхнего уровня каталога источников.

Отметим, что при включении фрагмента внутри условной конструкции условие относится ко всему фрагменту.

Включаемые таким способом фрагменты Makefile всегда распространяются, так как они нужны для сборки Makefile.in.

Внутри фрагмента конструкция %reldir% заменяется каталогом фрагмента относительно базы Makefile.am, а %canon_reldir% заменяется канонизированной (3.5. Именование производных переменных) формой %reldir%. Для удобства %D% служит синонимом %reldir%, а %C% — %canon_reldir%. Особенность состоит в том, что при размещении фрагмента в одном каталоге с базовым Makefile.am (%reldir% = .) переменные %reldir% и %canon_reldir% преобразуются в пустую строку, поглощая следующий символ / или _.

Фрагмент makefile может иметь вид

    bin_PROGRAMS += %reldir%/mumble 
    %canon_reldir%_mumble_SOURCES = %reldir%/one.c

20. Конструкции с условием

Automake поддерживает простые конструкции с условием, которые отличаются от условных конструкций GNU Make. В Automake условия проверяются во время работы сценария configure и влияют на трансляцию файлов Makefile.in в Makefile. Проверка основывается на опциях, переданных configure и результатах, полученным сценарием при изучении системы. Условия в GNU Make проверяются во время работы make и основаны на переменных, переданных программе make или заданных в Makefile. Условные конструкции Automake работают с любой программой make.

20.1. Использование условных конструкций

Перед использованием условия его нужно задать макросом AM_CONDITIONAL в configure.ac (6.4. Макросы Autoconf из пакета Automake). Макрос AM_CONDITIONAL (CONDITIONAL, CONDITION) принимает имя условия CONDITIONAL, которое должно начинаться с буквы и включать лишь буквы, цифры и символы подчеркивания, а также не может принимать значение TRUE или FALSE. Условие CONDITION (подходит для оператора оболочки if) проверяется в процессе работы configure. Важно отметить, что каждый макрос AM_CONDITIONAL должен вызываться при каждом запуске . Если макрос AM_CONDITIONAL вызывается по условию (например, в операторе if), результат может привести к путанице в automake. Условия обычно зависят от опций, предоставляемых пользователем сценарию configure. Ниже приведен пример условия, дающего в результате true, если пользователь указал опцию —enable-debug.

    AC_ARG_ENABLE([debug], 
    [  --enable-debug    Turn on debugging], 
    [case "${enableval}" in 
      yes) debug=true ;; 
      no)  debug=false ;; 
      *) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;; 
    esac],[debug=false]) 
    AM_CONDITIONAL([DEBUG], [test x$debug = xtrue])

Ниже приведен пример использования условия в файле Makefile.am.

    if DEBUG 
    DBG = debug 
    else 
    DBG = 
    endif 
    noinst_PROGRAMS = $(DBG)

Этот тривиальный пример можно обработать с помощью EXTRA_PROGRAMS (8.1.4. Условная компиляция программ).

В операторе if может проверяться лишь одна переменная и разрешено обращение с помощью знака !, а оператор else можно опускать. Условия допускают вложенность произвольной глубины. Можно задать аргумент для else, который в этом случае должен быть отрицанием условия в текущем операторе if. Аналогично можно задать условие, завершаемое строкой endif.

    if DEBUG 
    DBG = debug 
    else !DEBUG 
    DBG = 
    endif !DEBUG

Несбалансированные условия являются ошибками. Для операторов if, else и endif не следует использовать отступ.

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

Чтобы обеспечить доступ к условию, заданному AM_CONDITIONAL в configure.ac, и разрешить условие AC_CONFIG_FILES, можно применить AM_COND_IF. Макрос AM_COND_IF (CONDITIONAL, [IF-TRUE], [IF-FALSE]) при условии CONDITIONAL выполняет IF-TRUE, в ином случае — IF-FALSE. Если любая из ветвей содержит AC_CONFIG_FILES, это заставит automake вывести правила для соответствующих файлов только при данном условии.

Макросы AM_COND_IF могут быть вложенными при корректном использовании m4. Ниже приведен пример задания условного файла config.

    AM_CONDITIONAL([SHELL_WRAPPER], [test "x$with_wrapper" = xtrue]) 
    AM_COND_IF([SHELL_WRAPPER], 
               [AC_CONFIG_FILES([wrapper:wrapper.in])])

20.2. Ограничения условных конструкций

Условия должны содержать завершенные операторы, такие как определения переменных или правил. Automake не может работать с условиями внутри определений переменных и даже не может диагностировать такие ситуации

    # Automake не поймет такой синтаксис
    AM_CPPFLAGS = \ 
      -DFEATURE_A \ 
    if WANT_DEBUG 
      -DDEBUG \ 
    endif 
      -DFEATURE_B

Однако определение можно реализовать с помощью AM_CPPFLAGS

    if WANT_DEBUG 
      DEBUGFLAGS = -DDEBUG 
    endif 
    AM_CPPFLAGS = -DFEATURE_A $(DEBUGFLAGS) -DFEATURE_B

или

    AM_CPPFLAGS = -DFEATURE_A 
    if WANT_DEBUG 
    AM_CPPFLAGS += -DDEBUG 
    endif 
    AM_CPPFLAGS += -DFEATURE_B

Дополнительная информация и примеры приведены в параграфах 7.2. Условные подкаталоги, 8.1.3. Условная компиляция исходного кода, 8.1.4. Условная компиляция программ, 8.3.3. Условная сборка библиотек Libtool, 8.3.4. Библиотеки Libtool с условными источниками.

21. Молчаливая работа make

21.1. Подробный вывод по умолчанию

Обычно при выполнении правил, связанных с целью make выводит каждое правило перед его выполнением. Такое поведение существует достаточно долго и даже предписывается стандартом POSIX, но нарушает принцип UNIX «молчание — золото»

Когда программе нечего сказать интересного или удивительного, ей следует молчать. Хорошие программы Unix выполняют работу незаметно с минимальной суетой и беспокойством. Молчание — золото!

Хотя многословие make теоретически может быть полезным для отслеживания ошибок и причин непонятных отказов, оно также может скрывать предупреждения и сообщения об ошибках от привлекаемых make инструментов, топя их в потоке неинтересной и малополезной информации. Это может сильно раздражать, особенно разработчиков, которые обычно хорошо знают происходящее «за кулисами». Подробный вывод make для них представляет в основном шум, скрывающий потенциально важные предупреждения.

21.2. Как заставить make замолчать

Ниже описаны некоторые распространенные приемы обеспечения молчаливой работы make с их преимуществами и недостатками. В параграфе 21.3. Как Automake может «заглушить» make показано, как Automake может помочь в этом.

make -s

Эта опция просто отключает в make вывод правил перед их выполнением. Опция обязательна для POSIX, поддерживается всеми и назначение ее легко понять. Но у этого подхода есть ряд серьезных ограничений. Во-первых, он работает по принципу «все или ничего», что далеко не всегда приемлемо. Кроме того, при использовании флага -s вывод make может даже не позволять заметить ошибок или связанных с ними правил.

make >/dev/null || make

Этот вариант следует правилу «молчание — золото». Предупреждения от stderr проходят, а выходной отчет печатается лишь в случае ошибки и в этом случае ему следует быть достаточно подробным, чтобы определить причину и место ошибки. Однако два вызова make в одной строке могут скрывать ошибки (особенно чередующиеся) или менять ожидаемую семантику вызовов make, явно усложняя отладку и поиск ошибок.

make —no-print-directory

Этот метод относится к GNU make. Опция —no-print-directory отключает вывод рабочего каталога при вызове дочерних процессов make (сообщения о входе и выходе для каталогов), снижая объем информации. Однако опыт показывает, что это затрудняет отладку проектов с большой глубиной вложенности. Опция —no-print-directory автоматически активируется при задании флага -s.

21.3. Как Automake может «заглушить» make

Приведенные выше советы могут быть иной раз полезны, но у них имеются серьезные недостатки. Поэтому automake включает более развитый и гибкий способ снизить объем выводимой make информации (для большинства правил).

Сравним обычный вывод make и вывод со включенными «правилами тишины».

    % cat Makefile.am 
    bin_PROGRAMS = foo 
    foo_SOURCES = main.c func.c 
    % cat main.c 
    int main (void) { return func (); }  /* func used undeclared */ 
    % cat func.c 
    int func (void) { int i; return i; } /* i used uninitialized */

Вывод make обычно очень детален и это зачастую скрывает предупреждения компилятора.

    % make CFLAGS=-Wall 
    gcc -DPACKAGE_NAME=\"foo\" -DPACKAGE_TARNAME=\"foo\" ... 
    -DPACKAGE_STRING=\"foo\ 1.0\" -DPACKAGE_BUGREPORT=\"\" ... 
    -DPACKAGE=\"foo\" -DVERSION=\"1.0\" -I. -Wall -MT main.o 
    -MD -MP -MF .deps/main.Tpo -c -o main.o main.c 
    main.c: In function ‘main’: 
    main.c:3:3: warning: implicit declaration of function ‘func’ 
    mv -f .deps/main.Tpo .deps/main.Po 
    gcc -DPACKAGE_NAME=\"foo\" -DPACKAGE_TARNAME=\"foo\" ... 
    -DPACKAGE_STRING=\"foo\ 1.0\" -DPACKAGE_BUGREPORT=\"\" ... 
    -DPACKAGE=\"foo\" -DVERSION=\"1.0\" -I. -Wall -MT func.o 
    -MD -MP -MF .deps/func.Tpo -c -o func.o func.c 
    func.c: In function ‘func’: 
    func.c:4:3: warning: ‘i’ used uninitialized in this function 
    mv -f .deps/func.Tpo .deps/func.Po 
    gcc -Wall -o foo main.o func.o

Очистим сборку для повтора с «чистого листа».

    % make clean 
    test -z "foo" || rm -f foo 
    rm -f *.o

Правила молчанию включены и вывод стал меньше, но информативней и предупреждения компилятора видны.

    % make V=0 CFLAGS=-Wall 
      CC     main.o 
    main.c: In function ‘main’: 
    main.c:3:3: warning: implicit declaration of function ‘func’ 
      CC     func.o 
    func.c: In function ‘func’: 
    func.c:4:3: warning: ‘i’ used uninitialized in this function 
      CCLD   foo

В проектах с использованием libtool правила тишины могут автоматически включать опцию libtool —silent.

    % cat Makefile.am 
    lib_LTLIBRARIES = libx.la 

    % make # Both make and libtool are verbose by default. 
    ... 
    libtool: compile: gcc -DPACKAGE_NAME=\"foo\" ... -DLT_OBJDIR=\".libs/\" 
      -I. -g -O2 -MT libx.lo -MD -MP -MF .deps/libx.Tpo -c libx.c -fPIC 
      -DPIC -o .libs/libx.o 
    mv -f .deps/libx.Tpo .deps/libx.Plo 
    /bin/sh ./libtool --tag=CC --mode=link gcc -g -O2 -o libx.la -rpath 
      /usr/local/lib libx.lo 
    libtool: link: gcc -shared .libs/libx.o -Wl,-soname -Wl,libx.so.0 
      -o .libs/libx.so.0.0.0 
    libtool: link: cd .libs && rm -f libx.so && ln -s libx.so.0.0.0 libx.so 
    ... 

    % make V=0 
      CC     libx.lo 
      CCLD   libx.la

Для созданных Automake файлов Makefile пользователь может изменить уровень вывода при запуске configure и make:

  • configure —enable-silent-rules обеспечивает создание правил с менее подробным выводом, опция —disable-silent-rules задает обычный вывод;

  • при работе make заданный configure вывод можно переопределить, например, make V=1 задает подробный вывод, а make V=0 — менее детальный.

Отметим, что «правила тишины» по умолчанию выключены и пользователю нужно явно включить их в команде configure или make. Это считается хорошим тоном, поскольку позволяет пользователю возможность управления.

Разработчик, желающий снизить объем выводимой информации, может включить правила тишины по умолчанию, вызвав макрос AM_SILENT_RULES([yes]) из configure.ac. Если пользователь хочет включить правила тишины по умолчанию, он может указать в файле config.site переменную enable_silent_rules со значением yes. Это не помешает управления выводом в командах configure и make.

Для переносимости между разными реализациями make авторам пакетов не рекомендуется устанавливать переменную V в файле Makefile.am, чтобы позволить пользователю переопределять значения и для подкаталогов.

Для более эффективной работы в текущей реализации этой функции обычно применяется преобразование вложенной переменной $(VAR1$(V)), которое не требуется стандартом POSIX 2008, но широко применяется на практике. В редких реализациях make, не поддерживаемых преобразование вложенных переменных, правила молчания определяются при работе configure и не могут быть переопределены для make. В будущих версиях POSIX вероятно придется пересмотреть преобразование вложенных переменных, поэтому ограничение может со временем исчезнуть.

Для задания тишины в пользовательских правилах имеется два варианта, рассмотренных ниже.

  • Можно использовать предопределенную переменную AM_V_GEN как префикс команд, которым следует выводить строку состояния в режиме тишины и AM_V_at — для команд, которым не следует выводить ничего. При подробном выводе обе переменные преобразуются в пустые строки.

  • Можно заставить задание замолчать безоговорочно с помощью @ и затем применять переменную AM_V_P, чтобы узнать режим вывода make, настраивая уровень подробности вывода задания, как показано ниже.

             generate-headers: 
                             ... [команды, задающие переменную среды '$headers'] ...; \ 
                     if $(AM_V_P); then set -x; else echo " GEN   [headers]"; fi; \ 
                     rm -f $$headers && generate-header --flags $$headers
  • Можно добавить свои переменные для показа строк по вашему выбору. Приведенный ниже фрагмент определение эквивалента переменной AM_V_GEN’

         pkg_verbose = $(pkg_verbose_@AM_V@) 
         pkg_verbose_ = $(pkg_verbose_@AM_DEFAULT_V@)
         pkg_verbose_0 = @echo PKG-GEN $@; 

         foo: foo.in 
                 $(pkg_verbose)cp $(srcdir)/foo.in $@

В заключение отметим, что даже при включенных правилах тишины опция —no-print-directory нужна GNU make, если не нужно видеть сообщений о входе и выходе для каталогов.

22. Опции —gnu и —gnits

Опция —gnu (или gnu в переменной AUTOMAKE_OPTIONS) заставляет automake проверять указанные ниже условия.

  • Наличие файлов INSTALL, NEWS, README, AUTHORS и ChangeLog, а также одного из файлов COPYING.LIB, COPYING.LESSER или COPYING в каталоге верхнего уровня.

    Если задана опция —add-missing, automake будет добавлять базовый вариант файла INSTALL и файл COPYING с текстом текущей версии GNU General Public License на момент выпуска Automake (сейчас версия 3). Однако имеющийся COPYING никогда не переписывается программой automake.

  • Опции no-installman и no-installinfo запрещены.

В будущем опция —gnu может быть расширена дополнительными проверками (желательно ознакомиться с требованиями стандартов GNU). Кроме того, могут требоваться некоторые нестандартные программы для различных правил сопровождающих, например, в будущем может потребоваться pathchk для цели make dist.

Опция —gnits выполняет все проверки —gnu и ряд дополнительных.

  • Команда make installcheck проверяет реальный вывод командами —help и —version справочной информации и номера версии. Это опция std-options (17. Смена поведения Automake).

  • Команда make dist проверяет наличие файла NEWS и его обновления до текущей версии.

  • Проверяется переменная VERSION на предмет соответствия стандартам Gnits.

  • Если переменная VERSION указывает предварительный (alpha) выпуск и в каталоге верхнего уровня имеется файл README-alpha, этот файл включается в распространение. Это делается только в режиме —gnits, поскольку лишь в этом режиме формат версии ограничен, поэтому только в нем Automake может автоматически решить вопрос включения файла README-alpha.

  • Наличие файла THANKS.

23. Когда Automake не достаточно

В некоторых случаях Automake не может полностью решить задачу и приходиться писать правила вручную.

23.1. Расширение правил Automake

За небольшими исключениями (например, переменные _PROGRAMS, TESTS, XFAIL_TESTS, которые переписываются для добавления в $(EXEEXT)), содержимое файла Makefile.am дословно копируется в Makefile.in. Семантика копирования означает, что многие проблемы можно обойти, просто добавив некоторые переменные и правила make в файл Makefile.am. Automake игнорирует такие добавления. Поскольку Makefile.in создается на основе данных, собранных из разных мест (Makefile.am, configure.ac, automake), могут возникнуть конфликты между определениями правил или переменных. При сборке Makefile.in программа automake учитывает указанные ниже приоритеты оставляя за пользователем последнее слово.

  • Пользовательские переменные в Makefile.am имеют приоритет на AC_SUBST из configure.ac, а переменные AC_SUBST — над переменными, заданными automake.

  • Заданное пользователем правило переопределяет любой правило automake для той же цели.

Такая семантика позволяет точно настроить принятые по умолчанию установки Automake или заменить некоторые правила. Переопределение правил Automake зачастую нежелательно, особенно на верхнем уровне пакета с подкаталогами. Опция -Woverride (5. Создание Makefile.in) полезна для поиска переопределений вручную. Отметим, что Automake не различает правила с командами и правила лишь с зависимостями. Поэтому невозможно добавить зависимости для заданной automake цели без переопределения всего правила. Однако некоторые полезные цели имеют вариант -local, который можно задать в Makefile.am. Automake будет дополнять стандартную цель предоставленными пользователем целями. Локальные версии поддерживаются для целей all, info, dvi, ps, pdf, html, check, install-data, install-dvi, install-exec, install-html, install-info, install-pdf, install-ps, uninstall, installdirs, installcheck и разных целей clean (mostlyclean, clean, distclean, maintainer-clean). Цели uninstall-exec-local и uninstall-data-local отсутствуют, используется просто uninstall-local, поскольку нет смысла удалять лишь данные или исполняемые файлы. Ниже приведен пример удаления подкаталога по команде make clean (13. Очистка).

    clean-local: 
            -rm -rf testSubDir

Может возникнуть желание использовать install-data-local для установки файла в жестко заданное место, но этого следует избегать (27.10. Установка в жестко заданные места).

Для целей -local нет особой гарантии порядка выполнения, обычно они запускаются рано но при параллельной работе make это не гарантируется.

У некоторых правил есть способ запустить другое правило, называемый ловушкой (hook), при этом ловушки всегда выполняются после основного правила. Ловушки именуются по основной цели с суффиксом -hook и поддерживаются для целей install-data, install-exec, uninstall, dist и distcheck. Ниже приведен пример создания жесткой ссылки на установленную программу.

    install-exec-hook: 
            ln $(DESTDIR)$(bindir)/program$(EXEEXT) \ 
               $(DESTDIR)$(bindir)/proglink$(EXEEXT)

Хотя жесткие ссылки дешевле и переносимее символьных, они работают не всегда (например, в OS/2 не ln). В идеале следует вернуться к cp -p, когда ln не работает. Если приемлемы символьные ссылки, проще всего добавить AC_PROG_LN_S в configure.ac и использовать $(LN_S) в Makefile.am. Ниже показано, как можно установить версионную копию программы с использованием $(LN_S).

    install-exec-hook: 
            cd $(DESTDIR)$(bindir) && \ 
              mv -f prog$(EXEEXT) prog-$(VERSION)$(EXEEXT) && \ 
              $(LN_S) prog-$(VERSION)$(EXEEXT) prog$(EXEEXT)

Отметим переименование программы, в результате чего новая версия будет удалять символьную ссылку, а не реальный файл. Переход в целевой каталог (cd) служит для создания относительных ссылок. При создании install-exec-hook или install-data-hook следует учитывать, что различие исполняемых файлов и данных основано на каталогах, а не первичных переменных (2.2.7. Раздельная установка). Поэтому foo_SCRIPTS будет устанавливаться install-data, а barexec_SCRIPTS — install-exec.

23.2. Сторонние файлы Makefile

В большинстве проектов все Makefile создаются Automake, но иногда во вложенных каталогах нужны созданные вручную Makefile. Например, каталог может включать сторонний проект со своей системой сборки без Automake. Можно указать любые каталоги в переменной SUBDIRS или DIST_SUBDIRS для указания наличия в них Makefile, распознающих приведенные ниже рекурсивные цели. При запуске одной из таких целей будет выполняться рекурсия во все каталоги, поэтому сторонние Makefile должны поддерживать эти цели.

all

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

distdir

Копировать файлы для распространения в $(distdir) перед созданием архива. Цель не нужна при использовании опции no-dist (17. Смена поведения Automake). Переменные $(top_distdir) и $(distdir) (14.3. «Ловушка» dist) могут быть переданы из внешней программы в субпакет при вызове цели distdir. Эти переменные задаются для каталога, в который выполняется рекурсия, поэтому они готовы к использованию.

install
install-data
install-exec
uninstall

Установить или удалить установленные файлы (12. Установка).

install-dvi
install-html
install-info
install-ps
install-pdf

Установить документацию в указанном формате (11.1. Texinfo).

installdirs

Создать каталоги для установки, не устанавливая файлов.

check
installcheck

Проверка пакета (15. Поддержка тестов).

mostlyclean
clean
distclean
maintainer-clean

Правила очистки (13. Очистка).

dvi
pdf
ps
info
html

Сборка документации в разных форматах (11.1. Texinfo).

tags
ctags

Сборка TAGS и CTAGS (18.1. Взаимодействие с etags).

Проекты с Gettext являются хорошим примером использования сторонних Makefile с Automake. Makefile из gettextize помещает в каталоги po/ и intl/ рукописные Makefile, реализующие указанные выше цели. Таким образом они могут быть добавлены в SUBDIRS пакетов Automake. Каталоги, указанные в DIST_SUBDIRS, но не в SUBDIRS, должны иметь лишь правила distclean, maintainer-clean и distdir (7.2. Условные подкаталоги). Обычно многие из этих правил не имеют отношения к стороннему субпроекту, но нужны для работы пакета в целом. Наличие ничего не делающих правил является нормой в сторонних пакетах, поэтому при отсутствии документации или поддержки тегов в стороннем пакете можно просто дополнить его Makefile, как показано ниже.

    EMPTY_AUTOMAKE_TARGETS = dvi pdf ps info html tags ctags 
    .PHONY: $(EMPTY_AUTOMAKE_TARGETS) 
    $(EMPTY_AUTOMAKE_TARGETS):

Другим аспектом взаимодействия со сторонними системами сборки является поддержка сборки VPATH (2.2.6. Параллельная сборка (VPATH)). Обычно при отсутствии в субпакете поддержки VPATH пакет целиком не поддерживает сборки VPATH. Это означает, что make distcheck не будет работать, поскольку эта цель опирается на сборки VPATH. Это устраивает многие (не все пользователи Automake слышали о make distcheck), но другие предпочтут обновить имеющиеся Makefile для поддержки VPATH. Для этого не обязательно требуется Automake и достаточно Autoconf. Необходимые подстановки @srcdir@, @top_srcdir@ и @top_builddir@ определяются configure при обработке Makefile и они не задаются Makefile, подобно упомянутым выше $(distdir) и $(top_distdir).

Иногда неудобно редактировать сторонний Makefile для добавления указанных выше целей. Например, это может быть сделано для упрощения перехода к новым версиям стороннего проекта. Можно пойти иным путем. Если предполагается GNU make одним из вариантов является добавление в подкаталог GNUmakefile с требуемыми целями и включением Makefile. Чтобы это работало со сборкой VPATH, GNUmakefile должен размещаться в каталоге сборки. Проще всего это сделать путем создания файла GNUmakefile.in и его обработки макросом AC_CONFIG_FILES из внешнего пакета. Например, если Makefile определяет все цели, кроме документации, а цель check называется test, можно создать GNUmakefile (или GNUmakefile.in) вида

    # Сначала включается реальный Makefile 
    include Makefile 
    # Затем определяются цели, требуемые для файлов Automake Makefile
    .PHONY: dvi pdf ps info html check 
    dvi pdf ps info html: 
    check: test

Другая идея без использования include заключается в создании промежуточного (proxy) Makefile, диспетчеризующего правила в реальный Makefile с целью $(MAKE) -f Makefile.real $(AM_MAKEFLAGS) target (для переименования исходного Makefile) или командой cd subdir && $(MAKE) $(AM_MAKEFLAGS) target (для сохранения проекта на один каталог глубже). Промежуточный файл Makefile можно создать с помощью Automake. Для этого нужны лишь цели -local (23.1. Расширение правил Automake), выполняющие диспетчеризацию. Доступны и другие функции Automake, поэтому можно разрешить Automake распространение или установку. Ниже приведен возможный файл Makefile.am.

    all-local: 
            cd subdir && $(MAKE) $(AM_MAKEFLAGS) all 
    check-local: 
            cd subdir && $(MAKE) $(AM_MAKEFLAGS) test 
    clean-local:
            cd subdir && $(MAKE) $(AM_MAKEFLAGS) clean 

    # Предполагается, что пакет знает как установить себя
    install-data-local: 
            cd subdir && $(MAKE) $(AM_MAKEFLAGS) install-data 
    install-exec-local: 
            cd subdir && $(MAKE) $(AM_MAKEFLAGS) install-exec 
    uninstall-local: 
            cd subdir && $(MAKE) $(AM_MAKEFLAGS) uninstall 

    # Распространять файлы отсюда
    EXTRA_DIST = subdir/Makefile subdir/program.c ...

Доведя эту идею до крайности, можно игнорировать систему сборки субпроекта и собрать все из промежуточного файла Makefile.am. Это может быть разумно, если нудна сборка VPATH, а субпроект ее не поддерживает.

24. Распространение Makefile.in

Automake не задает ограничений на распространение файлов Makefile.in. Авторам пакетов рекомендуется распространять свои работы их на условиях, подобных лицензии GPL, но для этого не требуется использовать Automake. Некоторые из файлов, автоматически устанавливаемых при использовании опции —add-missing, попадают под действие GPL. Однако для них имеются специальные исключения, позволяющие распространять файлы с пакетом, независимо от выбранного лицензирования.

25. Версии Automake API

Новые выпуски Automake обычно включают исправления ошибок и новые функции, но к сожалению могут вносить новые ошибки и несовместимости. Этим обусловлены 4 причины, по которым пакету может требоваться конкретная версия Automake. Ситуация усложняется при поддержке большого дерева пакетов, которым могут требоваться разные версии Automake. Раньше это требовало от разработчика (иногда и от пользователя) установки нескольких версий Automake в разных местах и соответствующего переключения $PATH для каждого пакета.

Начиная с версии 1.6, Automake устанавливает версионные исполняемые файлы, что позволяет иметь несколько версий Automake в одном каталоге $prefix, и выбирать нужную версию Automake командами вида automake-1.6 или automake-1.7 без манипуляция с $PATH. Кроме того, Makefile, созданные Automake 1.6 будут явно использовать automake-1.6 в правилах пересборки. Значение 1.6 в automake-1.6 указывает версию Automake API, а не самой программы. При выпуске, например, Automake 1.6.1 с исправлением ошибок версия API останется 1.6 и пакет, работавший с Automake 1.6, продолжит работать с 1.6.1, а после этого ожидается выпуск с исправлением ошибок. Если пакет опирается на исправление ошибки или функцию, добавленную в выпуске, можно передать эту версию Automake, чтобы старые выпуски не применялись. Например, можно указать в файле configure.ac

      AM_INIT_AUTOMAKE([1.6.1])    dnl Require Automake 1.6.1 or better.

или в отдельном файле Makefile.am

      AUTOMAKE_OPTIONS = 1.6.1   # Require Automake 1.6.1 or better.

Automake будет выдавать сообщение об ошибке, если его версия старше запрашиваемой.

Что есть в API

Определить программный интерфейс Automake не так просто. По сути, он должен включать как минимум все документированные переменные и цели, которые можно применять в Makefile.am, и описывать связанное с ними поведение (например, точки запуска -hook), командный интерфейс automake и aclocal, …

Чего нет в API

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

26. Перевод пакета на новую версию Automake

Automake поддерживает в пакете 3 типа файлов:

  • aclocal.m4;

  • Makefile.in;

  • дополнительные инструменты, такие как install-sh и py-compile.

Файлы aclocal.m4 создаются программой aclocal и содержат некоторые макросы M4 от Automake. Дополнительные инструменты устанавливаются при необходимости командой automake —add-missing. Файлы Makefile.in создаются из Makefile.am командой automake на основе определений макросов M4 из aclocal.m4, а также действий дополнительных инструментов. Поскольку все эти файлы тесно связаны, важно регенерировать их при переходе к новому выпуску Automake. Обычно для этого применяется приведенная ниже последовательность команд.

    aclocal # с нужными опциями (такими как -I m4) 
    autoconf 
    automake --add-missing --force-missing

или более коротким путем

    autoreconf -vfi

Опция —force-missing обеспечивает обновление дополнительных инструментов (5. Создание Makefile.in). Важно обновить все эти файлы при каждом обновлении Automake, даже если новый выпуск лишь исправляет ошибки. Например, исправление ошибки может включать изменения правил, создаваемых в Makefile.in и поддержку макросов M4, копируемых в aclocal.m4.

В настоящее время automake может диагностировать ситуации, когда файл aclocal.m4 был создан другой версией aclocal, однако актуальность дополнительных инструментов не проверяется. Т. е. automake скажет о необходимости повторного запуска aclocal, но не сообщит о необходимости использовать —force-missing. Разумно прочитать файл NEWS перед обновлением. В этом файле указываются все различия — новые функции, устаревшие конструкции, известные несовместимости и способы обхода.

27. Часто задаваемые вопросы об Automake

Здесь рассмотрены некоторые вопросы, часто задаваемые в почтовых конференциях.

27.1. CVS и генерируемые файлы

Распространение сгенерированных файлов

Пакеты, собранные с Autoconf и Automake, распространяются с некоторыми сгенерированными файлами, такими как configure и Makefile.in. Эти файлы создаются в системе разработчика и распространяются для того, чтобы пользователям не приходилось устанавливать некоторые инструменты, нужные для сборки. Другие сгенерированные файлы (сканеры Lex, анализаторы Yacc, документация Info) распространяются с аналогичными целями. Automake выводит в Makefile правила для пересборки этих файлов. Например, make будет запускать autoconf для пересборки configure при каждом изменении configure.ac. Это делает разработку более безопасной и обеспечивает соответствие сценария configure действительному файлу configure.ac. Поскольку сгенерированные файлы из пакетов могут устаревать, а tar сохраняет временные метки, правила пересборки не запускаются при распаковке и первой сборке пакета.

CVS и временные метки

Если не используется ключевое слово CVS (что требует обновления файлов при фиксации), CVS сохраняет временные метки при операциях cvs commit и cvs import -d. При извлечении файлов по команде cvs checkout временные метки устанавливаются по выбранному выпуску. Однако по команде cvs update файлы получат дату обновления, а не исходную временную метку выпуска. Это сделано для того, чтобы обеспечить уведомление make об обновлении исходных файлов. Такой сдвиг временных меток становится хлопотным, когда источники и сгенерированные файлы хранятся в CVS. Поскольку CVS обрабатывает файлы в лексическом порядке, файл configure.ac будет новее configure после команды cvs update, обновляющей оба файла, дае если файл configure был новей configure.ac при проверке. Вызов make приведет в результате к ненужной пересборке.

Сопровождающие делятся на два клана — те, кто хранит все распространяемые файлы в CVS, и те, кто держит генерируемые файлы за пределами CVS.

Все файлы в CVS

  • Репозиторий CVS содержит все распространяемые файлы и можно извлечь прежнюю версию целиком.

  • Сопровождающий может видеть изменения генерируемых файлов (например, изменение Makefile.in при обновлении Automake).

  • Пользователю не нужно иметь autotools для сборки загруженного проекта, можно работать как с архивом.

  • При использовании команды cvs update для обновления копии вместо cvs checkout для извлечения свежей версии, временные метки будут неточными, что активирует некоторые правила пересборки с попыткой запуска инструментов разработки, таких как autoconf и automake.

    Обращения к таким инструментам выполняются путем вызова сценария missing (27.2. Сценарий missing и режим AM_MAINTAINER_MODE), поэтому пользователь увидит много описательных предупреждения об отсутствующих или устаревших инструментах и, возможно, предложений о способах установки вместо ошибок command not found или (что хуже) непонятных сообщений от старых версий требуемых инструментов.

    Сопровождающие, заинтересованные в собираемости своих пакетов после выборки CVS даже у пользователей, не имеющих специфических инструментов, могут предоставить вспомогательный сценарий (или улучшенный вариант bootstrap) для корректировки временных меток после команды cvs update или git checkout для предотвращения ненужной пересборки. Если в проекте фиксируются созданные Autotools файлы, а также сгенерированные файлы .info, такой сценарий может иметь вид

         #!/bin/sh 
         # fix-timestamp.sh: prevents useless rebuilds after "cvs update" 
         sleep 1 
         # aclocal-generated aclocal.m4 depends on locally-installed 
         # '.m4' macro files, as well as on 'configure.ac' 
         touch aclocal.m4 
         sleep 1 
         # autoconf-generated configure depends on aclocal.m4 and on
         # configure.ac 
         touch configure 
         # so does autoheader-generated config.h.in 
         touch config.h.in 
         # and all the automake-generated Makefile.in files 
         touch `find . -name Makefile.in -print` 
         # finally, the makeinfo-generated '.info' files depend on the 
         # corresponding '.texi' files 
         touch doc/*.info
  • В распределенной среде вероятно использование разработчиками разных версий инструментов и повторные сборки, вызванные потерей временных меток приведут к фиктивным изменениям в генерируемых файлах. Есть несколько решений этой проблемы:

    • всем разработчиками следует применять одни версии, чтобы собранные заново файлы были идентичны файлам в CVS (это сложно при использовании разных версий);

    • использовать сценарий корректировки временных меток после (в команде GCC такой сценарий есть);

    • в файле configure.ac использовать макрос AM_MAINTAINER_MODE, отключающий по умолчанию все правила пересборки (27.2. Сценарий missing и режим AM_MAINTAINER_MODE).

  • Кроме ложных пересборок могут возникать и обратные ситуации, когда обработка меток CVS показывает, что устаревший файл не является таковым. Предположим, например, что разработчик изменил Makefile.am и заново собрал Makefile.in, а перед фиксацией обоих файлов решил еще раз изменить Makefile.am (без пересборки Makefile.in). Это изменение делает файл Makefile.in устаревшим. CVS обрабатывает файлы в алфавитном порядке и при использовании другим разработчиком команды cvs update файл Makefile.in окажется новее Makefile.am и разработчик не увидит, что файл Makefile.in реально устарел.

Сгенерированные файлы вне CVS

Одним из способов «примирить» CVS и make является хранение сгенерированных файлов вне CVS, т. е. не включать в число управляемых CVS файлов цели Makefile (производные файлы). Это позволяет разработчику не думать об изменениях в генерируемых файлах и не отслеживать их версии (при условии совместимости). Кроме того, не теряются временные метки, не пропускаются изменения исходных файлов как в рассмотренном выше примере.

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

Возможность разработчиков применять разные версии инструментов может скрывать ошибки при работе в распределенной среде. Разработчики применяют (и тестируют) свои сгенерированные файлы вместо выпускаемых фактически. Подготавливающий архив разработчик может применять версию инструментов, которая создает ложные выходные данные (например, не переносимые файлы C), что могли бы заметить другие разработчики при использовании одной версии инструментов.

Сторонние файлы

Еще один класс составляют файлы, которые распространяются с пакетом, но поддерживаются отдельно. Эти файлы не создают проблем с временными метками и не рассматривались выше. Например, такие инструменты как gettextize и autopoint (изи Gettext) или libtoolize (из Libtool) могут устанавливать или обновлять файлы пакета. Эти файлы, независимо от хранения в CVS, вызывают аналогичные опасения в части несоответствия версий инструментов.

27.2. Сценарий missing и режим AM_MAINTAINER_MODE

missing

Сценарий missing является оболочкой для нескольких инструментов разработки, предназначенной для уведомления пользователей об отсутствии нужных инструментов. Типичными инструментами сопровождающего служат autoconf, automake, bison и т. п. Поскольку создаваемые этими инструментами файлы распространяются с пакетом, эти инструменты не требуются при сборке пользователем и не проверяются в сценарии configure.

Однако при запуске правила пересборки с участием отсутствующих инструментов сценарий missing предупреждает пользователя, указывая, как можно получить нужный инструмент (по крайней мере широко известный, например, makeinfo или bison). Это удобней для пользователя, чем простое применение правил перестройки с выводом сообщений вида «sh: TOOL: command not found». Подобные предупреждения missing выдает и при обнаружении у пользователя слишком старых инструментов (эта задача сложнее отсутствия нужных инструментов, поэтому такие предупреждения могут выдаваться не всегда).

Если нужный инструмент установлен, missing пытается запустить его, но не будет продолжать попытки после отказа. Это удобно при разработке, поскольку фиксирует отказы. Однако пользователи, у которых отсутствует или устарел нужный инструмент, получат ошибку, когда вызванное правило пересборки остановит процесс. Такие случаи используют в качестве аргументов сторонники режима AM_MAINTAINER_MODE.

AM_MAINTAINER_MODE

AM_MAINTAINER_MODE позволяет управлять правилами повторной сборки. При AM_MAINTAINER_MODE([enable]) правила включены по умолчанию. Если правила отключены, при наличии AM_MAINTAINER_MODE в configure.ac и запуске команды ./configure && make, программа make никогда не будет пытаться заново создать configure, Makefile.in, вывод Lex или Yacc и т. п. Таким образом, отключается повторная сборка файлов, которые обычно распространяются и пользователю не нужно их обновлять. Пользователь может изменить принятый по умолчанию режим опциями команды configure —enable-maintainer-mode или —disable-maintainer-mode.

AM_MAINTAINER_MODE применяется для того, чтобы пользователей не раздражали потери временных меток (27.1. CVS и генерируемые файлы) или потому, что правила пересборки не созданы и следует явно запускать инструменты. Режим AM_MAINTAINER_MODE также позволяет отключить некоторые пользовательские правила сборки по условию. Некоторые разработчики применяют это для отключений правил, требующих экзотических инструментов.

Несколько лет назад Франсуа Пинар представил аргументы против макроса AM_MAINTAINER_MODE, большая часть которых относилась к безопасности. Удаление зависимостей веде к независимым сборкам — изменение исходных файлов не будет влиять на генерируемые файлы и может создавать путаницу, если не будет замечено. Франсуа отметил, что безопасность не следует оставлять лишь на сопровождающих (как предлагает —enable-maintainer-mode), скорее напротив. Если один пользователь изменил Makefile.am, нужно изменить Makefile.in или выдать предупреждение (для чего Automake использует missing) и самым последним делом является игнорирование изменений без уведомления пользователя (как при отключении пересборки с помощью AM_MAINTAINER_MODE).

Джим Мейринг (Jim Meyering) — создатель AM_MAINTAINER_MODE принял аргументы Франсуа и отказался от AM_MAINTAINER_MODE в своих пакетах. Однако многие продолжают использовать AM_MAINTAINER_MODE, поскольку это помогает при работе с проектами, где файлы хранятся в системе управления версиями и сценария missing не достаточно, если используются разные версии инструментов.

27.3. Почему Automake не поддерживает шаблоны?

Разработчики ленивы и предпочли бы использовать шаблоны в Makefile.am, чтобы не обновлять эти файлы при каждом добавлении, удалении или переименовании файла. Однако есть ряд причин отказа от поддержки шаблонов.

  • При использовании CVS (или аналога) разработчики должны помнить о необходимости использовать cvs add или cvs rm. Поэтому обновление Makefile.am быстро становится рефлексом. И наоборот, если приложение не компилируется в результате того, что файл Makefile.am не был обновлен, это напомнит о cvs add.

  • Использование шаблонов упрощает распространение ошибок. Например, код, с которым экспериментирует разработчик (скажем, тестовый пример) может неожиданно стать частью дистрибутива.

  • При использовании шаблонов легко пропустить по ошибке некоторые файлы. Например, один разработчик создает новый файл, использует его во многих метах, но забывает зафиксировать. Другой разработчик может проверить незавершенный проект и даже окажется способным выполнить команду make dist, несмотря на отсутствие файла, но получит сообщение об отсутствующих файлах.

  • Шаблоны не переносимы в некоторые отличные от GNU реализации make. Например в NetBSD make не преобразует * в предварительных условиях для цели.

  • Наконец, действительно трудно забыть о добавлении файлов в Makefile.am, поскольку такие файлы не будут компилироваться и устанавливаться, что не позволит даже протестировать их.

Однако эти аргументы скорей философские и можно привести серьезные возражения для них или аргументы в пользу шаблонов. Поэтому следует рассмотреть и техническую сторону вопроса — переносимость. Хотя $(wildcard …) работает в GNU make, это не переносится в другие реализации make. Единственным способом поддержки $(wildcard …) в Automake является преобразование $(wildcard …) в процессе работы automake. Полученные файлы Makefile.in будут переносимыми, поскольку в них указаны все файлы и не используется $(wildcard …). Однако это требует от разработчиков запускать automake при каждом добавлении, удалении или переименовании файла. По сравнению с редактированием Makefile.am это очень маленький выигрыш. Конечно, проще и быстрее набрать automake; make, чем emacs Makefile.am; make. Но пока никто не удосужился добавить поддержку этого. Кое-кто использует сценарии для генерации списка файлов в Makefile.am или отдельных фрагментах Makefile.

Даже если переносимость не волнует и желание использовать $(wildcard …) сохраняется в силу нацеленности лишь на GNU Make, следует помнить, что во многих местах программе Automake нужно точно знать, какие файлы следует обрабатывать. Если Automake не знает, как преобразовать $(wildcard …), шаблоны не удастся применить в таких местах. $(wildcard …) является «черным ящиком» по сравнению с подстановкой AC_SUBST, используемой Automake. Можно получать предупреждения об использовании конструкций $(wildcard …) путем указания флага -Wportability.

27.4. Ограничения для имен файлов

Automake пытается поддерживать все типы имен файлов, включая имена с необычными символами и слишком длинные. Однако некоторые ограничения задают операционные системы и применяемые инструменты. Большинство ОС запрещает использовать null-символы в именах файлов, а / служит разделителем каталогов. Также требуется использование кодировки символов в соответствии с настройкой locale. Automake соблюдает такие ограничения. В переносимых пакетах следует соблюдать для имен требования POSIX, позволяющие использовать в именах буквы ASCII, цифры, а также символы «_», «.» и «-» с разделением компонент имени символом /. Имена компонент не могут начинаться с -. Переносимые имена файлов POSIX не могут включать компоненты размером более 14 байтов, но сегодня достаточно безопасно применять ограничение XOPEN в 255 байтов. POSIX ограничивает размер полного имени (все компоненты) 255 байтами (XOPEN — 1023), но можно ограничивать размер имен в архивах 99 байтами для предотвращения проблем несовместимости со старыми версиями tar.

При отступлении от этих правил (например, символы не-ASCII или слишком длинные имена), установщики могут столкнуться с проблемами по причинам, не связанным с Automake. Тем не менее, следует помнить и об ограничениях Automake. Эти ограничения нежелательны, но некоторые из низ, по-видимому, присущи базовым инструментам, таким как Autoconf, Make, M4 и командные процессоры. Ограничения делятся на 3 категории — для каталогов установки и сборки, а также для имен файлов.

Символы

    newline " # $ ' `

не следует применять в именах каталогов установки, например, включать а параметр опции configure —prefix.

Для каталогов сборки эти ограничения сохраняется, а также в их имена не следует включать символы

    & @ \

Например, в полном имени каталога с исходными кодами не должно быть таких символов.

Имена исходных и устанавливаемых файлов (например, main.c) ограничены еще сильнее и для них следует выполнять правила POSIX/XOPEN. Кроме того, при возможном переносе в отличные от POSIX среды следует избегать имен, отличающихся лишь регистром символов (например, makefile и Makefile). Ограничение 8.3 (DOS) можно забыть.

27.5. Ошибки distclean

Здесь описана диагностика, которая может возникать при работе с командой make distcheck. Как отмечено в параграфе 14.4. Проверка дистрибутива, make distcheck пытается собрать и проверить пакет на предмет ошибок, подобных этой. Команда make distcheck выполняет сборку VPATH для пакета (2.2.6. Параллельная сборка (VPATH)), а затем вызывает make distclean. Оставшиеся в каталоге сборки файлы перечисляются после такой ошибки. Диагностика охватывает два типа ошибок:

  • файлы, забытые distclean;

  • распространяемые файлы, по ошибке собранные заново.

Первые файлы не распространяются, поэтому для исправления ошибки достаточно пометить их для очистки (13. Очистка). Вторая ошибка не всегда понятна, поэтому рассмотрим ее на примере. Предположим, что пакет содержит программу, для которой нужно создать страницу man с помощью help2man. Программа GNU help2man создает простые страницы руководств из вывода —help и —version других команд. Поскольку мы не хотим требовать от пользователя установки help2man, созданная страница man включена в дистрибутив, как показано ниже.

    # Фиктивный файл Makefile.am 
    bin_PROGRAMS = foo 
    foo_SOURCES = foo.c 
    dist_man_MANS = foo.1 

    foo.1: foo$(EXEEXT) 
            help2man --output=foo.1 ./foo$(EXEEXT)

Это будет распространять страницу man, однако make distcheck завершится отказом с ошибкой

    ERROR: files left in build directory after distclean: 
    ./foo.1

Почему повторно собирается foo.1? Хотя файл foo.1 распространяется, он зависит от нераспространяемого файла foo$(EXEEXT), который собирает пользователь и поэтому файл всегда кажется новее распространяемого foo.1. Команда make distcheck обнаружила несогласованность в пакете. Планировалось распространять foo.1, чтобы пользователи могли не устанавливать help2man, однако правило всегда перестраивает файл и пользователю требуется help2man. Нужно сделать так, чтобы файл foo.1 не перестраивался пользователями или отказаться от его распространения.

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

Одним из путей распространить в этом примере файл foo.1 является исключение зависимости от foo$(EXEEXT). Например, предположим, что вывод foo —version и foo —help не меняются, пока не изменится foo.c или configure.ac. Тогда можно указать в файле Makefile.am

    bin_PROGRAMS = foo 
    foo_SOURCES = foo.c 
    dist_man_MANS = foo.1 

    foo.1: foo.c $(top_srcdir)/configure.ac 
            $(MAKE) $(AM_MAKEFLAGS) foo$(EXEEXT) 
            help2man --output=foo.1 ./foo$(EXEEXT)

В результате foo.1 не будет перестраиваться при каждом изменении foo$(EXEEXT). Вызов make обеспечивает актуальность foo$(EXEEXT) до вызова help2man. Другим способом является использование разных каталогов для исполняемых файлов и страниц man с организаций SUBDIRS так, чтобы исполняемые файлы собирались раньше man.

Можно отказаться от распространения foo.1. В этом случае хорошо иметь зависимость foo.1 от foo$(EXEEXT), поскольку оба нужно перестроить. Однако будет невозможно собрать пакет в среде кросс-компиляции, поскольку сборка foo.1 включает вызов foo$(EXEEXT).

Другим случаем, когда возникают такие ошибки, является распространение файлов, созданных инструментами, собранными пакетом. Например,

    distributed-file: built-tools distributed-sources 
            build-command

следует заменить на

    distributed-file: distributed-sources
            $(MAKE) $(AM_MAKEFLAGS) built-tools 
            build-command

или отказаться от распространения distributed-file, если кросс-компиляция не важна.

Резюмируем рассмотренные в примерах вопросы:

  • распространяемые файлы не должны зависеть от нераспространяемых;

  • распространяемые файлы следует распространять с их зависимостями;

  • если файл предназначен для пересборки пользователем, нет смысла его распространять.

В безнадежных случаях можно отключить эту проверку, путем установки distcleancheck_listfiles в соответствии с параграфом 14.4. Проверка дистрибутива. Предварительно следует убедиться в понимании причины жалоб make distcheck. Установка distcleancheck_listfiles не исправляет ошибки, а скрывает их. Всегда можно сделать лучше.

27.6. Порядок переменных флагов

В чем разница между AM_CFLAGS, CFLAGS и mumble_CFLAGS? Почему automake выводит CPPFLAGS после AM_CPPFLAGS в строке компиляции? Разве не должно быть наоборот? Сценарий configure добавляет флаги предупреждений в CXXFLAGS. Почему при добавлении флага в конце AM_CXXFLAGS он помещается в начало?

Переменные флагов компиляции

В этом параграфе рассматриваются все приведенные выше вопросы. В примерах рассматривается в основном CPPFLAGS, но реально это относится ко всем флагам, применяемым в Automake — CCASFLAGS, CFLAGS, CPPFLAGS, CXXFLAGS, FCFLAGS, FFLAGS, GCJFLAGS, LDFLAGS, LFLAGS, LIBTOOLFLAGS, OBJCFLAGS, OBJCXXFLAGS, RFLAGS, UPCFLAGS, YFLAGS.

Переменные CPPFLAGS, AM_CPPFLAGS и mumble_CPPFLAGS могут служить для передачи флагов препроцессору C (фактически и другим языкам, таким как C++ или Fortran с предварительной обработкой). Переменная CPPFLAGS является пользовательской (3.6. Пользовательские переменные), AM_CPPFLAGS относится к Automake, а mumble_CPPFLAGS — к цели mumble (переменная на уровне цели, см. 8.4. Переменные для программ и библиотек).

Automake всегда использует две переменные при компиляции исходных файлов C. При компиляции объектного файла для цели mumble первой переменной будет mumble_CPPFLAGS (если она задана) или AM_CPPFLAGS. Второй переменной всегда служит CPPFLAGS. В приведенном ниже примере

    bin_PROGRAMS = foo bar 
    foo_SOURCES = xyz.c 
    bar_SOURCES = main.c 
    foo_CPPFLAGS = -DFOO 
    AM_CPPFLAGS = -DBAZ

xyz.o будет компилироваться с флагами $(foo_CPPFLAGS) $(CPPFLAGS) (поскольку xyz.o является частью foo), а main.o — с $(AM_CPPFLAGS) $(CPPFLAGS) (поскольку нет переменной для цели bar).

Разница между mumble_CPPFLAGS и AM_CPPFLAGS достаточно очевидна, поэтому сосредоточимся на CPPFLAGS. Это пользовательская переменная, т. е. она служит пользователю для управления компиляцией пакета. Переменная, наряду с другими, указана в конце вывода команды configure —help.

Например, для добавления /home/my/usr/include в путь поиска компилятора C можно указать

    ./configure CPPFLAGS='-I /home/my/usr/include'

и этот флаг будет распространен в правила компиляции всех Makefile.

Пользовательские переменные нередко переопределяются в процессе работы make. Многие установщики делают это с переменной prefix, но может быть полезно менять и для флаги компилятора. Например, при отладке проекта C++ может потребоваться отключить оптимизацию в определенном объектном файле, как показано ниже

    rm file.o 
    make CXXFLAGS=-O0 file.o 
    make

Причина появления $(CPPFLAGS) после $(AM_CPPFLAGS) или $(mumble_CPPFLAGS) в команде компиляции заключается в том, что последнее слово должно оставаться за пользователем. Это может обрести больше смысла, если посмотреть на флаг CXXFLAGS=-O0 в примере выше, который должен переопределить AM_CXXFLAGS или mumble_CXXFLAGS (а также заменить прежнее значение CXXFLAGS).

Никогда не следует переопределять пользовательские переменные (такие как CPPFLAGS) в Makefile.am. Для обнаружения таких ошибок служит команда automake -Woverride. Даже команды вида

    CPPFLAGS = -DDATADIR=\"$(datadir)\" @CPPFLAGS@

являются ошибочными. Хотя эта команда сохраняет заданное configure значение CPPFLAGS, определение DATADIR потеряется, если пользователь переопределит CPPFLAGS из команды make. Определение

    AM_CPPFLAGS = -DDATADIR=\"$(datadir)\"

показывает все, что нужно знать об использовании флагов на уровне цели.

Не следует добавлять опции к пользовательским переменным в configure по той же причине. Иногда нужно изменить эти переменные для выполнения теста , но впоследствии значения следует вернуть. И напротив, нормально менять переменные AM_ внутри configure, при использовании для них AC_SUBST, но это требуется достаточно редко, если действительно не нужно менять принятые по умолчанию определения переменных AM_ во всех Makefile.

Рекомендуется задавать дополнительные флаги в отдельных переменных. Например, можно создать макрос Autoconf для создания набора опций предупреждений для компилятора C и использовать AC_SUBST для подстановки в WARNINGCFLAGS. Можно также создать макрос Autoconf для определения флагов компилятора и компоновщика, которые следует применять при компоновке с библиотекой libfoo, и использовать AC_SUBST для подстановки в LIBFOOCFLAGS и LIBFOOLDFLAGS. Затем Makefile.am может использовать эти флаги, как показано ниже.

    AM_CFLAGS = $(WARNINGCFLAGS) 
    bin_PROGRAMS = prog1 prog2 
    prog1_SOURCES = ... 
    prog2_SOURCES = ... 
    prog2_CFLAGS = $(LIBFOOCFLAGS) $(AM_CFLAGS) 
    prog2_LDFLAGS = $(LIBFOOLDFLAGS)

В этом примере обе программы компилируются с флагами, подставленными в $(WARNINGCFLAGS), а prog2 дополнительно получает флаги, нужные для компоновки с libfoo. Отметим, что указание AM_CFLAGS в переменной CFLAGS на уровне цели является гарантией применения AM_CFLAGS к каждой цели в Makefile.in.

Такое использование переменных обеспечивает полный контроль за порядком флагов. Например, если в переменной $(WARNINGCFLAGS) есть флаг, который нужно отменить для конкретной цели, можно использовать что-то вроде prog1_CFLAGS = $(AM_CFLAGS) -no-flag. Если бы все флаги были принудительно добавлены в CFLAGS, не было бы возможности отменить один флаг. Это еще одна причина сохранять пользовательские переменные для пользователя.

Переменная из примера LIBFOO_LDFLAGS (с подчеркиванием) не была указана, поскольку это заставило бы Automake думать, что это переменная на уровне цели (как mumble_LDFLAGS) для некой необъявленной цели LIBFOO.

Другие переменные

В Automake есть другие переменные, следующие похожим принципам для пользовательских опций. Например, правила Texinfo (11.1. Texinfo) используют MAKEINFOFLAGS и AM_MAKEINFOFLAGS, тесты DejaGnu (15.5. Тесты DejaGnu) — RUNTESTDEFAULTFLAGS и AM_RUNTESTDEFAULTFLAGS, правила tags и ctags (18.1. Взаимодействие с etags) — ETAGSFLAGS, AM_ETAGSFLAGS, CTAGSFLAGS и AM_CTAGSFLAGS, правила Java (8.16. Компиляция файлов Java с помощью gcj) — JAVACFLAGS и AM_JAVACFLAGS. Ни одно из этих правил (пока) не поддерживает флагов для цели.

В некоторой степени даже AM_MAKEFLAGS (7.1. Рекурсия каталогов) подчиняется этой схеме именования. Незначительное отличие состоит в том, что MAKEFLAGS передается субкоманде make неявно самой командой make.

ARFLAGS (8.2. Сборка библиотек) обычно задается Automake и не имеет AM_ или аналога на уровне цели.

Не следует считать, что наличие переменной для каждой цели предполагает существование AM_ или пользовательской переменной. Например, переменная уровня цели mumble_LDADD переопределяет переменную LDADD уровня Makefile (не является пользовательской) и mumble_LIBADD существует лишь на уровне цели (8.4. Переменные для программ и библиотек).

27.7. Переименование объектных файлов

Это происходит при использовании флагов компиляции на уровне цели. Объектные файлы переименовываются на случай совпадения имен с файлами тех же источников, скомпилированныи с другими флагами. Рассмотрим пример

    bin_PROGRAMS = true false 
    true_SOURCES = generic.c 
    true_CPPFLAGS = -DEXIT_CODE=0 
    false_SOURCES = generic.c 
    false_CPPFLAGS = -DEXIT_CODE=1

Очевидно, что две программы собираются из одного источника, но было бы плохо использовать один объект, потому что generic.o нельзя собрать одновременно с -DEXIT_CODE=0 и -DEXIT_CODE=1. Поэтому automake создает правила для двух разных объектов — true-generic.o и false-generic.o.

На деле automake не проверяет использование общих источников при решении вопроса о переименовании объектов. Программа просто переименовывает все объекты цели, если видит использование флагов компиляции на уровне цели. Использование общих объектных файлов нормально, если не применяются фланги компиляции на уровне цели. Например, true и false используют один объект version.o в следующем примере.

    AM_CPPFLAGS = -DVERSION=1.0 
    bin_PROGRAMS = true false 
    true_SOURCES = true.c version.c 
    false_SOURCES = false.c version.c

Отметим, что на переименование объектов влияет также _SHORTNAME (8.4. Переменные для программ и библиотек).

27.8. Флаги компиляции на уровне объекта

Automake поддерживает флаги компиляции на уровне программ и библиотек (8.4. Переменные для программ и библиотек, 27.6. Порядок переменных флагов). Это позволяет задать флаги компиляции для всех файлов цели. Например,

    bin_PROGRAMS = foo 
    foo_SOURCES = foo.c foo.h bar.c bar.h main.c 
    foo_CFLAGS = -some -flags

Объекты foo-foo.o, foo-bar.o и foo-main.o компилируются с -some -flags. Отметим, что foo_CFLAGS дает флаги для использования при компиляции всех источников C программы foo, ничего не задавая отдельно для foo.c или foo-foo.o.

Если foo.c нужно скомпилировать в foo.o с особыми флагами, ситуация усложняется. Обычно флаги на уровне программы здесь не применимы напрямую. Предполагается что-то вроде флагов на уровне объекта, которые будут применяться лишь для foo-foo.o. Automake не поддерживает этого, однако можно легко обмануть программу, используя библиотеку с единственным объектом и своими флагами компиляции.

    bin_PROGRAMS = foo 
    foo_SOURCES = bar.c bar.h main.c 
    foo_CFLAGS = -some -flags 
    foo_LDADD = libfoo.a 
    noinst_LIBRARIES = libfoo.a 
    libfoo_a_SOURCES = foo.c foo.h 
    libfoo_a_CFLAGS = -some -other -flags

Здесь foo-bar.o и foo-main.o компилируются с -some -flags, а libfoo_a-foo.o с -some -other -flags. Затем все объекты можно скомпоновать в foo. Этого можно добиться и с помощью вспомогательных библиотек Libtool, например, задав noinst_LTLIBRARIES = libfoo.la (8.3.5. Вспомогательные библиотеки Libtool).

Другая заманчивая идея заключается в реализации флагов на уровне объекта путем переопределения флагов компиляции, которые automake будет создавать для этих файлов. Automake не будет задавать правило для цели, которая определена, поэтому можно думать о задании правила foo-foo.o: foo.c. Делать это не рекомендуется по причине вероятных ошибок. Например, если добавить такое правило в первый пример выше, это создаст проблемы при удалении foo_CFLAGS (foo.c в этом случае будет компилироваться как foo.o вместо foo-foo.o, см. 27.7. Переименование объектных файлов). Для поддержки отслеживания зависимостей, двух расширений .o и .obj, а также всех других переменных флагов, вовлеченных в компиляцию, потребуется в конечном итоге изменить копию правила, выведенного ранее automake для этого файла. Если новый выпуск Automake создаст иное правило, снова придется обновлять копию вручную.

27.9. Инструменты обработки с объемным выводом

В этом параграфе рассматривается использование make с инструментами, создающими много выходных файлов. Это не привязано к Automake и может применяться в обычных файлах Makefile. Предположим, что имеется программа foo, которая читает файл data.foo и создает файлы data.c и data.h. нужно написать правило Makefile, фиксирующую зависимость «один к двум». Естественное правило некорректно

    # Это является ошибкой
    data.c data.h: data.foo 
            foo data.foo

Приведенное правило говорит о том, что каждый из файлов data.c и data.h зависит от data.foo и каждый можно собрать запуском foo data.foo. Иными словами, это эквивалентно

    # Этого не нужно. 
    data.c: data.foo 
            foo data.foo 
    data.h: data.foo 
            foo data.foo

Это означает, что программа foo может быть запущена дважды. Обычно она дважды не запускается, поскольку реализация make достаточно эффективна для проверки наличия второго файла после сборки первого и увидит имеющийся файл. Однако в нескольких случаях повторных запуск произойдет в любом случае.

  • Наиболее тревожным является случай параллельной работы make. Если файлы data.c и data.h собираются параллельно, одновременно будут выполняться две команды foo data.foo, что нехорошо.

  • Другой случай возникает, когда зависимость (здесь data.foo) является фиктивной целью (или зависит от такой).

Решение для параллельной работы make, но не для фиктивных зависимостей показано ниже.

    data.c data.h: data.foo 
            foo data.foo 
    data.h: data.c

Эти правила эквивалентны

    data.c: data.foo 
            foo data.foo 
    data.h: data.foo data.c 
            foo data.foo

Поэтому в параллельном режиме make должны быть последовательно собраны data.c и data.h и будет обнаружено, что второй запуск не требуется после завершения первого.

Предложенного варианта достаточно в большинстве случаев, однако он слабо расширяется для большого числа файлов (требуется общая упорядоченность в соответствии с зависимостями) и нужно искать другое решение.

    # остается еще проблема с этим.
    data.c: data.foo 
            foo data.foo 
    data.h: data.c

Идея состоит в запуске foo data.foo только при необходимости обновления data.c, но далее будет указано, что data.h зависит от data.c. Т. е., если требуется data.h, а файл data.foo устарел, зависимость от data.c вызовет сборку. Это почти идеально, но предположим, что созданы файлы data.h и data.c, а затем data.h удален. Тогда команда make data.h не соберет заново data.h. Приведенное выше правило говорит лишь, что файл data.c должен быть актуален по отношению к data.foo и это условие выполняется. Нужно правило, вызывающее перестройку при отсутствии data.h.

    data.c: data.foo 
            foo data.foo 
    data.h: data.c 
    ## Восстановление после удаления $@ 
            @if test -f $@; then :; else \ 
              rm -f data.c; \ 
              $(MAKE) $(AM_MAKEFLAGS) data.c; \ 
            fi

Приведенную схему можно расширить для обработки большего числа файлов на входе и выходе. Один из выходов выбирается в качестве свидетельства успешного завершения команды — он зависит от всех входов, а остальные выходы зависят от него. Например, если foo вдобавок читает data.bar, а также создает data.w и data.x, можно задать

    data.c: data.foo data.bar 
            foo data.foo data.bar 
    data.h data.w data.x: data.c 
    ## Восстановление после удаления $@ 
            @if test -f $@; then :; else \ 
              rm -f data.c; \ 
              $(MAKE) $(AM_MAKEFLAGS) data.c; \ 
            fi

Однако в этом случае возникает три мелких проблемы. Одна связана с порядком временных меток data.h, data.w, data.x и data.c, другая — с состязанием в восстановлении при параллельном запуске нескольких экземпляров make, а третья — с правилом рекурсии, прерывающим make -n в случае работы с GNU make (и некоторыми другими реализациями), поскольку это может удалять data.h без необходимости.

Разберемся с первой проблемой — foo выводит 4 файла, но порядок их создания неизвестен. Предположим, что data.h создается раньше data.c. Тогда возникает странная ситуация и при следующем запуске make файл data.h будет старше data.c, что вызовет второе правил и оболочка будет выполнять команду if…fi, но в действительности будет выполняться ветвь then, т. е. ничего не произойдет. Иными словами, поскольку выбранный свидетель не является первым файлом, созданным командой foo, make запустит оболочку, которая ничего не сделает. Простым решением является исправление временных меток, как показано ниже.

    data.c: data.foo data.bar 
            foo data.foo data.bar 
    data.h data.w data.x: data.c 
            @if test -f $@; then \ 
              touch $@; \ 
            else \ 
    ## Восстановление после удаления $@ 
              rm -f data.c; \ 
              $(MAKE) $(AM_MAKEFLAGS) data.c; \ 
            fi

Другим решением является использование в качестве свидетеля выделенного файла, а не вывода foo.

    data.stamp: data.foo data.bar 
            @rm -f data.tmp 
            @touch data.tmp 
            foo data.foo data.bar 
            @mv -f data.tmp $@ 
    data.c data.h data.w data.x: data.stamp 
    ## Восстановление после удаления $@ 
            @if test -f $@; then :; else \ 
              rm -f data.stamp; \ 
              $(MAKE) $(AM_MAKEFLAGS) data.stamp; \ 
            fi

Файл data.tmp создается до запуска foo, поэтому его временная метка будет старше всех выходных файлов foo. Этот файл переименовывается в data.stamp после завершения работы foo, поскольку мы не хотим обновлять data.stamp в случае отказа foo.

В этом решении еще сохраняется вторая проблема — состязание в правиле восстановления. Если после успешной сборки пользователь удалит файлы data.c и data.h, а затем запустит make j, программа make может начать восстановление обоих файлов в параллель. Если два экземпляра правила выполнят $(MAKE) $(AM_MAKEFLAGS) data.stamp одновременно, сборка приведет к отказу (например, 2 правила создадут data.tmp, но лишь одно сможет переименовать его). Правда, такие ситуации не возникают при обычной сборке и бывают лишь в случаях повреждения дерева сборки. Здесь data.c и data.h були явно удалены без удаления data.stamp и других выходных файлов. Команды make clean; make всегда будут восстанавливать из таких состояний (даже при параллельной сборке) поэтому можно подумать, что правило восстановления предназначено исключительно для того, чтобы помочь при непараллельном использовании make. Исправление требует иного механизма блокировки для гарантированного применения лишь одного правила восстановления, повторно собирающего data.stamp. Можно представить что-то вроде

    data.c data.h data.w data.x: data.stamp 
    ## Восстановление после удаления $@ 
            @if test -f $@; then :; else \ 
              trap 'rm -rf data.lock data.stamp' 1 2 13 15; \ 
    ## mkdir является переносимой проверкой
              if mkdir data.lock 2>/dev/null; then \ 
    ## Код, выполняемый первым процессом. 
                rm -f data.stamp; \ 
                $(MAKE) $(AM_MAKEFLAGS) data.stamp; \ 
                result=$$?; rm -rf data.lock; exit $$result; \ 
              else \
    ## Код, выполняемый следующими процессами. 
    ## Ожидание завершения первого процесса. 
                while test -d data.lock; do sleep 1; done; \ 
    ## Успешно лишь при успехе первого процесса. 
                test -f data.stamp; \ 
              fi; \ 
            fi

Использование выделенного свидетеля (data.stamp) очень удобно, когда список выходных файлов заранее неизвестен. В качестве иллюстрации рассмотрим приведенные ниже правила для компиляции множества файлов *.el в файлы *.elc одной командой. И неважно, как задается цель ELFILES (она должна быть непустой в соответствии с POSIX).

    ELFILES = one.el two.el three.el ... 
    ELCFILES = $(ELFILES:=c) 

    elc-stamp: $(ELFILES) 
            @rm -f elc-temp 
            @touch elc-temp 
            $(elisp_comp) $(ELFILES) 
            @mv -f elc-temp $@ 

    $(ELCFILES): elc-stamp 
            @if test -f $@; then :; else \ 
    ## Восстановление после удаления $@ 
              trap 'rm -rf elc-lock elc-stamp' 1 2 13 15; \ 
              if mkdir elc-lock 2>/dev/null; then \ 
    ## Код, выполняемый первым процессом. 
                rm -f elc-stamp; \ 
                $(MAKE) $(AM_MAKEFLAGS) elc-stamp; \ 
                rmdir elc-lock; \ 
              else \ 
    ## Код, выполняемый следующими процессами. 
    ## Ожидание завершения первого процесса. 
                while test -d elc-lock; do sleep 1; done; \ 
    ## Успешно лишь при успехе первого процесса. 
                test -f elc-stamp; exit $$?; \ 
              fi; \ 
            fi

Во всех приведенных решениях сохраняется третья проблема — нарушается обещание не вносить реальных изменений в дерево по команде make -n. Для решений без файлов блокировки можно разделить правила восстановления на две команды, одна из которых выполняет все, кроме рекурсии, а другая рекурсивно вызывает $(MAKE). Решения с блокировкой могут воздействовать на содержимое переменной MAKEFLAGS, но переносимость этого не очевидна. Например,

    ELFILES = one.el two.el three.el ... 
    ELCFILES = $(ELFILES:=c) 

    elc-stamp: $(ELFILES) 
            @rm -f elc-temp 
            @touch elc-temp 
            $(elisp_comp) $(ELFILES) 
            @mv -f elc-temp $@ 

    $(ELCFILES): elc-stamp 
    ## Восстановление после удаления $@ 
            @dry=; for f in x $$MAKEFLAGS; do \ 
              case $$f in \ 
                *=*|--*);; \ 
                *n*) dry=:;; \ 
              esac; \ 
            done; \ 
            if test -f $@; then :; else \ 
              $$dry trap 'rm -rf elc-lock elc-stamp' 1 2 13 15; \ 
              if $$dry mkdir elc-lock 2>/dev/null; then \ 
    ## Код, выполняемый первым процессом. 
                $$dry rm -f elc-stamp; \ 
                $(MAKE) $(AM_MAKEFLAGS) elc-stamp; \ 
                $$dry rmdir elc-lock; \ 
              else \ 
    ## Код, выполняемый следующими процессами. 
    ## Ожидание завершения первого процесса. 
                while test -d elc-lock && test -z "$$dry"; do \ 
                  sleep 1; \ 
                done; \ 
    ## Успешно лишь при успехе первого процесса. 
                $$dry test -f elc-stamp; exit $$?; \ 
              fi; \ 
            fi

Для полноты следует отметить, что GNU make позволяет задавать правила с множеством выходных файлов, посредством шаблонных правил. Такие правила не рассматриваются здесь по причине непереносимости, но могут быть удобны в пакетах, предполагающих GNU make.

27.10. Установка в жестко заданные места

Почему при попытке применить приведенное правило для установки файла конфигурации make distcheck дает отказ?

         # Не делайте так. 
         install-data-local: 
                 $(INSTALL_DATA) $(srcdir)/afile $(DESTDIR)/etc/afile

Пакету нужно поместить данные в установочный каталог другого пакета. Этот каталог легко определить в configure, но при установке файлов туда возникает отказ make distcheck. Что делать?

У этих вариантов установки есть общая черта — отказ make distcheck обусловлен попыткой установки в жестко заданные места. Во втором случае путь на задан жестко в пакете, но является жестко заданным в системе (или в инструменте, предоставляющем путь). Пока путь не использует ни одну из стандартных переменных ($(prefix), $(bindir), $(datadir) и т. п.), эффект будет сохраняться и пользователь не сможет установить файл.

У обычного (не root) пользователя, желающего установить пакет, обычно нет права помещать что-либо в /usr или /usr/local. В результате применяется что-то вроде ./configure —prefix ~/usr для установки в каталог ~/usr. Если пакет пытается установить что-то по заданному жестко пути (например, /etc/afile), независимо от значения —prefix установка приведет к отказу. Команда make distcheck выполняет установку с —prefix и поэтому тоже завершается отказом.

Ниже приведено несколько простых решений. Пример install-data-local для установки /etc/afile лучше заменить на

    sysconf_DATA = afile

По умолчанию sysconfdir имеет значение $(prefix)/etc, поскольку этого требуют стандарты GNU. Когда такой пакет устанавливается в файловую систему FHS, установщик задает —sysconfdir=/etc. Сопровождающему не нужно заботиться о таких правилах сайта — просто используется стандартная переменная каталога для установки файлов, которую установщик легко может переопределить в соответствии с правилами сайта.

Установка файлов для использования другими пакетами несколько сложнее. Рассмотрим пример установки общей библиотеки, которая является модулем расширения Python. Если спросить Python, куда устанавливать библиотеку, ответ будет иметь вид

    % python -c 'from distutils import sysconfig; 
                 print sysconfig.get_python_lib(1,0)' 
    /usr/lib/python2.5/site-packages

Если действительно указать этот абсолютный путь для установки общей библиотеки, пользователь (не root) не сможет установить пакет и distcheck завершится отказом. Рассмотрим другой вариант. Функция sysconfig.get_python_lib() действительно принимает третий аргумент, заменяющий префикс установки Python.

    % python -c 'from distutils import sysconfig; 
                 print sysconfig.get_python_lib(1,0,"${exec_prefix}")' 
    ${exec_prefix}/lib/python2.5/site-packages

Можно использовать этот новый путь. В этом случае

  • пользователь root может установить пакет с тем же —prefix, что у Python (поведение предыдущей попытки);

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

Макрос AM_PATH_PYTHON использует похожие команды для заданий $(pythondir) и $(pyexecdir) (10.5. Python).

Конечно, не все инструменты настолько развиты, как Python в части подстановки PREFIX. Поэтому другая стратегия заключается в указании части каталога установки, которая должна сохраняться. Ниже показано, как макрос AM_PATH_LISPDIR (10.1. Emacs Lisp) находит $(lispdir).

    $EMACS -batch -Q -eval '(while load-path 
      (princ (concat (car load-path) "\n")) 
      (setq load-path (cdr load-path)))' >conftest.out 
    lispdir=`sed -n 
      -e 's,/$,,' 
      -e '/.*\/lib\/x*emacs\/site-lisp$/{ 
            s,.*/lib/\(x*emacs/site-lisp\)$,${libdir}/\1,;p;q;
          }' 
      -e '/.*\/share\/x*emacs\/site-lisp$/{ 
            s,.*/share/\(x*emacs/site-lisp\),${datarootdir}/\1,;p;q; 
          }' 
      conftest.out`

Просто выбирается первый каталог вида */lib/*emacs/site-lisp или */share/*emacs/site-lisp в пути поиска emacs и подставляется ${libdir} или ${datadir}. Случай emacs выглядит сложным, поскольку в нем обрабатывается список и предлагается две возможных схемы. В остальном все просто, а преимущества для обычных пользователей компенсируют дополнительный вызов sed.

27.11. Отладка правил Make

Правила и деревья зависимостей, создаваемые automake, могут быть достаточно сложными и создавать для разработчиков проблемы, когда что-то пошло не так. В дополнение к предоставляемым командой make опций отладки имеется еще ряд способов эффективной отладки файлов, созданных automake.

  • Если в пакете включен сокращенный вывод с помощью правил тишины (21.3. Как Automake может «заглушить» make), можно использовать make V=1 для просмотра выполняемых команд.

  • Команда make -n может показать, что было сделано без реального выполнения этого. Однако следует помнить, что не будут учитываться команды с префиксом +, а при использовании GNU make — команды со строками $(MAKE) и ${MAKE}. Как правило, это полезно для просмотра действий рекурсивных правил, но это означает, что в ваших правилах не следует смешивать рекурсию с действиями, которые меняют файлы9. Кроме того, отметим, что GNU make обновит предварительные условия для Makefile даже при наличии -n.

  • Команда make SHELL=»/bin/bash -vx» может помочь при отладке сложных правил.

  • Команда echo ‘print: ; @echo «$(VAR)»‘ | make -f Makefile -f — print может помочь в проверке преобразованных значений переменных. Может потребоваться указание отличной от print цели, если та уже применяется или есть файл с таким именем.

  • На странице http://bashdb.sourceforge.net/remake/ описана измененная команда GNU make, называемая remake, которая копирует связанные с GNU make файлы Makefiles и позволяет отслеживать их выполнение, проверять переменные и вызывать правила в интерактивном режиме, подобно отладчику.

27.12. Информирование об ошибках

Любая нетривиальная программа имеет ошибки и Automake не является исключением. Разработчики не могу обещать исправить и даже признать ошибку, но прежде всего о ней нужно узнать. Для этого пользователям при обнаружении ошибок следует информировать о них разработчиков. Перед отправкой сообщения об ошибке разумно узнать, не указал ли кто-нибудь на нее раньше. Для этого можно посмотреть GNU Bug Tracker и почтовые конференции bug-automake. Ранее для информировании об ошибках использовалась база данных Gnats и сообщения о некоторых ошибках уже могут быть в ней (не следует использовать эту базу для новых сообщений).

Если ошибка еще не известна, следует сообщить о ней. Важно сообщать об ошибках так, чтобы это было полезно и информативно. Для этого следует ознакомиться с документами How to Report Bugs Effectively и How to Ask Questions the Smart Way, что поможет вам и разработчикам сэкономить время. Для сообщений об ошибках, запроса новых функций и других предложений, следует использовать адрес bug-automake@gnu.org. Это будет создавать новую запись в системе отслеживания ошибок. В сообщении следует указать используемые версии Autoconf и Automake, а в идеале приложить файлы Makefile.am и configure.ac для воспроизведения проблемы. При отказах тестов следует предоставить файл test-suite.log.

Приложение A. Копирование этого руководства

A.1. GNU Free Documentation License

                    Version 1.3, 3 November 2008

Copyright © 2000-2018 Free Software Foundation, Inc. <http://fsf.org/>

Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

0. PREAMBLE

The purpose of this License is to make a manual, textbook, or other functional and useful document “free” in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.

This License is a kind of “copyleft”, which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.

We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.

1. APPLICABILITY AND DEFINITIONS

This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The “Document”, below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as “you”. You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.

A “Modified Version” of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.

A “Secondary Section” is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document’s overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.

The “Invariant Sections” are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections.  If the Document does not identify any Invariant Sections then there are none.

The “Cover Texts” are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.

A “Transparent” copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not “Transparent” is called “Opaque”.

Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.

The “Title Page” means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, “Title Page” means the text near the most prominent appearance of the work’s title, preceding the beginning of the body of the text.

The “publisher” means any person or entity that distributes copies of the Document to the public.

A section “Entitled XYZ” means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as “Acknowledgements”, “Dedications”, “Endorsements”, or “History”.) To “Preserve the Title” of such a section when you modify the Document means that it remains a section “Entitled XYZ” according to this definition.

The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document.  These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.

2. VERBATIM COPYING

You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.

You may also lend copies, under the same conditions stated above, and you may publicly display copies.

3. COPYING IN QUANTITY

If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document’s license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all of these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.

If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.

If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.

It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.

4. MODIFICATIONS

You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:

A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.

B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.

C. State on the Title page the name of the publisher of the Modified Version, as the publisher.

D. Preserve all the copyright notices of the Document.

E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.

F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.

G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document’s license notice.

H. Include an unaltered copy of this License.

I. Preserve the section Entitled “History”, Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled “History” in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.

J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the “History” section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.

K. For any section Entitled “Acknowledgements” or “Dedications”, Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.

L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.

M. Delete any section Entitled “Endorsements”. Such a section may not be included in the Modified Version.

N. Do not retitle any existing section to be Entitled “Endorsements” or to conflict in title with any Invariant Section.

O. Preserve any Warranty Disclaimers.

If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version’s license notice. These titles must be distinct from any other section titles.

You may add a section Entitled “Endorsements”, provided it contains nothing but endorsements of your Modified Version by various parties—for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.

You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.

The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.

5. COMBINING DOCUMENTS

You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.

The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.

In the combination, you must combine any sections Entitled “History” in the various original documents, forming one section Entitled “History”; likewise combine any sections Entitled “Acknowledgements”, and any sections Entitled “Dedications”. You must delete all sections Entitled “Endorsements.”

6. COLLECTIONS OF DOCUMENTS

You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.

You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.

7. AGGREGATION WITH INDEPENDENT WORKS

A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an “aggregate” if the copyright resulting from the compilation is not used to limit the legal rights of the compilation’s users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.

If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document’s Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.

8. TRANSLATION

Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.

If a section in the Document is Entitled “Acknowledgements”, “Dedications”, or “History”, the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.

9. TERMINATION

You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License.

However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it.

10. FUTURE REVISIONS OF THIS LICENSE

The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time.  Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.  See <http://www.gnu.org/copyleft/>.

Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License “or any later version” applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy’s public statement of acceptance of a version permanently authorizes you to choose that version for the Document.

11. RELICENSING

“Massive Multiauthor Collaboration Site” (or “MMC Site”) means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A “Massive Multiauthor Collaboration” (or “MMC”) contained in the site means any set of copyrightable works thus published on the MMC site.

“CC-BY-SA” means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization.

“Incorporate” means to publish or republish a Document, in whole or in part, as part of another Document.

An MMC is “eligible for relicensing” if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008.

The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing.

ADDENDUM: How to use this License for your documents

To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:

Copyright (C) YEAR YOUR NAME.

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled «GNU Free Documentation License».

If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the “with…Texts.” line with this:

with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.

If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.

If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.

Перевод на русский язык

Николай Малых

nmalykh@protokols.ru

1Эти переменные называют также макросами make, однако здесь термин «макрос» относится к макросам Autoconf.

2VPATH — название свойства make, используемого в Makefile для включения такой сборки

3Control+x, Control+c — одновременное нажатие клавиш.

4Эта работа является новой и может содержать ошибки.

5Есть и другие причины для этого ограничения.

6Следует отметить, что automake распознает -d в AM_YFLAGS лишь при отсутствии объединения (кластера) с другими опциями. Например, опция не будет распознана если AM_YFLAGS имеет значение -dt, но будет видна при значении -d -t или -t -d.

7Почти вся информация в последующих параграфах относится к предварительной обработке Fortran 77 и почти дословно скопирована из Catalogue of Rules (make).

8Например, cfortran решает вопросы совместного использования языков и работает почти со всеми компиляторами Fortran 77, C и C++ практически на любой платформе. Однако cfortran пока не распространяется свободно, хотя это планируется в будущем.

9В правилах Automake dist и distcheck была ошибка и они создавали каталоги даже с опцией -n (исправлена в Automake 1.11.

Рубрика: Linux | Комментарии к записи GNU Automake отключены

Утилита autoreconf для обновления файлов конфигурации

Утилита autoreconf для обновления файлов конфигурации

PDF

Команда autoreconf запускает утилиты autoconf (а также autoheader, aclocal, automake, autopoint1 и libtoolize, когда это применимо) для создания новых вариантов файлов GNU Build System в указанном параметром DIRECTORY каталоге и его подкаталогах. По умолчанию операции выполняются в текущем каталоге (.).

При отсутствии опций обновляются лишь файлы, для которых имеются более новые источники. Если установлена новая версия системы сборки GNU, можно обновить по команде autoreconf все файлы с помощью опции —force.

Синтаксис

autoreconf [OPTION]... [DIRECTORY]…

Опции

-h, —help

Выводит справку о работе с программой.

-V, —version

Выводит номер версии программы и завершает работу.

-v, —verbose

Задаёт подробный вывод в процессе работы.

-d, —debug

Отключает удаление временных файлов.

-f, —force

Задаёт регенерацию всех файлов конфигурации, считая имеющиеся устаревшими.

-i, —install

Задаёт копирование отсутствующих вспомогательных файлов.

—no-recursive

Отключает обновления для субпакетов.

-s, —symlink

При использовании вместе с опцией -i задаёт создание символьных ссылок вместо копирования файлов.

-m, —make

Если это возможно, выполняется команда ./configure && make.

-W, —warnings=CATEGORY

Задаёт вывод предупреждений указанных категорий.

cross

Сообщения, связанные с кросс-компиляцией.

gnu

Сообщения, связанные со стандартами кодирования GNU (принято по умолчанию в режимах gnu и gnits).

obsolete

Сообщения об устаревших функциях или конструкциях.

override

Сообщения о пользовательских переопределениях правил или переменных Automake.

portability

Сообщения о проблемах переносимости (принято по умолчанию в режимах gnu и gnits).

syntax

Сообщения о сомнительных синтаксических конструкциях (принято по умолчанию).

unsupported

Сообщения о неподдерживаемых и неполных функциях (принято по умолчанию).

all

Все предупреждения.

no-CATEGORY

Отключает вывод сообщений указанной категории (CATEGORY).

none

Отключает вывод всех предупреждений

error

Считать все предупреждения ошибками.

Значение переменной окружения WARNINGS учитывается. Некоторые инструменты могут поддерживать иные типы предупреждений, поэтому рекомендуется использовать режим all.

-B, —prepend-include=DIR

Добавляет значение DIR в начало строки путей поиска.

-I, —include=DIR

Добавляет значение DIR в конец строки путей поиска.

Учитываются значения переменных окружения AUTOM4TE, AUTOCONF, AUTOHEADER, AUTOMAKE, ACLOCAL, AUTOPOINT, LIBTOOLIZE, M4, MAKE.

Автор

Программу создали David J. MacKenzie и Akim Demaille.

Сообщения о найденных ошибках

Об ошибках в программе следует сообщать по адресу <bug-autoconf@gnu.org>. Страница GNU Autoconf доступна по ссылке <http://www.gnu.org/software/autoconf/>, общая информация о программах GNU — по ссылке <http://www.gnu.org/gethelp/>.

Авторские права

Copyright © 2012 Free Software Foundation, Inc. Лицензия GPLv3+/Autoconf: GNU GPL версии 3 или выше <http://gnu.org/licenses/gpl.html>, <http://gnu.org/licenses/exceptions.html>.

Это свободно распространяемая программа. Разрешение распространения программы законами не гарантируется.

 

Перевод на русский язык

Николай Малых

nmalykh@protokols.ru

1Ранее называлась gettextize.

Рубрика: Linux | Оставить комментарий