odl源码系列一restconf模块
odl北向接口基本是通过yang文件来定义,而北向接收到请求先得经过转化成标准的yang模型的api,再调用md-sal相关接口
入口项目为netconf 项目的restconf-nb-rfc8040,之前老的restconf-nb-bierman02已经废弃
入口类为JSONRestconfServiceRfc8040Impl, 这个类作为总北向入口,通过ServiceWrapper 作为代理来分发Put,get,delete,patch,post
invokeRpc等操作
构造器
@Injectpublic JSONRestconfServiceRfc8040Impl(final ServicesWrapper services,final DOMMountPointServiceHandler mountPointServiceHandler,final SchemaContextHandler schemaContextHandler) {this.services = services;this.mountPointServiceHandler = mountPointServiceHandler;this.schemaContextHandler = schemaContextHandler;}
invokeRpc
@SuppressWarnings("checkstyle:IllegalCatch")@SuppressFBWarnings(value = "NP_NULL_PARAM_DEREF", justification = "Unrecognised NullableDecl")@Overridepublic Optional<String> invokeRpc(final String uriPath, final Optional<String> input)throws OperationFailedException {requireNonNull(uriPath, "uriPath can't be null");final String actualInput = input.isPresent() ? input.get() : null;LOG.debug("invokeRpc: uriPath: {}, input: {}", uriPath, actualInput);String output = null;try {# 将uripath转化成标准的yang模型相关的上下文final NormalizedNodeContext inputContext = toNormalizedNodeContext(uriPath, actualInput, true);LOG.debug("Parsed YangInstanceIdentifier: {}", inputContext.getInstanceIdentifierContext().getInstanceIdentifier());LOG.debug("Parsed NormalizedNode: {}", inputContext.getData());# 实际经过多层解析,会调用md-sal相关api来查找实际rpc地址,并调用 final NormalizedNodeContext outputContext =services.invokeRpc(uriPath, inputContext, new SimpleUriInfo(uriPath));if (outputContext.getData() != null) {output = toJson(outputContext);}} catch (RuntimeException | IOException e) {propagateExceptionAs(uriPath, e, "RPC");}return Optional.ofNullable(output);}
一.解析url
首先会先通过url找到对应yang schema然后转换成对应Yang对应的上下文, 再转化成标准Node 的上下文
private NormalizedNodeContext toNormalizedNodeContext(final String uriPath, final @Nullable String payload,final boolean isPost) throws OperationFailedException {// 解析成yang模型上下文final InstanceIdentifierContext<?> instanceIdentifierContext = ParserIdentifier.toInstanceIdentifier(uriPath, schemaContextHandler.get(), Optional.of(mountPointServiceHandler.get()));if (payload == null) {return new NormalizedNodeContext(instanceIdentifierContext, null);}final InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));// 转换成标准请求的上下文实例return JsonNormalizedNodeBodyReader.readFrom(instanceIdentifierContext, entityStream, isPost);}
解析url,判断是否是mountPoint挂载点,如果是的话解析mountpoint yang id 和schemaContext,如果不是直接通过identifier 和schemaContext来解析生成yang模型标识,
schemaContext这个里面有所有yang文件定义解析出来的定义
public static InstanceIdentifierContext<?> toInstanceIdentifier(final String identifier,final SchemaContext schemaContext,final Optional<DOMMountPointService> mountPointService) {if (identifier == null || !identifier.contains(RestconfConstants.MOUNT)) {return createIIdContext(schemaContext, identifier, null);}if (!mountPointService.isPresent()) {throw new RestconfDocumentedException("Mount point service is not available");}final Iterator<String> pathsIt = MP_SPLITTER.split(identifier).iterator();final String mountPointId = pathsIt.next();final YangInstanceIdentifier mountPath = IdentifierCodec.deserialize(mountPointId, schemaContext);final DOMMountPoint mountPoint = mountPointService.get().getMountPoint(mountPath).orElseThrow(() -> new RestconfDocumentedException("Mount point does not exist.",ErrorType.PROTOCOL, ErrorTag.DATA_MISSING));final SchemaContext mountSchemaContext = mountPoint.getSchemaContext();final String pathId = pathsIt.next().replaceFirst("/", "");return createIIdContext(mountSchemaContext, pathId, mountPoint);}
createIIdContext
private static InstanceIdentifierContext<?> createIIdContext(final SchemaContext schemaContext, final String url,final @Nullable DOMMountPoint mountPoint) {final YangInstanceIdentifier urlPath = IdentifierCodec.deserialize(url, schemaContext);return new InstanceIdentifierContext<>(urlPath, getPathSchema(schemaContext, urlPath), mountPoint,schemaContext);}
IdentifierCodec.deserialize(url, schemaContext) 会查找schemaContext里面跟urlpath对应yang 具体定义,然后生成
YangInstanceIdentifier,感兴趣的伙伴可以伸入看一下源码,这里就不做过多介绍
解析出来YangInstanceIdentifier,下一步就是将utl 参数和input(http body 如果有的话)和YangInstanceIdentifier一一对应起来
返回一个NormalizedNodeContext
public static NormalizedNodeContext readFrom(final InstanceIdentifierContext<?> path, final InputStream entityStream, final boolean isPost) {....final JsonParserStream jsonParser = JsonParserStream.create(writer,JSONCodecFactorySupplier.RFC7951.getShared(path.getSchemaContext()), parentSchema);final JsonReader reader = new JsonReader(new InputStreamReader(entityStream, StandardCharsets.UTF_8));jsonParser.parse(reader);NormalizedNode<?, ?> result = resultHolder.getResult();final List<YangInstanceIdentifier.PathArgument> iiToDataList = new ArrayList<>();InstanceIdentifierContext<? extends SchemaNode> newIIContext;while (result instanceof AugmentationNode || result instanceof ChoiceNode) {final Object childNode = ((DataContainerNode<?>) result).getValue().iterator().next();if (isPost) {iiToDataList.add(result.getIdentifier());}result = (NormalizedNode<?, ?>) childNode;}if (isPost) {if (result instanceof MapEntryNode) {iiToDataList.add(new YangInstanceIdentifier.NodeIdentifier(result.getNodeType()));iiToDataList.add(result.getIdentifier());} else {iiToDataList.add(result.getIdentifier());}} else {if (result instanceof MapNode) {result = Iterables.getOnlyElement(((MapNode) result).getValue());}}final YangInstanceIdentifier fullIIToData = YangInstanceIdentifier.create(Iterables.concat(path.getInstanceIdentifier().getPathArguments(), iiToDataList));newIIContext = new InstanceIdentifierContext<>(fullIIToData, path.getSchemaNode(), path.getMountPoint(),path.getSchemaContext());return new NormalizedNodeContext(newIIContext, result);}
二.调用实际rpc接口
解析完成后,会调用ServicesWrapper的对应put,get, post, patch, invokeRpc方法
下面为ServiceWrapper invokeRpc方法
@Overridepublic NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload,final UriInfo uriInfo) {return this.delegRestconfInvokeOpsService.invokeRpc(identifier, payload, uriInfo);}
而delegRestconfInvokeOpsService在ServiceWrapper构造器赋值
public static ServicesWrapper newInstance(final SchemaContextHandler schemaCtxHandler,final DOMMountPointServiceHandler domMountPointServiceHandler,final TransactionChainHandler transactionChainHandler, final DOMDataBrokerHandler domDataBrokerHandler,final RpcServiceHandler rpcServiceHandler, final ActionServiceHandler actionServiceHandler,final NotificationServiceHandler notificationServiceHandler, final DOMSchemaService domSchemaService) {RestconfOperationsService restconfOpsService = new RestconfOperationsServiceImpl(schemaCtxHandler,domMountPointServiceHandler);final DOMYangTextSourceProvider yangTextSourceProvider = domSchemaService.getExtensions().getInstance(DOMYangTextSourceProvider.class);RestconfSchemaService restconfSchemaService = new RestconfSchemaServiceImpl(schemaCtxHandler,domMountPointServiceHandler, yangTextSourceProvider);RestconfStreamsSubscriptionService restconfSubscrService = new RestconfStreamsSubscriptionServiceImpl(domDataBrokerHandler, notificationServiceHandler, schemaCtxHandler, transactionChainHandler);RestconfDataService restconfDataService = new RestconfDataServiceImpl(schemaCtxHandler, transactionChainHandler,domMountPointServiceHandler, restconfSubscrService, actionServiceHandler);RestconfInvokeOperationsService restconfInvokeOpsService = new RestconfInvokeOperationsServiceImpl(rpcServiceHandler, schemaCtxHandler);RestconfService restconfService = new RestconfImpl(schemaCtxHandler);return new ServicesWrapper(restconfDataService, restconfInvokeOpsService, restconfSubscrService,restconfOpsService, restconfSchemaService, restconfService);}
ServiceWrapper几个成员变量作用
yang文件有关的查询接口
RestconfOperationsService 提供查询yang文件有哪些操作的接口
RestconfSchemaService 提供查询yang文件定义的接口
RestconfService 提供查询当前yang lib库版本的接口
datastore和rpc操作相关的接口
RestconfStreamsSubscriptionService 北向接口定阅
RestconfDataService 操作datastorestore接口
RestconfInvokeOperationsService operations相关操作接口
实际调用rpc
public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload,final UriInfo uriInfo) {......if (mountPoint == null) {if (namespace.equals(RestconfStreamsConstants.SAL_REMOTE_NAMESPACE.getNamespace())) {if (identifier.contains(RestconfStreamsConstants.CREATE_DATA_SUBSCRIPTION)) { // 创建websocket监听流response = CreateStreamUtil.createDataChangeNotifiStream(payload, refSchemaCtx);} else {throw new RestconfDocumentedException("Not supported operation", ErrorType.RPC,ErrorTag.OPERATION_NOT_SUPPORTED);}} else {// 调用rpcresponse = RestconfInvokeOperationsUtil.invokeRpc(payload.getData(), schemaPath,this.rpcServiceHandler);}schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());} else {// 调用mountpoint rpcresponse = RestconfInvokeOperationsUtil.invokeRpcViaMountPoint(mountPoint, payload.getData(), schemaPath);schemaContextRef = new SchemaContextRef(mountPoint.getSchemaContext());}......}
到这里真正去调用md-sal来执行真正的rpc service
通过md-sal提供的DOMRpcService来调用
public static DOMRpcResult invokeRpc(final NormalizedNode<?, ?> data, final SchemaPath schemaPath,final RpcServiceHandler rpcServiceHandler) {final DOMRpcService rpcService = rpcServiceHandler.get();if (rpcService == null) {throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);}final ListenableFuture<DOMRpcResult> rpc = rpcService.invokeRpc(schemaPath, data);return prepareResult(rpc);}
odl restconf大概执行流程就是这样,关于DOMRpcService怎样查找真正rpc 实现,以及某个rpc service如何注册到md-sal,下一期会讲一下
odl源码系列一restconf模块相关推荐
- 源码解读_入口开始解读Vue源码系列(二)——new Vue 的故事
作者:muwoo 转发链接:https://github.com/muwoo/blogs/blob/master/src/Vue/2.md 目录 入口开始解读Vue源码系列(一)--造物创世 入口开始 ...
- [darknet源码系列-3] 在darknet中,如何根据解析出来的配置进行网络层构建
[darknet源码系列-3] 在darknet中,如何根据解析出来的配置进行网络层构建 FesianXu 20201120 at UESTC 前言 笔者在[1,2]中已经对darknet如何进行配置 ...
- 大白话Vue源码系列(01):万事开头难
阅读目录 Vue 的源码目录结构 预备知识 先捡软的捏 Angular 是 Google 亲儿子,React 是 Facebook 小正太,那咱为啥偏偏选择了 Vue 下手,一句话,Vue 是咱见过的 ...
- Android xUtils3源码解析之图片模块
本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...
- 源码系列第1弹 | 带你快速攻略Kafka源码之旅入门篇
大家过年好,我是 华仔, 又跟大家见面了. 从今天开始我将为大家奉上 Kafka 源码剖析系列文章,正式开启 「Kafka的源码之旅」,跟我一起来掌握 Kafka 源码核心架构设计思想吧. 今天这篇我 ...
- Android xUtils3源码解析之注解模块
本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...
- Android xUtils3源码解析之数据库模块
本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...
- SpringMVC源码系列:HandlerMapping
SpringMVC源码系列:HandlerMapping SpringMVC源码系列:AbstractHandlerMapping HandlerMapping接口是用来查找Handler的.在Spr ...
- elasticsearch源码分析之search模块(server端)
elasticsearch源码分析之search模块(server端) 继续接着上一篇的来说啊,当client端将search的请求发送到某一个node之后,剩下的事情就是server端来处理了,具体 ...
- elasticsearch源码分析之search模块(client端)
elasticsearch源码分析之search模块(client端) 注意,我这里所说的都是通过rest api来做的搜索,所以对于接收到请求的节点,我姑且将之称之为client端,其主要的功能我们 ...
最新文章
- git fetch和git pull
- Win64 驱动内核编程-17. MINIFILTER(文件保护)
- C语言 显示数组元素的值和地址
- 两经纬度之间的距离计算
- 利用java反射调用类的的私有方法
- Windows - Windows下安装MSI程序遇到2503和2502错误
- Win32 程序运行原理
- VGGNet原理和实现
- ConstraintLayout约束控件详解
- MySQL数据库regdate_第十五章 MySQL 数据库
- 中国音频放大器市场现状研究分析与发展前景分析报告
- js 监听浏览器刷新操作
- 各大主流编程语言简介
- 小程序——scroll-view 页面不滚动与隐藏导航条
- Espresso之RecyclerView
- java 图片背景色_java处理图片背景颜色的方法
- Remove Double Negative(去除双重否定)
- 轻量级程序编辑器的选择:EmEditor、Editplus等---Web开发系列之工具篇(一)
- 输入手机号获取验证码的注册页面,说出测试过程
- RNA-seq 详细教程:搞定count归一化(5)
热门文章
- 计算机固态加机械硬盘,在台式机中添加固态/机械硬盘驱动器,让我与这篇文章一起教你...
- Gym 100015A
- 使用工具清理Windows的winsxs目录
- 帝国cms灵动标签调用标题图片没有图片时让其显示默认图片的方法
- php 判断非负整数,PHP-检测负数
- html选项卡出现乱码,html乱码
- palantir_Palantir开源的两个库– Cinch和Sysmon
- 数据结构与算法——深入理解红-黑树!
- CMD命令Program Files问题
- matlab有LLG方程的解么,matlab在常微分方程数值解中应用.docx