信而泰自动化OSPFv2测试小技巧

news2025/8/14 18:54:24

OSPFv2协议简介

OSPFv2(开放式最短路径优先版本2)是互联网协议(IP)网络的路由协议。它使用链路状态路由(LSR)算法,并且属于在单个自治系统(AS)内运行的内部网关协议(IGP)组。

测试拓扑

典型测试场景介绍

1.测试仪表端口Port_1模拟OSPFv2协议会话与DUT设备端口Gi0/0/1建立OSPFv2邻居。

2.测试仪表端口Port_2创建一个interface接口模拟一个路由器设备与DUT设备端口Gi0/0/2在同一网关。

3.测试仪表端口Port_1模拟OSPFv2协议创建一条External LSA发布路由。

4.被测DUT设备生成路由表后,测试仪表端口Port_2创建interface->OSPFv2 External的路由绑定流。

5.发送流量,检查流量没有丢包,路由正常。

手动测试仪表配置

1.预约两个端口Port_1、Port_2

2.端口Port_1创建一个ipv4 interface

3.端口Port_1创建一个OSPFv2会话并绑定到interface

4.端口Port_2创建一个ipv4 interface

5.端口Port_2创建一条interface->OSPFv2 External的路由绑定流StreamTemplate_1

6.订阅StreamBlockStats统计

7.启动OSPFv2协议,查看设备路由,启动Port_2的绑定流StreamTemplate_2-1

8.查看流量收发包相等,没有丢包

Renix Python API自动化测试

测试前置步骤

导入Renix Python API修改当前使用的设备平台

>>> from renix_py_api.renix import *>>>>>> # 初始化Renix>>> initialize()renix python api init beginlog path: c:\renix_py_api\logs\script_Jul_29_22_11_30_52.log[INFO] 2022-07-29 11:31:33,324  Receive Msg: listen_port 9002
[INFO] 2022-07-29 11:31:33,324  launch CL successfully with listen port: 9002[INFO] 2022-07-29 11:31:33,469  Renix api initialization finished>>> # 修改产品类型>>> sys_entry = get_sys_entry()>>> sys_entry.ProductType = 'DARYU'

测试步骤

1.预约两个端口Port_1、Port_2

>>> # 创建端口>>> Port_1 = Port(upper=sys_entry, Location='//10.0.11.191/1/15')>>> print(Port_1.Name)Port_1>>> Port_2 = Port(upper=sys_entry, Location='//10.0.11.191/1/16')>>> print(Port_2.Name)Port_2>>> # 端口上线>>> online_command = BringPortsOnlineCommand(PortList=[Port_1.handle, Port_2.handle])>>> online_command.execute()>>> print(online_command.PortList)['Port_1', 'Port_2']

2.端口Port_1创建一个ipv4 interface​​​​​​​

>>> # 创建接口>>> interface1 = Interface(upper=Port_1)>>> print(interface1.Name)Interface_1>>> # 修改接口类型为IPv4接口>>> BuildInterfaceCommand(InterfaceList=[interface1.handle], NetworkLayers='eth ipv4').execute()>>> # 修改接口类型IP地址参数>>> ipv4layer1 = interface1.get_children('Ipv4Layer')[0]>>> ipv4layer1.edit(Address='192.168.10.2', Gateway='192.168.10.1', PrefixLength='24')>>> print(ipv4layer1.__dict__){'_Address': '192.168.10.2', '_Step': '0.0.0.1', '_AddressList': [], '_PrefixLength': '24', '_Gateway': '192.168.10.1', '_GatewayStep': '0.0.0.0', '_GatewayList': [], '_GatewayCount': 1, '_GatewayMac': '00:00:02:01:01:02', '_ResolvedMacList': [], '_ResolvedState': False, '_LayerState': <EnumLayerState.READY: 1>, '_AddressMode': <EnumAddressMode.RANGE: 0>, '_Count': 1, '_VrfName': '', '_VrfHandle': '', '_Name': 'Ipv4Layer_1', '_Enable': True, '_ROMTag': '', 'force_auto_sync': True, 'handle': 'Ipv4Layer_1', 'lower': [], 'upper': None, 'relatives': {}}

3.端口Port_1创建一个OSPFv2会话并绑定到interface​​​​​​​

>>> # 创建OSPFv2协议会话>>> Ospfv2Sesson = Ospfv2ProtocolConfig(upper=Port_1)>>> print(Ospfv2Sesson.Name)Ospfv2ProtocolConfig_1>>> # OSPFv2协议会话与接口绑定>>> SelectInterfaceCommand(ProtocolList=[Ospfv2Sesson.handle], InterfaceList=[interface1.handle]).execute()

4.端口Port_2创建一个ipv4 interface​​​​​​​

>>> # 创建接口>>> interface2 = Interface(upper=Port_2)>>> print(interface2.Name)Interface_2>>> # 修改接口类型为IPv4接口>>> BuildInterfaceCommand(InterfaceList=[interface2.handle], NetworkLayers='eth ipv4').execute()>>> # 修改接口类型IP地址参数>>> ipv4layer2 = interface2.get_children('Ipv4Layer')[0]>>> ipv4layer2.edit(Address='192.168.20.2', Gateway='192.168.20.1', PrefixLength='24')>>> print(ipv4layer2.__dict__){'_Address': '192.168.20.2', '_Step': '0.0.0.1', '_AddressList': [], '_PrefixLength': '24', '_Gateway': '192.168.20.1', '_GatewayStep': '0.0.0.0', '_GatewayList': [], '_GatewayCount': 1, '_GatewayMac': '00:00:02:01:01:02', '_ResolvedMacList': [], '_ResolvedState': False, '_LayerState': <EnumLayerState.READY: 1>, '_AddressMode': <EnumAddressMode.RANGE: 0>, '_Count': 1, '_VrfName': '', '_VrfHandle': '', '_Name': 'Ipv4Layer_2', '_Enable': True, '_ROMTag': '', 'force_auto_sync': True, 'handle': 'Ipv4Layer_2', 'lower': [], 'upper': None, 'relatives': {}}

5.端口Port_2创建一条interface->OSPFv2 External的路由绑定流StreamTemplate_1​​​​​​​

>>> # OSPFv2协议会话创建External Lsa>>> Ospfv2ExternalLsa = Ospfv2ExternalLsaConfig(upper=Ospfv2Sesson, RouteCount=10, StartNetworkPrefix='100.0.0.2', PrefixLength=24)>>> print(Ospfv2ExternalLsa.__dict__){'_AdvertisingRouterId': '2.1.1.2', '_LsType': <EnumExtLsaLsType.ExtLsaLsType1: 0>, '_RouteCount': 10, '_StartNetworkPrefix': '100.0.0.2', '_PrefixLength': 24, '_Increment': 1, '_EndNetworkPrefix': '100.0.9.2', '_MetricType': <EnumExtLsaLsMetricType.ExtLsaLsMetricType1: 0>, '_Metric': 1, '_ForwardingAddress': '0.0.0.0', '_RouterTag': 0, '_Options': <EnumOspfv2OptionBit.EBIT: 2>, '_Age': 1, '_SequenceNumber':2147483649, '_LsaAutomaticConversion': True, '_Checksum': True, '_Name': 'Ospfv2ExternalLsaConfig_1', '_Enable': True, '_ROMTag': '', 'force_auto_sync': True, 'handle': 'Ospfv2ExternalLsaConfig_1', 'lower': [], 'upper': <renix_py_api.api_gen.Ospfv2ProtocolConfig_Autogen.Ospfv2ProtocolConfig object at 0x0000013D228775E0>, 'relatives': {}}

技巧1:OSPFv2绑定流的源目的端点需要通过LSA对象的get_relatives函数获取,具体方法如下​​​​​​​

>>> # 获取OSPFv2协议绑定流端点对象>>> DstEndpoint = Ospfv2ExternalLsa.get_relatives('GenerateLsa', direction=EnumRelationDirection.SOURCE)[0]>>> print(DstEndpoint.__dict__){'_NetworkAddress': '100.0.0.2', '_Step': '0.0.1.0', '_PrefixLength': 24, '_NetworkList': [], '_NextHopList': [], '_PrefixLengthList': [], '_Count': 10, '_AddressMode': <EnumAddressMode.RANGE: 0>, '_VrfName': '', '_VrfHandle': '', '_Name': 'Ospfv2AsExternalRoute_1', '_Enable': True, '_ROMTag': '', 'force_auto_sync': True, 'handle': 'Ospfv2AsExternalRoute_1', 'lower': [], 'upper': None, 'relatives': {}}

技巧2:接口interface绑定流的IPv4源目的端点是IPv4Layer对象,使用方法如下​​​​​​​

>>> # 创建OSPFv2绑定流>>> binding_stream_command = CreateBindingStreamCommand(SrcEndpointHandles=ipv4layer2.handle, DstEndpointHandles=DstEndpoint.handle, Bidirection=False)>>> binding_stream_command.execute()# 创建OSPFv2绑定流>>> binding_stream_command = CreateBindingStreamCommand(SrcEndpointHandles=ipv4layer1.handle, DstEndpointHandles=DstEndpoint.handle,                                                   Bidirection=True)>>> binding_stream_command.execute()>>> # 获取创建的绑定流对象的handle>>> streams_handle = binding_stream_command.BindingStreams>>> print(streams_handle)['StreamTemplate_1']

技巧3:通过API的唯一标识handle获取对应的对象使用ROMManager.get_object()  例如: StreamObject = ROMManager.get_object('StreamTemplate_1')​​​​​​​

>>> streams = [ROMManager.get_object(handle) for handle in streams_handle]>>> print(streams)[<renix_py_api.api_gen.StreamTemplate_Autogen.StreamTemplate object at 0x0000013D228777C0>]

6.订阅统计​​​​​​​

>>> # 创建OSPFv2会话统计对象>>> ospf_result_view = PageResultView(upper=sys_entry, DataClassName='Ospfv2SessionResultPropertySet')>>> ospf_result_query = ResultQuery(upper=ospf_result_view)>>> # 创建StreamBlockStats统计对象>>> stream_result_view = PageResultView(upper=sys_entry, DataClassName='StreamBlockStats')>>> stream_result_query = ResultQuery(upper=stream_result_view)>>> # 订阅统计>>> SubscribeResultCommand(ResultViewHandles=[ospf_result_view.handle, stream_result_view.handle]).execute()

7.启动OSPFv2协议,启动Port_2的绑定流StreamTemplate_1

技巧4:停流获取统计必须等待3秒才能获取到稳定统计数据

>>> # 启动协议>>> StartAllProtocolCommand().execute()>>> time.sleep(60)>>> # 发送流量>>> StartAllStreamCommand().execute()>>> time.sleep(30)>>> StopAllStreamCommand().execute()>>> StopAllProtocolCommand().execute()>>> # 停流获取统计必须等待3sec才能获取到稳定统计数据>>> time.sleep(3)

8.查看流量收发包相等,没有丢包

>>> # 获取OSPFv2会话1统计>>> session_stats = ospf_result_query.get_children('Ospfv2SessionResultPropertySet')>>> print('TxHello: {}'.format(session_stats[0].TxHello))TxHello: 10>>> print('RxHello: {}'.format(session_stats[0].RxHello))RxHello: 10>>> # 获取流量StreamTemplate_1的StreamBlockStats统计结果>>> streamblock_stats = stream_result_query.get_children('StreamBlockStats')>>> print('TxStreamFrames: {}'.format(streamblock_stats[0].TxStreamFrames))TxStreamFrames: 24036657>>> print('RxStreamFrames: {}'.format(streamblock_stats[0].RxStreamFrames))RxStreamFrames: 24036657

测试后置步骤

释放占用的仪表端口

# 释放端口ReleasePortCommand(LocationList=['//10.0.11.191/1/15', '//10.0.11.191/1/16'], ForceRelease=True).execute()

总结

脚本语言可以将测试过程编写成脚本,相比手动测试,自动化测试优势在于可以24小时不间断执行测试,能大大提高测试效率,减少人为失误。自动化测试的难点在于脚本开发阶段需要投入比手工测试多出数倍的工作量,而通过学习总结自动化测试中API使用的技巧可以提高自动化开发效率,达到事半功倍的效果。

附录:脚本源码

# 初始化Renix initialize() # 修改产品类型 sys_entry = get_sys_entry() sys_entry.ProductType = 'DARYU'  # 创建端口 Port_1 = Port(upper=sys_entry, Location='//10.0.11.191/1/15') Port_2 = Port(upper=sys_entry, Location='//10.0.11.191/1/16') # 端口上线 BringPortsOnlineCommand(PortList=[Port_1.handle, Port_2.handle]).execute()  # 创建接口 interface1 = Interface(upper=Port_1) # 修改接口类型为IPv4接口 BuildInterfaceCommand(InterfaceList=[interface1.handle], NetworkLayers='eth ipv4').execute()  # 修改接口类型IP地址参数 ipv4layer1 = interface1.get_children('Ipv4Layer')[0] ipv4layer1.edit(Address='192.168.10.2', Gateway='192.168.10.1', PrefixLength='24')  # 创建OSPFv2协议会话 Ospfv2Sesson = Ospfv2ProtocolConfig(upper=Port_1) # OSPFv2协议会话与接口绑定 SelectInterfaceCommand(ProtocolList=[Ospfv2Sesson.handle], InterfaceList=[interface1.handle]).execute()  # 创建接口 interface2 = Interface(upper=Port_2) # 修改接口类型为IPv4接口 BuildInterfaceCommand(InterfaceList=[interface2.handle], NetworkLayers='eth ipv4').execute() # 修改接口类型IP地址参数 ipv4layer2 = interface2.get_children('Ipv4Layer')[0] ipv4layer2.edit(Address='192.168.20.2', Gateway='192.168.20.1', PrefixLength='24')  # OSPFv2协议会话创建External Lsa Ospfv2ExternalLsa = Ospfv2ExternalLsaConfig(upper=Ospfv2Sesson, RouteCount=10, StartNetworkPrefix='100.0.0.2', PrefixLength=24)  # 创建一条interface->OSPFv2 External的路由绑定流StreamTemplate_1 # 获取OSPFv2协议绑定流端点对象 DstEndpoint = Ospfv2ExternalLsa.get_relatives('GenerateLsa', direction=EnumRelationDirection.SOURCE)[0] # 创建OSPFv2绑定流 binding_stream_command = CreateBindingStreamCommand(SrcEndpointHandles=ipv4layer2.handle, DstEndpointHandles=DstEndpoint.handle, Bidirection=False) binding_stream_command.execute()  # 获取创建的绑定流对象的handle streams_handle = binding_stream_command.BindingStreams # 通过API的唯一标识handle获取对应的对象使用ROMManager.get_object()例如: StreamObject = ROMManager.get_object('StreamTemplate_1') streams = [ROMManager.get_object(handle) for handle in streams_handle]  # 创建OSPFv2会话统计对象 ospf_result_view = PageResultView(upper=sys_entry, DataClassName='Ospfv2SessionResultPropertySet') ospf_result_query = ResultQuery(upper=ospf_result_view) # 创建StreamBlockStats统计对象 stream_result_view = PageResultView(upper=sys_entry, DataClassName='StreamBlockStats') stream_result_query = ResultQuery(upper=stream_result_view) # 订阅统计 SubscribeResultCommand(ResultViewHandles=[ospf_result_view.handle, stream_result_view.handle]).execute()  # 保存配置文件 dirname, tempfilename = os.path.split(os.path.abspath(__file__)) filename, extension = os.path.splitext(tempfilename) SaveTestCaseCommand(TestCase=f'{dirname}/xcfg/{filename}.xcfg').execute()  # 启动协议 StartAllProtocolCommand().execute() time.sleep(60) # 发送流量 StartAllStreamCommand().execute() time.sleep(30) StopAllStreamCommand().execute() StopAllProtocolCommand().execute() # 停流获取统计必须等待3sec才能获取到稳定统计数据 time.sleep(3)  # 获取OSPFv2会话1统计 session_stats = ospf_result_query.get_children('Ospfv2SessionResultPropertySet') print(session_stats[0].__dict__) TxHello = session_stats[0].TxHello RxHello = session_stats[0].RxHello print('TxStreamFrames: {}'.format(TxHello)) print('RxStreamFrames: {}'.format(RxHello)) # 获取流量StreamTemplate_1的StreamBlockStats统计结果 streamblock_stats = stream_result_query.get_children('StreamBlockStats') print(streamblock_stats[0].__dict__) TxStreamFrames = streamblock_stats[0].TxStreamFrames RxStreamFrames = streamblock_stats[0].RxStreamFrames print('TxStreamFrames: {}'.format(TxStreamFrames)) print('RxStreamFrames: {}'.format(RxStreamFrames))  # 释放端口 ReleasePortCommand(LocationList=['//10.0.11.191/1/15', '//10.0.11.191/1/16'], ForceRelease=True).execute()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/14903.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Inter RealSense深度相机ROS驱动

文章目录知识目标1. 深度相机的分类及工作原理2. Inter RealSense D415相机知识目标 学习深度相机的分类和工作原理&#xff1b; 学习Intel RealSense D415相机硬件构成和工作原理。 1. 深度相机的分类及工作原理 深度相机&#xff08;可以测量物体到相机的距离&#xff09;…

Java三大特性篇之——多态篇(千字详解)

JAVA面向对象编程有三大特性&#xff1a;封装、继承、多态,在我们学习了继承后&#xff0c;我们将继续学习多态。 文章目录前言&#xff1a;什么是多态&#xff1f;一、多态实现二、再谈重写三、向上转移四、多态优缺点五、多态案例最后前言&#xff1a;什么是多态&#xff1f;…

MR场景直播-帮助企业高效开展更有意思的员工培训

阿酷TONY / 2022-11-18 / 长沙 MR场景直播、MR培训场景和内容呈现以及直播互动功能&#xff0c;帮助企业高效开展员工培训&#xff0c;让整个培训过程更高效~~~ MR场景直播有哪些有意思的地方呢&#xff1f;先来一个图&#xff1a; ▲ 模拟真实光照还原现实景 丰富培训场景&a…

SQL实用功能手册

SQL实用功能手册 SQL基础复习 SQL结构化查询语言&#xff0c;是一种访问和处理数据库的计算机语言 对数据库操作对表操作对数据进行CRUD操作操作视图、存储过程、索引 环境基础操作 安装mysql、启动mysql、配置环境变量检查mysql版本&#xff1a;mysql --version链接mysql…

开源共建 | Dinky 扩展批流统一数据集成框架 ChunJun 的实践分享

一、前言 ChunJun&#xff08;原FlinkX&#xff09;是一个基于 Flink 提供易用、稳定、高效的批流统一的数据集成工具&#xff0c;既可以采集静态的数据&#xff0c;比如 MySQL&#xff0c;HDFS 等&#xff0c;也可以采集实时变化的数据&#xff0c;比如 binlog&#xff0c;Ka…

(九)DateTime——PHP

文章目录第九章 Date & Time1 time()获取时间戳2 getDate()转换时间戳3 date()转换时间戳第九章 Date & Time 1 time()获取时间戳 time()函数返回的整数表示自1970年1月1日格林尼治标准时间午夜起经过的秒数。这一时刻称为UNIX历元&#xff0c;自那时起经过的秒钟数称…

Metabase学习教程:提问-1

创建交互式图表 可以通过使用查询生成器、构建模型或添加自定义目标来创建图表。供用户在Metabase中钻取数据。类似&#xff1a; 图1。放大特定类别和时间范围&#xff0c;然后查看构成图表上某个条形图的订单。 如果您只使用SQL编写过问题&#xff0c;那么您可能会忽略这样一…

颠覆IoT行业的开发神器!涂鸦智能重磅推出TuyaOS操作系统【程序员必备】

1 前言 作为降低 IoT 技术门槛的开发神器&#xff0c;TuyaOS 操作系统重磅发布 3.6.0 新版本啦&#xff01;针对设备安全、功耗、通信速率等关键功能&#xff0c;做了重大创新和优化升级。为了助力开发者更快速便捷地接入涂鸦IoT PaaS&#xff0c;并低门槛开发出有创意的智能单…

RabbitMQ初步到精通-第三章-RabbitMQ面板及环境搭建

第三章-RabbitMQ面板及环境搭建 1、RabbitMQ面板介绍 Rabbitmq安装完毕后&#xff0c;若是本地环境&#xff0c;打入&#xff1a;http://localhost:15672/#/ 进入到MQ的控制台页面中&#xff1a; 可以观察到此页面涉及的各个TAB&#xff0c;和我们前面介绍到的rabbitMQ架构中…

Docker入门学习笔记(狂神版)

下述笔记是自己花一天时间看B站狂神说Docker视频的笔记&#xff0c;下列的笔记是根据自己的实践的记录下来的&#xff0c;若想细学掌握Docker建议自行观看&#xff08;《Docker入门到精通》&#xff09;&#xff0c;去观看狂胜的视频记得三连支持一下。他的Docker讲解个人觉得是…

每日一个设计模式之【代理模式】

文章目录每日一个设计模式之【代理模式】☁️前言&#x1f389;&#x1f389;&#x1f389;&#x1f33b;模式概述&#x1f331;代理模式的实现&#x1f340;静态代理&#x1f340;动态代理&#x1f433;JDK代理&#x1f433;CGLib代理&#x1f340;拓展&#x1f433;虚拟代理&…

UE 5.1正式发布,有哪些值得一试的新功能?

UE 5.1正式发布&#xff0c;所以今天咱们就来看看最新版都具体更新和改进了哪些功能吧—— Nanite和Lumen Nanite和Lumen是UE 5.0版本更新的两个主要内容&#xff0c;UE 5.1则是对其进行进一步的改进。 Nanite添加了对双面材质和新的可编程光栅化程序的支持&#xff0c;可以通…

认识前端闭包

1、前言&#xff1a;&#xff08;先介绍一下函数的存储原理&#xff09; &#xff08;1&#xff09;基本函数存储原理&#xff1a; 首先我们定义一个函数&#xff0c;然后调用&#xff0c;如下&#xff1a; <script>function test(){let name yiyiconsole.log(name)}t…

未来的产品经理,需要什么样的原型设计工具?

我和原型工具的往事 作为一只工龄十年的产品汪&#xff0c;我一直在使用原型设计工具——摹客 &#xff0c;对于摹客可谓耳熟能详。 初次听说&#xff0c;是产品社群里的点赞安利&#xff1a;做移动端&#xff0c;用摹客。 当时正值2015年&#xff0c;移动端热到不行&#x…

【动态规划】字串中回文的个数

一、题目描述 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 注&#xff1a; 回文字符串 是正着读和倒过来读一样的字符串。子字符串 是字符串中的由连续字符组成的一个序列。具有不同开始位置或结束位置的子串&#xff0c;即使是由相同的字符…

Matlab论文插图绘制模板—水平三维柱状图(渐变)

在之前的文章中&#xff0c;分享了Matlab水平三维柱状图的绘制模板。 高度赋色的水平三维柱状图&#xff1a; 这次再来分享一下渐变赋色的水平三维柱状图的绘制模板。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;Matlab论文插图绘制模板系列&#xff0c;旨在降低大家…

CellMarker 2.0 | 鼠标点一点就完成单细胞分析的完美工具~

1写在前面 本期我们介绍一下CellMarker 2.0上更新的6个网页工具&#xff0c;主要是用于scRNA-seq数据的分析与可视化。&#x1f970; 网址如下&#xff1a;&#x1f447; &#x1f4cd;http://bio-bigdata.hrbmu.edu.cn/CellMarker/index.html 2Single cell web tools 概览 作者…

“幂等”不等于“分布式锁”,也不得不考虑返回值

. 概览 在分布式系统中&#xff0c;幂等是一个非常重要的概念&#xff0c;常常与“重试”一起出现。当调用一个远程服务发生超时&#xff0c;调用方并不知道请求是否执行成功&#xff0c;这就是典型的“第三态”问题。对于这个问题最常见的解决方案便是进行主动重试&#xff0…

20道前端高频面试题(附答案)

setTimeout 模拟 setInterval 描述&#xff1a;使用setTimeout模拟实现setInterval的功能。 实现&#xff1a; const mySetInterval(fn, time) {let timer null;const interval () > {timer setTimeout(() > {fn(); // time 时间之后会执行真正的函数fninterval()…

2022年NPDP新版教材知识集锦--【第三章节】(4)

【敏捷开发】 8.1敏捷开发模型的定义 门径和敏捷方法的特点&#xff1a;门径流程适用于开发硬件产品&#xff0c;而敏捷方法适用于开发软件产品。这两种方法是相对独立的。敏捷方法和门径流程不是互相取代的关系。相反敏捷方法是一种有效的微观规划工具或项目管理工具&#x…