




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
RFC6020YANG-NETCONF数据建模语言第页××××详细设计书RFC6020YANG-NETCONF数据建模语言
修订记录修订日期修改人修改内容2020-03-15程怀玺-Jade完成翻译初稿概述:YANG(YetAnotherNextGeneration)是一种数据建模语言,用于对NETCONF协议操纵的配置和状态数据、NETCONF远程过程调用以及NETCONF的通知进行建模。
目录目录 31引言 112 关键字 123 术语 123.1 Mandatory节点 134 YANG概览 144.1 功能概述 144.2 语言概述 154.2.1 模块和子模块 154.2.2 数据建模基础 164.2.3 状态数据 194.2.4 内置类型 204.2.5 派生类型(typedef) 214.2.6 可重用节点组(分组) 214.2.7 选择 234.2.8 扩展数据模型(augment) 244.2.9 RFC定义 244.2.10 通知定义 255 语言概念 265.1 模块和子模块 265.1.1 模块和子模块 275.1.2 模块层次(ModuleHierarchies) 285.2 文件布局 285.3 XML名称空间(Namesapce) 295.3.1 YANGXML名称空间 295.4 解析组、类型和身份标识 295.5 嵌套类型定义和分组 295.6 一致性 305.6.1 基本行为 305.6.2 可选特性 305.6.3 偏差 315.6.4 在<hello>中通告一致性信息 315.7 数据存储修改 336 YANG语法 336.1 词汇标记(LexicalTokenization) 336.1.1 注释(Comments) 346.1.2 Tokens 346.1.3 使用引号(Quoting) 346.2 ID(Identifiers) 356.2.1 标识符和其名字空间 356.3 语句 366.3.1 语言扩展 366.4 XPath评估 366.4.1 XPath上下文 376.5 架构(Schema)节点标识符 377 YANG语句 387.1 module语句 387.1.1 模块的子语句 397.1.2 yang-version语句 407.1.3 namespace语句 407.1.4 prefix语句 407.1.5 import语句 407.1.6 include语句 417.1.7 organization语句 427.1.8 contact语句 427.1.9 revision语句 427.1.10 使用范例 427.2 submodule语句 437.2.1 子模块的子语句 447.2.2 子模块的子语句 457.2.3 使用范例 457.3 typedef语句 467.3.1 typedef语句的子语句 467.3.2 typedef的type语句 467.3.3 typedef的units语句 477.3.4 typedef的default语句 477.3.5 使用范例 477.4 type语句 477.4.1 type的子语句 477.5 container语句 487.5.1 在场container(PresenceContainer) 487.5.2 container子语句 487.5.3 must语句 497.5.4 must的子语句 507.5.5 presence语句 517.5.6 容器子节点语句 517.5.7 XML映射规则 517.5.8 NETCONF<edit-config>操作 527.5.9 使用范例 527.6 leaf语句 537.6.1 叶子的默认值 537.6.2 叶子的子语句 547.6.3 叶子的type语句 547.6.4 叶子的default语句 547.6.5 叶子的mandatory语句 557.6.6 XML映射规则 557.6.7 NETCONF<edit-config>操作 557.6.8 使用范例 567.7 leaf-list语句 567.7.1 顺序 577.7.2 leaf-list的子语句 577.7.3 min-elements语句 587.7.4 max-elements语句 587.7.5 ordered-by语句 587.7.6 XML映射规则 597.7.7 NETCONF<edit-config>操作 597.7.8 使用范例 607.8 list语句 617.8.1 list的子语句 617.8.2 list的key语句 627.8.3 list的unique语句 627.8.4 list的孩子节点语句 647.8.5 XML映射规则 647.8.6 NETCONF<edit-config>操作 647.8.7 使用范例 657.9 choice语句 687.9.1 choice子语句 687.9.2 choice的case语句 697.9.3 choice的default语句 707.9.4 choice的mandatory语句 717.9.5 XML映射规则 727.9.6 NETCONF<edit-config>操作 727.9.7 使用范例 727.10 anyxml语句 737.10.1 anyxml子语句 737.10.2 XML映射规则 747.10.3 NETCONF<edit-config>操作 747.10.4 使用范例 747.11 grouping语句 757.11.1 group的子语句 757.11.2 使用范例 767.12 uses语句 767.12.1 uses的子语句 767.12.2 refine语句 777.12.3 XML映射规则 777.12.4 使用范例 777.13 rpc语句 787.13.1 rpc的子语句 797.13.2 input语句 797.13.3 output语句 807.13.4 XML映射规则 817.13.5 使用范例 817.14 notification语句 827.14.1 notification的子语句 827.14.2 XML映射规则 827.14.3 使用范例 837.15 augment语句 837.15.1 augment的子语句 847.15.2 XML映射规则 857.15.3 使用范例 857.16 identity语句 867.16.1 identity的子语句 877.16.2 base语句 877.16.3 使用范例 877.17 extension语句 887.17.1 extension的子语句 887.17.2 argument的子语句 887.17.3 使用范例 897.18 一致性相关语句 907.18.1 feature语句 907.18.2 if-feature语句 917.18.3 deviation语句 917.19 公共语句 947.19.1 config语句 947.19.2 status语句 947.19.3 description语句 957.19.4 reference语句 957.19.5 when语句 958 约束 968.1 数据约束 968.2 约束层次 968.3 约束执行模型 978.3.1 载荷解析 978.3.2 NETCONF<edit-config>操作 988.3.3 验证 989 内置类型 999.1 表示规范 999.2 整数内置类型 999.2.1 词汇形式 1009.2.2 规范形式 1009.2.3 限制 1009.2.4 rang语句 1009.2.5 使用范例 1019.3 decimal64内置类型 1019.3.1 词法表示 1029.3.2 规范表示 1029.3.3 限制 1029.3.4 fraction-digits语句 1029.3.5 使用范例 1039.4 string内置类型 1039.4.1 词法形式 1039.4.2 规范形式 1039.4.3 限制 1039.4.4 length语句 1039.4.5 使用范例 1049.4.6 pattern子语句 1049.4.7 使用范例 1059.5 boolean内置类型 1059.5.1 词法表示 1059.5.2 规范表示 1059.5.3 限制 1069.6 enumeration内置类型 1069.6.1 词法表示 1069.6.2 规范表示 1069.6.3 限制 1069.6.4 enum语句 1069.6.5 使用范例 1079.7 bits内置类型 1079.7.1 限制 1079.7.2 词法表示 1079.7.3 规范表示 1089.7.4 bit语句 1089.7.5 使用范例 1089.8 binary内置类型 1099.8.1 限制 1099.8.2 词法表示 1099.8.3 规范表示 1099.9 leafref内置类型 1099.9.1 限制 1109.9.2 path语句 1109.9.3 词法表示 1109.9.4 规范表示 1119.9.5 使用范例 1119.10 identityfre内置类型 1149.10.1 限制 1149.10.2 identityref的base语句 1149.10.3 语法表示 1149.10.4 规范表示 1149.10.5 使用范例 1149.11 empty内置类型 1159.11.1 限制 1159.11.2 词法形式 1159.11.3 规范形式 1169.11.4 适用范例 1169.12 union内置类型 1169.12.1 限制 1169.12.2 词法表示 1179.12.3 规范表示 1179.13 instance-identifier内置类型 1179.13.1 限制 1179.13.2 require-instance语句 1189.13.3 句法表示 1189.13.4 规范表示 1189.13.5 使用范例 11810 更新模型 11911 YIN 12011.1.1 YIN正式定义 12012 YANGABNF语法 12413 YANG相关错误的错误响应 14213.1 违反唯一性语句的数据的错误消息 14213.2 违反max-elements语句的数据的错误消息 14213.3 违反min-elements语句的数据的错误消息 14313.4 违反must语句的数据的错误消息 14313.5 违反require-instance语句的数据的错误消息 14313.6 无匹配leafref类型数据的错误消息 14313.7 违反mandatoryChoice语句的数据的错误消息 14413.8 "insert"操作的错误消息 14414 IANA注意事项 14414.1 媒体类型application/yang 14514.2 媒体类型application/yin+xml 14615 安全注意事项 14616 贡献者 14717 致谢 14718 参考文档 14718.1 NormativeReferences 14718.2 InformativeReferences 148
1引言YANG(YetAnotherNextGeneration)是一种数据建模语言,用于对NETCONF协议操纵的配置和状态数据、NETCONF远程过程调用(RPC)以及NETCONF的通知进行建模。YANG通常也用于对NETCONF协议的操作层和内容层进行建模(参考RFC4741NETCONF配置协议的1.1章节)。本文档描述了YANG语言的语法和语义,YANG模型中定义的数据模型如何用XML来呈现,NETCONF操作如何操纵数据。关键字本文档涉及的关键字"MUST","MUSTNOT","REQUIRED","SHALL","SHALLNOT","SHOULD","SHOULDNOT","RECOMMENDED","MAY",和"OPTIONAL"请参考RFC2119[RFC2119]。术语anyxml:一个可以包含未知XML数据块的数据节点。augment:增强,在已有架构(schema:也可翻译为模式)节点的基础上,增加新的架构节点。basetype:基本类型,用于派生其他类型,基本类型可以是内置类型或其他派生类型。built-intypes:内置类型:YANG语言定义的数据类型,比如uint32或string。choice:选择:一个架构节点,在多个被标识的多个可选项中只有一个是有效的。类似于c语音的switchcase语句。configurationdata:配置数据:由RFC4741定义,用于将系统从初始的默认状态转换为当前状态的配置数据的集合。conformance:一致性:设备遵循一个数据模型准确性的度量。container:容器,在数据树中最多存在一个实例的内部数据节点,容器没有值(有值没有孩子节点的是叶子节点,容器是数据树中包含叶子的节点),但是包含一组子节点。datadefinitionstatement:数据定义语句:定义一个新的数据节点的语句。可以是container容器,leaf叶子,leaf-list叶子列表,list列表,choice选择,case分支,augment增强,uses和anyxml中的一个。datamodel:数据模型,数据模型定义了呈现和访问数据的方式。datanode:数据节点,架构树上的可以在数据树中实例化的节点,节点可以是容器container、叶子leaf、叶子列表leaf-list、列表list和anyxml中的一种。datatree:数据树,配置和状态数据在设备上的实例树。derivedtype:派生类型,一个从内置类型或其他派生类型派生出的类型。devicedeviation:设备偏差,设备未能如实的实现某个模块。extension:扩展,扩展将非YANG语义添加到语句(statement)中,扩展语句定义了表达这些语义的新的语句。feature:特性,将模型的一部分做成可选的一种机制,定义可以通过一个特性名字作为标签,并且只有在标签有效的设备上支持该特性。等于NETCONF中的能力。grouping:分组,架构节点重用的集合,组也许用于模块内部、用于包含它的模块、用于引入它的模块。分组语句不是一个数据定义声明,并且没有在架构树中定义任何节点。分组纯粹是为了方便重用。indentifier:标识符,用于标识不同YANG条目的名字。instanceidentifier:实例标识,标识数据树中特定节点的机制。interiornode:内部节点,层次结构内部非叶子节点的节点leaf:叶子,在数据树中最多存在一个实例的数据节点。叶子有值,但不能包含子节点。leaf-list:叶子列表,类似于叶子节点,但定义了一组可唯一识别的节点集,而不是单个节点。每个节点都有值,但没有子节点。leaf-list可以理解是单个类型元素组成的数组。list:列表,一个在数据树中可能存在多个实例的内部数据节点。列表没有值,指示一组子节点。list可以理解为结构体数组。module:模块,YANG定义的模块是一个用作NETCONF基础操作的节点层次树。通过其自身的定义和从其他地方引入的定义,一个模块是自包含且“可编译”的。RPC:远程过程调用,用于NETCONF协议。RPCoperation:NETCONF内部使用的一个特定的RPC调用,也称为协议操作。schemanode:架构树中的节点,可以是container、leaf、leaf-list、list、choice、case、rpc、input、notification、anyxl中的一个。schemanodeindentifier:架构节点标识符,一种标识架构树中一个特定节点的机制。schematree:架构树,模型内部制定的层次树的定义。statedata:状态数据,RFC4741定义的系统内部除配置数据外的只读状态数据和话统指标。submodule:子模块,向模块提供派生类型、分组、数据节点、RPC、通知等等类型定义的模块,一个YANG模块可以由多个子模块组成。top-leveldatanode:一个在它和模块或子模块语句之间,没有其他数据节点的节点。uese:“uses”语句用于实例化分组语句中定义的架构节点集。实例化的节点可以被改进和扩充,以使它们适应任何特定的需要。Mandatory节点以下之一是强制(Mandatory)节点:o包含取值为"true"的"mandatory"的leaf、choice或者anyxml节点,即leaf、choice或者anyxml节点中,包含取值为"true"的"mandatory"语句;o包含取值大于0的"min-elements"语句的list或leaf-list节点,即list或leaf-list节点,包含取值大于0的"min-elements"语句o一个不包含"presence"语句的容器节点,其中至少包含一个mandatory节点作为子节点;YANG概览功能概述YANG是用于NETCONF协议的数据建模语言,一个YANG模块定义了一个可以用于NETCONF基本操作的数据层次结构,包括配置、状态数据、RPCs和通知,这可以完整描述NETCONF客户端和Server间交互的所有数据。YANG将数据的层次结构模型化为一个树,树中每个节点都有一个名字,和一个取值或者一个子节点集合。YANG对每个节点以及节点间的交互提供了清晰的、简洁的描述。YANG将数据模型组织成为模块和子模块。模块可以从其他外部模块导入数据,从子模块中包含数据。允许一个模块向其他模块定义的层次树增加数据节点,从而增强该模块定义的层次结构,同时这种增强操作可以是条件性的,即只有满足特定条件时新的节点才会出现。YANG模型可以描述附加于数据上约束,基于其他节点是否存在或其取值,去限制层次结构中特定节点的出现或者取值。这些约束由Client或Server强制执行,并且有效的内容必须遵从这些约束。YANG定义了一组内置数据类型,并且定义了基于内置类型增加其他类型的派生机制。派生类型可以使用诸如指定范围(range)或模式(pattern)的方式,去限制Client或Server强制要求的,基于的基础类型的取值范围。它们还可以定义派生类型的约定用法,比如包含一个主机名字的基于字符串的类型。YANG允许将节点分组,以便于复用,这些分组的实例可以完善(refine)或扩大(augment)节点,使其可以根据特定需求裁剪节点。派生类型和分组可以定义在单独的模块或子模块中,可以在本地或其他模块以及子模块中引入或包含它。YANG数据层次结构包括定义列表,并且列表的条目可以通过key作为键以区分每个条目。这些列表在用户定义时排序或者被系统自动排序。对于用户排序的列表,定义了用于操纵列表条目顺序的操作。YANG模块可以被转换为一个等效的XML语法,称为YANGIndependentNotation(YIN),使得应用程序可以使用XML解析器或者XSLT(ExtensibleStylesheetLanguageTransformations)脚本,直接操作数据模型。从YANG到YIN的转换是无损的,所以YIN的内容可以往返回YANG。YANG在高层数据建模和底层有线编码间取得了平衡。YANG模块的读者可以看到数据模型的高层视图,同时理解在NETCONF操作中如何编码数据。YANG是一个可以扩展的语言,允许标准机构、供应商和个人定义扩展语句。语句语法允许这些扩展以自然的方式与标准YANG语句共存,而YANG模块中的扩展非常突出,足以使读者注意到它们。YANG抵制去解决所有问题的倾向,而是限定问题空间,以表达NETCONF的数据模型,而不是任意的XML文档或任意的数据模型。YANG描述的数据模型更多的是使得通过NETCONF操作更加轻松。就是说,YANG就是为了建模NETCONF的配置数据和操作。为了可能的扩展,YANG维护了与SNMP(SimpleNetworkManagementProtocol)定义的SMIV2(StructureofManagementInformationversion2[RFC2578],[RFC2579])的兼容性。基于SMIv2的MIB模块可以自动地转换为YANG模块,以进行只读访问。然而,YANG不关心从YANG到SMIv2的反向转换。和NETCONF一样,YANG致力于和设备本机管理基础架构的平滑集成,以允许实现利用其现有的访问机制去保护或者暴露数据模型的元素。语言概述本章介绍YANG语言中一些重要的数据结构,这将有助于描述后续章节中的语言规范。这种渐进的方式处理了YANG的概念和语言内部相互关联的本质。YANG语句和语法从第7章开始详细描述。模块和子模块模块由三种类型的语句组成:模块头语句、修订语句、定义语句。模块头语句描述模块以及模块自身的特定信息;修订语句给出模块的修订记录;定义语句用来定义数据模型,它是模块的主体内容。NETCONFServer也许会实现多个模块,以支持相同数据的不同视图或者设备数据不连续的多个视图。当然,Server也许只实现一个定义了所有可用数据的模块。基于模块所有者的需求,模块可以划分为多个子模块,但是,其外部视图始终是一个模块,不论是否存在子模块或子模块的尺寸大小。"include"语句用于模块或子子模块引用其他子模块的内容,"import"语句用于引用其他模块中定义的。数据建模基础YANG为数据模型定义了四种类型的节点。如下章节中的每个章节中的示例展示了YANG的语法以及其对应的XML表现形式。叶子节点LeafNodes叶子节点包含简单的数据,比如整数或字符串。它只包括一个特定类型的值,并且没有孩子节点。YANG示例:leafhost-name{typestring;description"Hostnameforthissystem";}NETCONFXML示例:<host-name></host-name>第7.6章节介绍"leaf"语句。叶子列表节点Leaf-ListNodes叶子列表节点是一个叶子节点的列表(可以理解为单个类型的元素组成的数组),列表中每个叶子节点有且仅有一个特定类型的值。YANG示例:leaf-listdomain-search{typestring;description"Listofdomainnamestosearch";}NETCONFXML示例:<domain-search></domain-search><domain-search></domain-search><domain-search></domain-search>第7.6章节介绍"leaf-list"语句。容器节点ContainerNodes容器节点用于组织子树中的相关节点,容器节点没有值,只有孩子节点。容器节点可以包含任意类型的任意数目的孩子节点,孩子节点的类型可以是叶子节点、列表节点、容器、以及叶子列表。YANG示例:containersystem{containerlogin{leafmessage{typestring;description"Messagegivenatstartofloginsession";}}}NETCONFXML示例:<system><login><message>Goodmorning</message></login></system>容器节点的描述在7.5章节。列表节点ListNodes列表由一系列列表条目组成,每个条目就像一个结构或记录的实例,并且通过它的键叶叶子的值唯一识别。列表可以指定多个键叶叶子,并且可以包含任意类型的任意数目的孩子节点(节点的类型可以是叶子节点、列表节点、容器节点等等)。YANG示例:listuser{key"name";leafname{typestring;}leaffull-name{typestring;}leafclass{typestring;}}NETCONFXML示例:<user><name>glocks</name><full-name>GoldieLocks</full-name><class>intruder</class></user><user><name>snowey</name><full-name>SnowWhite</full-name><class>free-loader</class></user><user><name>rzell</name><full-name>RapunZell</full-name><class>tower</class></user>列表的定义在7.8章节。模块示例这些语句组合定义了一个模块://Contentsof"acme-system.yang"moduleacme-system{namespace"/system";prefix"acme";organization"ACMEInc.";contact"joe@";description"ThemoduleforentitiesimplementingtheACMEsystem.";revision2007-06-09{description"Initialrevision.";}containersystem{leafhost-name{typestring;description"Hostnameforthissystem";}leaf-listdomain-search{typestring;description"Listofdomainnamestosearch";}containerlogin{leafmessage{typestring;description"Messagegivenatstartofloginsession";}listuser{key"name";leafname{typestring;}leaffull-name{typestring;}leafclass{typestring;}}}}}状态数据YANG可以基于"config"语句为状态数据和配置数据建模,当一个节点被打上"configfalse"标签,那么它的子层次被标志为状态数据,可以通过使用NETCONF的<get>操作上报,而不是<get-config>操作。同时,其父亲容器、列表以及键叶叶子会一起上报,以给出状态数据的上下文信息。在下边这个例子中,每个接口定义了两个叶子节点,一个是配置速度,另一个是观察速度。其中观察速度不是配置,所以它会被NETCONF的<get>操作返回,而不是<get-config>操作。观察速度不是配置数据,因此它不能被<edit-config>操纵。listinterface{key"name";leafname{typestring;}leafspeed{typeenumeration{enum10m;enum100m;enumauto;}}leafobserved-speed{typeuint32;configfalse;}}NETCONFXML示例:<interface><name>feilun</name><speed>auto</speed><observed-speed>10m</observed-speed></interface><interface><name>zhou</name><speed>auto</speed><observed-speed>100m</observed-speed></interface>内置类型YANG语言和其他许多编程语言一样,定义了一组内置类型,但是由于管理域的需求不同,存在一些差异。如下表格汇总了将在第9章介绍的内置类型。名字描述binary任何二进制数据bits位或标志的集合booleantrue或者falsedecimal6464-bit有符号10进制整数empty一个没有任何值的叶子节点enumeration枚举字符串identifyref对抽象标识的引用instance-identifier对数据树节点的引用int88-bit有符号整数int1616-bit有符号整数int3232-bit有符号整数int6464-bit有符号整数leafrefleaf实例的引用string人类可读的字符串uint88-bit无符号整数uint1616-bit无符号整数uint3232-bit无符号整数uint6464-bit无符号整数union成员类型选择类型语句在7.4章节详细介绍。派生类型(typedef)YANG可以使用typedef语句从基础类型派生其他类型,基础类型可以是内置类型或其他派生类型,以允许派生类型的层次结构。派生类型可以用作"type"语句的参数。YANG示例:typedefpercent{typeuint8{range"0..100";}description"Percentage";}leafcompleted{typepercent;}NETCONFXML示例:<completed>20</completed>在7.3章节介绍"typedef"语句。可重用节点组(分组)一组节点可以通过使用"grouping"语句组合成可重用的集合,分组定义了一个通过"uses"实例化的节点集合:groupingtarget{leafaddress{typeinet:ip-address;description"TargetIPaddress";}leafport{typeinet:port-number;description"Targetportnumber";}}containerpeer{containerdestination{usestarget;}}NETCONFXML示例:<peer><destination><address></address><port>830</port></destination></peer> 分组可以在使用时进行改进,使得特定的语句被重写。在下边这个例子中,描述(description)被重写了:containerconnection{containersource{usestarget{refine"address"{description"SourceIPaddress";}refine"port"{description"Sourceportnumber";}}}containerdestination{usestarget{refine"address"{description"DestinationIPaddress";}refine"port"{description"Destinationportnumber";}}}}分组语句在第7.11章节描述。选择YANG允许数据模型使用"choice"和"case"(类似C语音switchcase)将不兼容节点类型分为不同的选择。"choice"语句包含了一组"case"语句,"case"语句定义了不能同时出现的架构节点的集合。每个"case"语句可以包含多个节点,但是"choice"下的每个"case"也可以只包含一个节点。当一个case下的元素被创建时,其他所有case下的元素都被隐式的删除。设备处理该约束的强制执行,以防止与现有配置的不兼容性。choice和case节点都仅出现在架构树中,不会出现在数据树或NETCONF消息中。除了概念性的架构外,不需要额外的层次结构。YANG示例:containerfood{choicesnack{casesports-arena{leafpretzel{typeempty;}leafbeer{typeempty;}}caselate-night{leafchocolate{typeenumeration{enumdark;enummilk;enumfirst-available;}}}}}NETCONFXML示例:<food><pretzel/><beer/></food>"choice"语句在7.9章节中描述。扩展数据模型(augment)YANG允许模块将其他节点插入到数据模型中,包括当前模块(或它的子模块)或者外部模块。例如,这对供应商很有价值,使得供应商可以以可互操作的方式将其特定的参数添加到标准数据模型中。YANG语言的"augment"语句定义了数据模型层次结构中插入其他节点的位置,并且"when"语句指定了在什么条件下,新的节点是有效的。YANG示例:augment/system/login/user{when"class!=’wheel’";leafuid{typeuint16{range"1000..30000";}}}在上边的例子中,当user的"class"不是"wheel"时,插入"uid"节点。如果一个模块扩充(augments)了其它模块,则数据的XML呈现将反应扩展定义所在模块的前缀。比如,如果上面的扩充内容在一个以"other"为前缀的模块中,则相应的XML将如下所示:NETCONFXML示例:<user><name>alicew</name><full-name>AliceN.Wonderland</full-name><class>drop-out</class><other:uid>1024</other:uid></user>"augment"语句的描述在7.15章节。RFC定义YANG允许定义NETCONFRPCs。使用YANG的数据定义语句,可以对操作名字、输入参数、输出参数进行建模。YANG示例:rpcactivate-software-image{input{leafimage-name{typestring;}}output{leafstatus{typestring;}}}NETCONFXML示例:<rpcmessage-id="101"xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><activate-software-imagexmlns="/system"><image-name>acmefw-2.3</image-name></activate-software-image></rpc><rpc-replymessage-id="101"xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><statusxmlns="/system">Theimageacmefw-2.3isbeinginstalled.</status></rpc-reply>"rpc"语句的介绍在7.13章节。通知定义YANG允许定义适合于NETCONF的通知,YANG的数据定义语句可以用于为通知的内容进行建模。YANG示例:notificationlink-failure{description"Alinkfailurehasbeendetected";leafif-name{typeleafref{path"/interface/name";}}leafif-admin-status{typeadmin-status;}leafif-oper-status{typeoper-status;}}NETCONFXML示例:<notificationxmlns="urn:ietf:params:netconf:capability:notification:1.0"><eventTime>2007-09-01T10:00:00Z</eventTime><link-failurexmlns="/system"><if-name>so-1/2/3.0</if-name><if-admin-status>up</if-admin-status><if-oper-status>down</if-oper-status></link-failure></notification>"notification"在7.14章节描述。语言概念模块和子模块模块是YANG定义的基本单元,一个模块定义了一个单独的数据模型,一个模块可以定义一个完整的、内聚的模型(不依赖于第三方模块),或者使用其他节点扩充(augment)现有的数据模型。子模块(Submodule)是为模块提供定义的部分模块(partialmodule),一个模块也许包含任意数量的子模块,但是每个子模块仅能属于一个模块。所有模块和子模块的名字必须是唯一的,因此建议企业模块的开发者为他们的模块选择的名字要尽量降低与其他厂商的模块或者标准模块的名字冲突的概率。比如,使用企业名称或组织名称作为模块名字的前缀。模块使用"include"语句包含它的子模块,并且使用"import"语句引用外部的模块。同样地,子模块使用"import"引用其他模块,并且使用"include"引用模块内部的其他子模块。一个模块或子模块不得包含其他模块的子模块,并且子模块不得import它自身的模块。"include"语句是包含,"import"语句是导入,可以包含属于同一模块内部的子模块,而外部模块要用导入语句。import和include语句用于将定义对其他模块或子模块变得可用:o模块或子模块引用外部模块的定义时,外部模块必须被导入(imported)o一个模块要引用自身子模块内的定义,它必须包含(include)这个子模块o一个子模块要引用二级子模块内部的定义,该子模块必须包含(include)该二级子模块禁止出现任何import或include的循环链。例如,如果子模块a包含了子模块b,则b不能包含a。当外部模块的一个定义被引用时,必须为其使用一个本地定义的前缀,并紧跟":",然后是外部标识符。引用本地模块的定义也可以使用前缀表示法。因为内置的数据属性不属于任何模块,并且没有前缀,引用内置数据类型(比如int32)不能使用前缀表示法。模块和子模块发布的模块会随着时间独立演进。为了允许这种演进,模块需要使用特定的修订版本被导入。当编写模块时,它使用了其他模块当时可用的修订版本。随着被导入模块新修订版本的发布,importing模块(导入其他模块的模块)不受影响,它的内容保持不变。当模块的作者准备迁移到被导入模块的最新发布的修订版本时,该模块可以通过更新的import语句重新发布。通过指定新的修订重发布模块,作者显式地指示重发布的模块接受了被导入模块的任何变化。对于子模块,问题是相似的。一个包含其他子模块的模块或子模块,需要指定被包含模块的修订版本号。如果一个子模块发生了变更,任何包含它的模块或子模块都需要进行更新。比如,模块"b"导入了模块"a":modulea{revision2008-01-01{...}groupinga{leafeh{}}}moduleb{importa{prefixp;revision-date2008-01-01;}containerbee{usesp:a;}}当模块"a"的作者发布了新的修订版本时,修订变化对于模块"b"的作者也许是不可接受的。如果此次修订可以接受,则模块"b"的作者可以通过更新"import"语句中的修订版本,从而重新发布模块"b"。模块层次(ModuleHierarchies)YANG允许多层次的数据模型,其中数据也许会包含多对于一个顶层节点。有多个顶层节点的模型有时是方便的,所以被YANG所支持。NETCONF能够在<config>和<data>元素中携带任何XML内容作为有效载荷,YANG模块的顶级节点可以以任意顺序编码为这些元素的孩子元素,这封装保证了相应的NETCONF消息总是良构的XML文档。举例来说:modulemy-config{namespace"/schema/config";prefix"co";containersystem{...}containerrouting{...}}在NETCONF中编码为:<rpcmessage-id="101"xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><running/></target><config><systemxmlns="/schema/config"><!--systemdatahere--></system><routingxmlns="/schema/config"><!--routingdatahere--></routing></config></edit-config></rpc>文件布局YANG模块和子模块通常情况下保存在文件中,一般是一个模块或子模块一个文件。文件的名字应该是如下格式:module-or-submodule-name[’@’revision-date](’.yang’/’.yin’)YANG编译器通过此约定找到被导入(imported)或被包含(included)的子模块。YANG语言定义模块时,工具可能出于性能和管理上的原因,单独编译子模块。错误和告警在单独编译子模块可能无法检测出来,从而推迟到子模块被链接进内聚的模块时为止。XML名称空间(Namesapce)YANG指定的所有的定义都被绑定到一个特定的XML名称空间(参考[XML-NAMES]),它是一个全球唯一的URI(参考[RFC3986])。NETCONF客户端或Server对数据进行XML编码时使用该名称空间。RFCstream[参考RFC4844]中发布的模块的命名空间必须由IANA分配,请参见第14节。私有模块的名称空间由模块所属的组织自己分配,而不需要到INAN这种集中的管理机构登记。名称空间URI必须避免标准或其他公司的名称空间冲突,比如通过在名称空间中使用企业或组织的名字。“名称空间”在7.1.3章节中描述。YANGXML名称空间YANG为NETCONF<edit-config>操作和<error-info>内容定义了一个XML名字空间。名字空间的名字是:"urn:ietf:params:xml:ns:yang:1"。解析组、类型和标识名称分组、类型和标识名称在其定义的上下文中解析,而不是它们使用的上下文。分组(groupings)、类型定义(typedefs)、和标识名称(identities)的使用者不需要导入或包含子模块去满足原始定义所做的所有引用。这一点类似于传统编程语言中的静态作用域。举例来说,如果一个模块定义了一个分组,其中一个类型是引用而来,当这个分组被用在二级模块中时,这个引用来的类型在原始模块的上下文中解析,而不是在二级模块中。不必担心冲突,即使两个模块都定义了这个类型,因为没有歧义。嵌套类型定义和分组类型定义(typedefs)和分组(groupings)可以嵌套在许多YANG语句之下,使得这些类型定义和分组的范围限定在他们出现的层次结构下。这使得类型定义和分组可以定义在靠近使用他们的地方,而不是将他们放置于层次的顶层。在临近使用的地方定义,增加了可读性,此称为范围界定(scoping)。范围界定也允许类型定义,而不必担心不同子模块间不同类型名称的冲突问题。可以在不添加旨在防止大型模块内名称冲突的前导字符串的情况下指定类型名称。最后,范围界定使得模块作者将类型定义和分组保持为模块或子模块自有,以防止重用。因为只有顶层(top-level)的类型和分组(即作为模块的子语句或者子模块语句)可以被外部模块或子模块使用,开发者可以更好的控制,他们的模块的哪些部分可以对外部世界呈现,支持隐藏内部信息的需要,并在什么东西可以共享给外部世界和什么东西要保持私有之间维护一个界限。范围界定不能shadow上层的定义(类似C语言用同名的局部变量在不同层级的作用域重定义,NETCONF不允许这样使用)。一个类型或分组不能被定义,如果架构层次内的上个层次已经定义了一个相同的标识符。对一个不带前缀的类型或分组的引用,或者使用了当前模块的前缀,采用就近解析的原则:在其引用位置所在的每个祖先的每个子语句中查找匹配的类型定义(typedef)或分组语句(grouping)。一致性一致性是对一个设备满足模型准确程度的度量。一般来说,设备有责任忠实的实现模型,使得应用程序同等地对待实现模型的每一个设备。对模型实现的偏差会降低模型的实用性,同时会增加使用该模型的应用程序的脆弱性。YANG模型作者要从三个方面考虑一致性:o模型的基本行为o模型的可选特性o相对模型的偏差我们将逐一讨论每个方面。基本行为模型在NETCONF客户端和Server间建立了一个合同,使得双方确信对方了解模型化数据背后的语法和语义。YANG的强度(strength)建立在这份合同的强度上(strength)。可选特性在许多模型中,建模者允许模型的某些部分是条件性的,设备自身控制模型中这些条件性的部分是否支持或者有效。比如,一个syslog数据模型也许选择包括本地保存日志的能力,但是建模者需要意识到只有设备有本地存储时才是可能的。如果没有本地存储,则应用程序不应该让设备去保存日志。YANG使用一个称为"feature"的结构支持支持这种条件选择机制。特性给建模者提供了以一种被设备自身控制的方式将模型的部分制作为条件性的一种机制。模型可以表达不总是出现在所有设备中的构件(constructs)。这些特性(features)被包含在模型的定义中,以实现一致性视图,使得应用程序学习到设备支持哪些特性,并据此定制他们对设备的行为。模块可以声明任意数量的特性,通过简单的字符串作为特性的标识,并且基于这些特性使得模型的某些部分成为可选的。如果设备支持一个特性,则模型中对应的部分对该设备就是有效的。如果设备不支持该特定,模型的那些部分就是无效的,并且应用程序应该做出相应的行为。特性使用"feature"语句定义,模块中对特性的条件性的定义通过"if-feature"语句指出,并将特性的名字作为其参数。特性的细节进一步参考章节7.18.1。偏差在一个理想的世界里,所有的设备被要求按照模型的定义精确的实现模型,并且相对模型的偏差是不被允许的。但是,在现实世界里,设备经常无法按照模型所写的那样实现它。为了使基于YANG的自动化环境能够处理这些设备偏差,必须为设备提供一种机制,以将此类偏差的具体情况通知应用程序。比如,一个BGP模块也许允许任意数量的BGP对端,但是一个特定设备exuberant只支持16个BGP对端。任何配置第17个对端的应用都将收到一个错误。虽然错误足以使应用程序了解到它不能增加另一个对端实体,如果应用程序能够事先知道这个限制,并且能够防止用户继续尝试不可能成功的路径,那就更好了。设备偏差通过"deviation"语句声明,该语句将标识架构树中节点的字符串作为其参数。语句的内容详细说明了设备实现与模型中定义的合同的偏差的方式。进一步信息可以参考章节7.18.3。在<hello>中通告一致性信息名称空间必须在<hello>消息中作为一个能力(capability)通告给对端,表明NETCONFServer支持YANG模型。通告的能力URI的格式必须如下:capability-string=namespace-uri[parameter-list]parameter-list="?"parameter*("&"parameter)parameter=revision-parameter/module-parameter/feature-parameter/deviation-parameterrevision-parameter="revision="revision-datemodule-parameter="module="module-namefeature-parameter="features="feature*(","feature)deviation-parameter="deviations="deviation*(","deviation)其中"revision-date"是NETCONFServer实现的模型的修订版本(参见7.1.9章节),"module-name"是模块的名字,其取值和"module"语句一样(参见7.1章节);"namespace-uri"是模块的名称空间,取值和"namespace"语句一样(参考7.1.3章节);"feature"是设备实现的可选特定的名字(参见7.18.1章节);"deviation"是模块定义的设备偏差的名字(参考7.18.3)。在parameterlist里,每个命名参数最多出现一次。译者注:下边的例子中给出的parameter分隔符号为";",而不是"&",应该是"&"。模块Server通过<hello>消息指示其支持的模块的名字。模型名称空间在能力字符串中被编码为基本URI,并且模块的名字被编码为基本URI的"module"的参数。Server必须通告其实现的所有模型的所有修订版本。比如,下边的<hello>消息通告了一个模块"syslog"。<helloxmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><capability>/syslog?module=syslog&revision=2008-04-01</capability></hello>特性Server通过<hello>消息指示其支持的特性(feature)的名字。在<hello>消息里,特性被编码在URI的"features"参数里。该参数的取值是一个以’,’分隔的特定模块锁支持的特性名字的列表。比如,下边的<hello>消息通告了一个模块,通知Client,Server支持模块"syslog"的"local-storage"特性。<helloxmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><capability>/syslog?module=syslog&features=local-storage</capability></hello>偏差设备偏差通过"deviations"参数通告。"deviations"参数的取值是一个’,’分隔的模块列表,列表中每个模块包含着能力模块的偏差。比如,下边的<hello>消息通告了两个模块,通知Client根据"my-devs"模块中列出的偏差,它偏离于"syslog"模块。<helloxmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><capability>/syslog?module=syslog&deviations=my-devs</capability><capability>/my-deviations?module=my-devs</capability></hello>数据存储修改数据模型也许允许Servr改变配置数据存储,而且并不是显式地通过NETCONF协议消息这种方式。比如,一个数据模型也许定义叶子节点,当Client不提供这些节点的值时,则以系统生成值进行赋值。为这种情况指定一个正式的机制,哪种情况允许这种改变,超出了本规范的范围。YANG语法YANG语法和SMIng[RFC3780]以及诸如C/C++等编程语言类似。为了可读性特意选择了类似C语言的语法,因为YANG重视模型读者的时间和精力,而不是模块作者和YANG工具链开发人员的时间和精力。本章节介绍了YANG的语法规范。YANG模块使用UTF-8[RFC3629]编码。词汇标记(LexicalTokenization)YANG模块被解析成一系列的token。本节详细介绍从一个输入流中识别出token的规则。YANG的token化(词语切分)规则是简单而高效的。这种简单性的驱动力来自于保持解析器更容易实现,而高效的驱动力来自于模型作者需要将他们的模型表达为具备可读性的形式的事实。注释(Comments)注释类似C++风格,单行注释用"//"发起,到行尾结束。块注释(跨行)包含在"/*"和"*/"之间。TokensYANG中的token是一个关键字、一个字符串、一个分号";"或者一个大括号("{"or"}")。字符串可以被或不变双引号括起来,关键字或者是本文档定义的YANG的关键字,或者是一个前缀ID,紧跟":",后跟语言扩展关键字,关键字是大小写敏感的,参考6.2章节关于ID的正式定义。使用引号(Quoting)如果一个字符串包含空格或者tab字符、分号(";")、大括号("{"or"}"),或者注释序列("//","/*",or"*/"),则它必须被双引号或引号括起来。如果双引号字符串包含一个换行符,后跟用于根据YANG的文件布局进行缩进的空格或制表符(tab),这些前导空白字符(空格和tab)在解析时将从字符串中删除,直到并包含双引号字符的列,或者知道第一个非空白字符,以先发生的为准。在此过程中,tab字符按照8个空格字符处理。如果双引号字符串在换行前包含空格或tab字符,则这些尾部空白字符也将从字符串中删除。单引号字符串(包含在’’)保留引号内的每个字符。一个单引号字符不能出现在单引号字符串里,即使其前面有反斜杠("/")。在双引号字符串里,一个反斜杠("/")字符引入一个特殊字符,含义依赖于反斜杠后跟的字符:\n:换行\t:tab字符\":双引号字符\\:反斜杠字符如果双引号字符串后跟一个加号字符("+"),并后跟另一个双引号字符串,则两个字符串拼接为一个字符串,允许多次拼接构建一个字符串。双引号字符串中空白字符的删除以及反斜转义字符的替换,在拼接前完成。引号示例如下的字符串是等价的:hello"hello"’hello’"hel"+"lo"如下的例子显示了一些特殊的字符串:"\""–包含双引号的字符串’"’-包含双引号的字符串"\n"–包含换行符的字符串’\n’–包含一个反斜杠后跟字符n的字符串如下的例子展示了一些非法的字符串:’’’’–单引号字符串不能包含单引号"""-双引号字符串中的双引号必须用反斜杠转义如下的字符串是等价的:"firstlinesecondline""firstline\n"+"secondline"ID(Identifiers)标识符(Identifiers)通常用名字来标识不同种类YANG项(items)。每个ID以大写或小写ASCII字母或者下划线字符开头,后跟0个或多个ACSII字母、数字、下划线字符、连字符或点字符。实现必须支持长度不超过64的标识符。标识符是大小写敏感的,标识符的语法规则在第12章中定义,标识符可以指定为带或不带双引号的字符串。标识符和其名字空间每个标识符在名称空间内是有效的,该名称空间取决于所定义的YANG项的类型。定义在名称空间里的所有标识符必须是唯一的。o所有模块和子模块的名字共享相同的全局模块标识符名称空间o定义在模块和其子模块中的所有扩展名字共享相同的扩展标识符名称空间o定义在模块和其子模块中的所有特性(feature)的名字共享相同的特性标识符名称空间o定义在模块和其子模块中的所有标识(identity)的名字共享相同的标识符名称空间o定义在父亲节点下或模块或其子模块顶层的所有派生类型的名字,共享相同的类型标识符名称空间。此名称空间的作用域是父节点或模块的所有子节点,这意味着任何后代节点在使用typedef时,不得定义相同名字的类型。o定义在父亲节点或模块或其子模块顶层的所有分组名字共享相同的分组标识符名称空间。此名称空间的作用域是父节点或模块的所有子节点,这意味后代节点不能定义重名的分组名字。o定义在父亲节点或模块或其子模块顶层的所有的叶子、叶子列表、列表、容器、选择、rpcs、通知以及anyxml被定义(字节或通过uese语句)在相同的标识符名称空间。此名称空间的作用域是父亲节点或模块,除非父亲节点是case节点,在这种情况下,名称空间的作用域是最近的祖先节点,而不是case或choice节点。ochoice语句中的所有case共享相同的标识符名称空间。该名称空间的作用域是父亲choice节点。前向引用在YANG中是允许的。语句YANG模块包含一系列语句,每个语句以关键字开始,跟随0个或多个参数,再跟分号(";")或者以大括号("{}")包围的子语句块:statement=keyword[argument](";"/"{"*statement"}")其中argument是字符串,参考6.1.2章节。语言扩展模块可以通过使用"extension"关键字(参见7.17章节)引入YANG扩展。扩展可以被其他模块通过"import"语句导入(参见7.1.5章节)。当使用一个导入的扩展时,扩展的关键字必须使用被引入的扩展模块前缀进行限定。如果一个扩展在其定义的模块使用,则扩展关键字必须被模块自身的前缀限定。因为子模块不能包含父模块,模块中任何要暴露给其他模块的扩展,必须定义在子模块中,然后子模块包含这个子模块以找到该扩展的定义。如果YANG编译器不能支持一个特定的扩展,其以作为一个未知语句(第12章)出现在YANG模块中,则真个未知语句将被编译器忽略。XPath评估YANG依赖于XMLPath语言1.0[XPATH]作为指定多个内部节点间引用和依赖的标记方法。NETCONF客户端和Server不是必须要实现Xpath解释器,但是必须保证编码在数据模型中的需求能够被执行。执行的方式由具体的实现确定。XPath表达式必须是句法正确,并且所有前缀必须出现在Xpath上下文中(参考6.4.1)。实现可以选择手动实现他们,而不是直接使用Xpath表达式。XPath表达式中使用的数据模型和XPath1.0[XPATH]是一样的,并且根节点的孩子节点的扩展和XSLT1.0[XSLT](章节3.1)是一样的。特别地,它意味着根节点可以有任意数量的元素作为其子节点。XPath上下文所有的YANGXPath表达式共享相同的XPath上下文定义:o名称空间声明的集合是所有导入语句(import)前缀和模块中XPath表达式指定的名称空间对的集合,以及为"namespace"语句的URI的"prefix"语句的前缀。o不带名称空间前缀的名字和当前节点的标识符属于相同的名称空间。在组内部,名称空间受组使用位置的影响(参考7.12章节)。o函数库是[XPATH]定义的核心函数库,并且一个"current()"函数返回带有初始节点上下文的集合。o变量绑定集为空。处理不带前缀的名字的机制参考XPath2.0[XPATH2.0],并且帮助YANG简化XPath表达式。由于YANG节点标识符始终是具有非空名称空间URI的限定名称,因此不会产生任何歧义。上下文节点随YANGXPath表达式而变化,并且也由带XPath表达式的YANG语句所定义的位置而定。架构(Schema)节点标识符架构节点标识符是一个字符串,它标识了架构树中的一个节点。它有两种形式:"absolute"和"descendant",分别由第12章中的"absolute-schema-nodeid"和"descendant-schema-nodeid"定义。一个架构节点标识符由一个标识符的路径组成,路径上的每个节点通过斜杠分隔("/")。在一个absolute架构节点标识符中,在前导斜杠后的第一个标识符是本地模块或所有导入模块的任何一个顶层(top-level)架构节点。引用外部模块定义的标识符必须通过合适的前缀限定,并且对本模块或其子模块定义的标识符的应用,也可以使用前缀。举例来说,为了表示顶层节点"a"的孩子节点"b",可以使用字符串"/a/b"。YANG语句以下章节将详细介绍YANG语言的语句。需要注意的是,即使YANG定义的语句没有包含子语句,但是厂商可以通过定义扩展作为其子语句。比如,YANG没有为"description"语句定义子语句,但是下边的语句是合法的:description"sometext"{acme:documentation-flag5;}module语句"module"语句定义了模块的名字,将属于同一模块的所有语句组织在一起。"module"语句的参数是模块的名字,后跟模块详细信息的子语句块。模块名字的定义遵循6.2章节定义的标识符的规则。在RFCstreams[RFC4844]中发布的模块名字必须由IANA分配(参考第14章)。私有模块的名字由模块所属的组织分配,而不需到注册中心登记。选择模块名字时推荐选择与标准或其他厂商模块冲突可能性小的名字。比如使用组织或公司的名字作为模块名字的前缀。一个典型的模块的布局如下:module<module-name>{//headerinformation<yang-versionstatement><namespacestatement><prefixstatement>//linkagestatements<importstatements><includestatements>//metainformation<organizationstatement><contactstatement><descriptionstatement><referencestatement>//revisionhistory<revisionstatements>//moduledefinitions<otherstatements>}模块的子语句子语句章节基数anyxml7.100..naugment7.150..nchoice7.90
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 资助学生的感谢信(15篇)
- 语文教研活动总结合集15篇
- 二年级数学100以内加减法竖式计算题单元练习习题大全附答案
- 徐州骏宏通集装箱制造有限公司年产2000个集装箱技改项目环境影响报告表
- 红领巾奖章个人事迹
- 银行工作人员工作总结20篇
- 药店防潮知识
- 2025喜乐元宵互动亲子国潮元宵佳节正月十五民俗主题活动策划方案
- 家庭教育中责任心的重要性与培养方法
- 道路交通安全标志课件
- 钣金结构件点检表
- 医疗安全(不良)事件汇总登记表(科室)
- 电子商务专升本考试(习题卷6)
- 铸造企业采购流程及管理制度
- 胸痛中心培训考试题(ACS医护人员版)附有答案附有答案
- 《村寨里的纸文明 中国少数民族剪纸艺术传统调查与研究 第三卷 》读书笔记
- 2023年副主任医师(副高)-皮肤与性病学(副高)考试历年真题拔高带答案必考
- 安全生产费用归集清单(安措费清单)
- 广东省五年一贯制语文考试题目
- 江苏省南京市2023届高三第一学期期初考试英语试题和答案
- 10kV电力线路改造工程量清单
评论
0/150
提交评论