第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<Future<?>> 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<RpcResult<Void>> 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.<Void> getRpcResult( true,

77.                                       Collections.<RpcError>emptyList() ) );

78.   }

79.     

80.   @Override

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

82.       final SettableFuture<RpcResult<Void>> futureResult = SettableFuture.create();

83.  

84.       checkStatusAndMakeToast( input, futureResult );

85.  

86.       return futureResult;

87.   }

88.  

89.   private void checkStatusAndMakeToast( final MakeToastInput input,

90.                                         final SettableFuture<RpcResult<Void>> 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<Optional<DataObject>> readFuture =

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

99.  

100.       final ListenableFuture<RpcResult<TransactionStatus>> commitFuture =

101.           Futures.transform( readFuture, new AsyncFunction<Optional<DataObject>,

102.                                                                   RpcResult<TransactionStatus>>() {

103.  

104.               @Override

105.               public ListenableFuture<RpcResult<TransactionStatus>> apply(

106.                       Optional<DataObject> 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.<TransactionStatus>getRpcResult(

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

134.               }

135.       } );

136.  

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

138.           @Override

139.           public void onSuccess( RpcResult<TransactionStatus> 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.<Void>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.<Void> 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<Void> {

184.  

185.       final MakeToastInput toastRequest;

186.       final SettableFuture<RpcResult<Void>> futureResult;

187.  

188.       public MakeToastTask( final MakeToastInput toastRequest,

189.                             final SettableFuture<RpcResult<Void>> 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<Boolean,Void>() {

220.               @Override

221.               public Void apply( Boolean result ) {

222.  

223.                   currentMakeToastTask.set( null );

224.  

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

226.  

227.                   futureResult.set( Rpcs.<Void>getRpcResult( true, null,

228.                                                          Collections.<RpcError>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<ToasterService> 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. <module>

273. <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">

274.            prefix:toaster-provider-impl  </type>

275.       <name>toaster-provider-impl</name>

276.        <rpc-registry>

277.               <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>

278.               <name>binding-rpc-broker</name>

279.        </rpc-registry>

280.      ...

281.   </module>

 

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.   }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章