第2部分 启用远程过程调用RPC

1 第2部分 启用远程过程调用——让我们构建一个烤面包机

烤面包机样例的第二部分将会加入一些烤面包行为,为了完成这个任务,我们将会在toaster yang 数据模型中定义一个RPC(远程过程调用)并且会写一个实现。

1.1 定义yang RPC

编辑现有的toaster.yang文件,我们将会定义2RPC方法,make-toast 和 cancel-toast。 (add the bold lines under the module toaster heading):

1.  module toaster {

2.        ... 

3.    //This defines a Remote Procedure Call (rpc). RPC provide the ability to initiate an action

4.    //on the data model. In this case the initating action takes two optional inputs (because default value is defined)

5.    //QUESTION: Am I correct that the inputs are optional because they have defaults defined? The REST call doesn't seem to account for this.

6.    rpc make-toast {

7.      description

8.        "Make some toast. The toastDone notification will be sent when the toast is finished.

9.         An 'in-use' error will be returned if toast is already being made. A 'resource-denied' error will 

10.         be returned if the toaster service is disabled.";

11.  

12.      input {

13.        leaf toasterDoneness {

14.          type uint32 {

15.            range "1 .. 10";

16.          }

17.          default '5';

18.          description

19.            "This variable controls how well-done is the ensuing toast. It should be on a scale of 1 to 10.

20.             Toast made at 10 generally is considered unfit for human consumption; toast made at 1 is warmed lightly.";

21.        }

22.  

23.        leaf toasterToastType {

24.          type identityref {

25.            base toast:toast-type;

26.          }

27.          default 'wheat-bread';

28.          description

29.            "This variable informs the toaster of the type of material that is being toasted. The toaster uses this information, 

30.              combined with toasterDoneness, to compute for how long the material must be toasted to achieve the required doneness.";

31.        }

32.      }

33.    }  // rpc make-toast

34.  

35.    // action to cancel making toast - takes no input parameters

36.    rpc cancel-toast {

37.      description

38.        "Stop making toast, if any is being made.

39.           A 'resource-denied' error will be returned 

40.           if the toaster service is disabled.";

41.    }  // rpc cancel-toast

42.    ...

43.  }

运行:

1. mvn clean install

将会额外生成以下几个类:

l ToasterService -一个扩展RPC服务的接口并且定义了RPC方法与yang数据模型的对应关系。

l MakeToastInput -定义了一个为调用make-toast提供输入参数的DTO(数据传输对象)接口。

l MakeToastInputBuilder -一个具体类,用来创建MakeToastInput实例。

注意:重要的是,你每次运行mvn clean时,你都会修改yang文件。有一些文件没有被生成,如果他们已经存在,这可能导致不正确的文件生成。每当你改变 .yang文件后,你都应该运行一下 mvn  clean,这样将会删除所有已生成的yang文件,通过 mvn-clean-plugin common.opendaylight中定义 pom.xml文件。

1.2 实现RPC方法

我们已经为调用RPC定义了数据模型接口——现在我们必须提供实现。我们将修改OpendaylightToaster去实现新的ToasterService接口(之前仅被生成了)。为简单起见,下面只给出相关的代码:

1. public class OpendaylightToaster implements ToasterService, AutoCloseable {

44.  

45.   ...  

46.   private final ExecutorService executor;

47.   

48.   // The following holds the Future for the current make toast task.

49.   // This is used to cancel the current toast.

50.   private final AtomicReference> currentMakeToastTask = new AtomicReference<>();

51.   

52.   public OpendaylightToaster() {

53.       executor = Executors.newFixedThreadPool(1);

54.   }

55.    

56.   /**

57.   * Implemented from the AutoCloseable interface.

58.   */

59.   @Override

60.   public void close() throws ExecutionException, InterruptedException {

61.       // When we close this service we need to shutdown our executor!

62.       executor.shutdown();

63.       

64.       ...

65.   }

66.   

67.   @Override

68.   public Future> cancelToast() {

69.   

70.       Future current = currentMakeToastTask.getAndSet( null );

71.       if( current != null ) {

72.           current.cancel( true );

73.       }

74.  

75.       // Always return success from the cancel toast call.

76.       return Futures.immediateFuture( Rpcs. getRpcResult( true,

77.                                       Collections.emptyList() ) );

78.   }

79.     

80.   @Override

81.   public Future> makeToast(final MakeToastInput input) {

82.       final SettableFuture> futureResult = SettableFuture.create();

83.  

84.       checkStatusAndMakeToast( input, futureResult );

85.  

86.       return futureResult;

87.   }

88.  

89.   private void checkStatusAndMakeToast( final MakeToastInput input,

90.                                         final SettableFuture> futureResult ) {

91.  

92.       // Read the ToasterStatus and, if currently Up, try to write the status to Down.

93.       // If that succeeds, then we essentially have an exclusive lock and can proceed

94.       // to make toast.

95.  

96.       final ReadWriteTransaction tx = dataProvider.newReadWriteTransaction();

97.       ListenableFuture> readFuture =

98.                                          tx.read( LogicalDatastoreType.OPERATIONAL, TOASTER_IID );

99.  

100.       final ListenableFuture> commitFuture =

101.           Futures.transform( readFuture, new AsyncFunction,

102.                                                                   RpcResult>() {

103.  

104.               @Override

105.               public ListenableFuture> apply(

106.                       Optional toasterData ) throws Exception {

107.  

108.                   ToasterStatus toasterStatus = ToasterStatus.Up;

109.                   if( toasterData.isPresent() ) {

110.                       toasterStatus = ((Toaster)toasterData.get()).getToasterStatus();

111.                   }

112.  

113.                   LOG.debug( "Read toaster status: {}", toasterStatus );

114.  

115.                   if( toasterStatus == ToasterStatus.Up ) {

116.  

117.                       LOG.debug( "Setting Toaster status to Down" );

118.  

119.                       // We're not currently making toast - try to update the status to Down

120.                       // to indicate we're going to make toast. This acts as a lock to prevent

121.                       // concurrent toasting.

122.                       tx.put( LogicalDatastoreType.OPERATIONAL, TOASTER_IID,

123.                               buildToaster( ToasterStatus.Down ) );

124.                       return tx.commit();

125.                   }

126.  

127.                   LOG.debug( "Oops - already making toast!" );

128.  

129.                   // Return an error since we are already making toast. This will get

130.                   // propagated to the commitFuture below which will interpret the null

131.                   // TransactionStatus in the RpcResult as an error condition.

132.                   return Futures.immediateFuture( Rpcs.getRpcResult(

133.                           false, null, makeToasterInUseError() ) );

134.               }

135.       } );

136.  

137.       Futures.addCallback( commitFuture, new FutureCallback>() {

138.           @Override

139.           public void onSuccess( RpcResult result ) {

140.               if( result.getResult() == TransactionStatus.COMMITED  ) {

141.  

142.                   // OK to make toast

143.                   currentMakeToastTask.set( executor.submit(

144.                                                    new MakeToastTask( input, futureResult ) ) );

145.               } else {

146.  

147.                   LOG.debug( "Setting error result" );

148.  

149.                   // Either the transaction failed to commit for some reason or, more likely,

150.                   // the read above returned ToasterStatus.Down. Either way, fail the

151.                   // futureResult and copy the errors.

152.  

153.                   futureResult.set( Rpcs.getRpcResult( false, null, result.getErrors() ) );

154.               }

155.           }

156.  

157.           @Override

158.           public void onFailure( Throwable ex ) {

159.               if( ex instanceof OptimisticLockFailedException ) {

160.  

161.                   // Another thread is likely trying to make toast simultaneously and updated the

162.                   // status before us. Try reading the status again - if another make toast is

163.                   // now in progress, we should get ToasterStatus.Down and fail.

164.  

165.                   LOG.debug( "Got OptimisticLockFailedException - trying again" );

166.  

167.                   checkStatusAndMakeToast( input, futureResult );

168.  

169.               } else {

170.  

171.                   LOG.error( "Failed to commit Toaster status", ex );

172.  

173.                   // Got some unexpected error so fail.

174.                   futureResult.set( Rpcs. getRpcResult( false, null, Arrays.asList(

175.                        RpcErrors.getRpcError( null, null, null, ErrorSeverity.ERROR,

176.                                               ex.getMessage(),

177.                                               ErrorType.APPLICATION, ex ) ) ) );

178.               }

179.           }

180.       } );

181.   }

182.  

183.   private class MakeToastTask implements Callable {

184.  

185.       final MakeToastInput toastRequest;

186.       final SettableFuture> futureResult;

187.  

188.       public MakeToastTask( final MakeToastInput toastRequest,

189.                             final SettableFuture> futureResult ) {

190.           this.toastRequest = toastRequest;

191.           this.futureResult = futureResult;

192.       }

193.  

194.       @Override

195.       public Void call() {

196.           try

197.           {

198.               // make toast just sleeps for n seconds.

199.               long darknessFactor = OpendaylightToaster.this.darknessFactor.get();

200.               Thread.sleep(toastRequest.getToasterDoneness());

201.           }

202.           catch( InterruptedException e ) {

203.               LOG.info( "Interrupted while making the toast" );

204.           }

205.  

206.           toastsMade.incrementAndGet();

207.  

208.           amountOfBreadInStock.getAndDecrement();

209.           if( outOfBread() ) {

210.               LOG.info( "Toaster is out of bread!" );

211.  

212.               notificationProvider.publish( new ToasterOutOfBreadBuilder().build() );

213.           }

214.  

215.           // Set the Toaster status back to up - this essentially releases the toasting lock.

216.           // We can't clear the current toast task nor set the Future result until the

217.           // update has been committed so we pass a callback to be notified on completion.

218.  

219.           setToasterStatusUp( new Function() {

220.               @Override

221.               public Void apply( Boolean result ) {

222.  

223.                   currentMakeToastTask.set( null );

224.  

225.                   LOG.debug("Toast done");

226.  

227.                   futureResult.set( Rpcs.getRpcResult( true, null,

228.                                                          Collections.emptyList() ) );

229.  

230.                   return null;

231.               }

232.           } );

233.           return null;

234.      }

235. }

在上面的代码中可以看到,我们已经实现了makeToast 和 cancelToast方法,除了AutoCloseable接口的close方法,以确保我们已经完全清理了嵌入的线程池。请参考内联注释,关注更多细节。

1.3 通过RPC服务注册OpendaylightToaster 

下一步是注册OpendaylightToaster作为RPC调用的提供者。要做到这些我们首先需要为toaster-provider-impl.yang文件中的MD-SAL's RPC注册服务声明一个依赖关系,类似于之前配置data broker服务的过程:

1.  //augments the configuration,  

236.    augment "/config:modules/config:module/config:configuration" {

237.        case toaster-provider-impl {

238.            when "/config:modules/config:module/config:type = 'toaster-provider-impl'";

239.            ...     

240.            

241.            //Wires dependent services into this class - in this case the RPC registry servic

242.            container rpc-registry {

243.                uses config:service-ref {

244.                    refine type {

245.                        mandatory true;

246.                        config:required-identity mdsal:binding-rpc-registry;

247.                    }

248.                }

249.            } 

250.        }

251.    }

重新生成资源。生成的AbstractToasterProviderModule类现在将有一个getRpcRegistryDependency()方法。我们可以访问toasterprovidermodule方法的实现来用RPC注册服务,注册OpenDaylightToaster

1. @Override

252.    public java.lang.AutoCloseable createInstance() {

253.        final OpendaylightToaster opendaylightToaster = new OpendaylightToaster();

254.    

255.        ...

256.        

257.        final BindingAwareBroker.RpcRegistration rpcRegistration = getRpcRegistryDependency()

258.                .addRpcImplementation(ToasterService.class, opendaylightToaster);

259.           

260.        final class AutoCloseableToaster implements AutoCloseable {

261.     

262.            @Override

263.            public void close() throws Exception {

264.                ...

265.                rpcRegistration.close();

266.                ...

267.            }

268.   

269.        }

270.    

271.        return new AutoCloseableToaster();

272.    }

最后我们需要为'rpc-registry'到初始配置的XML文件中的toaster-provider-impl module03-sample-toaster.xml)添加一个依赖关系,和之前配置'data-broker'的过程一样:

1. 

273. 

274.            prefix:toaster-provider-impl  

275.       toaster-provider-impl

276.        

277.               binding:binding-rpc-registry

278.               binding-rpc-broker

279.        

280.      ...

281.   

 

Thats it!现在我们已经准备好去部署我们已经更新的bundle并且尝试我们的makeToast和取消Toaster调用。

1.4 通过RestConf调用make-toast 

这是最后的时刻了,去做美味的小麦面包了!通过Restconf调用make-toast将执行一个HTTP POST去操作URL

1. HTTP Method => POST

282. URL => http://localhost:8080/restconf/operations/toaster:make-toast 

283. Header =>   Content-Type: application/yang.data+json  

284. Body =>  

285. {

286.   "input" :

287.   {

288.      "toaster:toasterDoneness" : "10",

289.      "toaster:toasterToastType":"wheat-bread" 

290.   }

291. }

292.  

注意:默认和强制性的标志目前还无法实现,所以即使面包类型和煮熟度在yang模型中是默认的,在这里你还必须给他们赋值。

1.5 通过RestConf调用 cancel-toast  

如果你不喜欢烤面包,在运行时你可能想要取消make-toast操作。这可以通过Restconf调用 cancel-toast方法进行远程调用:

1. URL => http://localhost:8080/restconf/operations/toaster:cancel-toast

293. HTTP Method => POST

注意:

1.6 看到烤面包机的状态更新

看到更新的烤面包机状态,调用make-toast call(煮熟度为10的最长延迟),然后立即调用检索烤面包机的运行状态。您现在应该看到:

1.  toaster: {

294.        toasterManufacturer: "Opendaylight"

295.        toasterModelNumber: "Model 1 - Binding Aware"

296.        toasterStatus: "Down"

297.   }

你可能感兴趣的:(opendaylight,SDN,mdsal,toaster,rpc)