Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / OpCodes.hs @ 82b948e4

History | View | Annotate | Download (16.7 kB)

1
{-# LANGUAGE TemplateHaskell #-}
2

    
3
{-| Implementation of the opcodes.
4

    
5
-}
6

    
7
{-
8

    
9
Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc.
10

    
11
This program is free software; you can redistribute it and/or modify
12
it under the terms of the GNU General Public License as published by
13
the Free Software Foundation; either version 2 of the License, or
14
(at your option) any later version.
15

    
16
This program is distributed in the hope that it will be useful, but
17
WITHOUT ANY WARRANTY; without even the implied warranty of
18
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19
General Public License for more details.
20

    
21
You should have received a copy of the GNU General Public License
22
along with this program; if not, write to the Free Software
23
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24
02110-1301, USA.
25

    
26
-}
27

    
28
module Ganeti.OpCodes
29
  ( OpCode(..)
30
  , TagObject(..)
31
  , tagObjectFrom
32
  , encodeTagObject
33
  , decodeTagObject
34
  , ReplaceDisksMode(..)
35
  , DiskIndex
36
  , mkDiskIndex
37
  , unDiskIndex
38
  , opID
39
  , allOpIDs
40
  , allOpFields
41
  , opSummary
42
  , CommonOpParams(..)
43
  , defOpParams
44
  , MetaOpCode(..)
45
  , wrapOpCode
46
  , setOpComment
47
  , setOpPriority
48
  ) where
49

    
50
import Data.Maybe (fromMaybe)
51
import Text.JSON (readJSON, JSON, JSValue, makeObj)
52
import qualified Text.JSON
53

    
54
import Ganeti.THH
55

    
56
import Ganeti.OpParams
57
import Ganeti.Types (OpSubmitPriority(..), fromNonEmpty)
58
import Ganeti.Query.Language (queryTypeOpToRaw)
59

    
60
-- | OpCode representation.
61
--
62
-- We only implement a subset of Ganeti opcodes: those which are actually used
63
-- in the htools codebase.
64
$(genOpCode "OpCode"
65
  [ ("OpTestDelay",
66
     [ pDelayDuration
67
     , pDelayOnMaster
68
     , pDelayOnNodes
69
     , pDelayRepeat
70
     ])
71
  , ("OpInstanceReplaceDisks",
72
     [ pInstanceName
73
     , pEarlyRelease
74
     , pIgnoreIpolicy
75
     , pReplaceDisksMode
76
     , pReplaceDisksList
77
     , pRemoteNode
78
     , pIallocator
79
     ])
80
  , ("OpInstanceFailover",
81
     [ pInstanceName
82
     , pShutdownTimeout
83
     , pIgnoreConsistency
84
     , pMigrationTargetNode
85
     , pIgnoreIpolicy
86
     , pIallocator
87
     , pMigrationCleanup
88
     ])
89
  , ("OpInstanceMigrate",
90
     [ pInstanceName
91
     , pMigrationMode
92
     , pMigrationLive
93
     , pMigrationTargetNode
94
     , pAllowRuntimeChgs
95
     , pIgnoreIpolicy
96
     , pMigrationCleanup
97
     , pIallocator
98
     , pAllowFailover
99
     ])
100
  , ("OpTagsGet",
101
     [ pTagsObject
102
     , pUseLocking
103
     ])
104
  , ("OpTagsSearch",
105
     [ pTagSearchPattern ])
106
  , ("OpTagsSet",
107
     [ pTagsObject
108
     , pTagsList
109
     ])
110
  , ("OpTagsDel",
111
     [ pTagsObject
112
     , pTagsList
113
     ])
114
  , ("OpClusterPostInit", [])
115
  , ("OpClusterDestroy", [])
116
  , ("OpClusterQuery", [])
117
  , ("OpClusterVerify",
118
     [ pDebugSimulateErrors
119
     , pErrorCodes
120
     , pSkipChecks
121
     , pIgnoreErrors
122
     , pVerbose
123
     , pOptGroupName
124
     ])
125
  , ("OpClusterVerifyConfig",
126
     [ pDebugSimulateErrors
127
     , pErrorCodes
128
     , pIgnoreErrors
129
     , pVerbose
130
     ])
131
  , ("OpClusterVerifyGroup",
132
     [ pGroupName
133
     , pDebugSimulateErrors
134
     , pErrorCodes
135
     , pSkipChecks
136
     , pIgnoreErrors
137
     , pVerbose
138
     ])
139
  , ("OpClusterVerifyDisks", [])
140
  , ("OpGroupVerifyDisks",
141
     [ pGroupName
142
     ])
143
  , ("OpClusterRepairDiskSizes",
144
     [ pInstances
145
     ])
146
  , ("OpClusterConfigQuery",
147
     [ pOutputFields
148
     ])
149
  , ("OpClusterRename",
150
     [ pName
151
     ])
152
  , ("OpClusterSetParams",
153
     [ pForce
154
     , pHvState
155
     , pDiskState
156
     , pVgName
157
     , pEnabledHypervisors
158
     , pClusterHvParams
159
     , pClusterBeParams
160
     , pOsHvp
161
     , pClusterOsParams
162
     , pDiskParams
163
     , pCandidatePoolSize
164
     , pUidPool
165
     , pAddUids
166
     , pRemoveUids
167
     , pMaintainNodeHealth
168
     , pPreallocWipeDisks
169
     , pNicParams
170
     , pNdParams
171
     , pIpolicy
172
     , pDrbdHelper
173
     , pDefaultIAllocator
174
     , pMasterNetdev
175
     , pMasterNetmask
176
     , pReservedLvs
177
     , pHiddenOs
178
     , pBlacklistedOs
179
     , pUseExternalMipScript
180
     , pEnabledDiskTemplates
181
     , pModifyEtcHosts
182
     ])
183
  , ("OpClusterRedistConf", [])
184
  , ("OpClusterActivateMasterIp", [])
185
  , ("OpClusterDeactivateMasterIp", [])
186
  , ("OpQuery",
187
     [ pQueryWhat
188
     , pUseLocking
189
     , pQueryFields
190
     , pQueryFilter
191
     ])
192
  , ("OpQueryFields",
193
     [ pQueryWhat
194
     , pQueryFields
195
     ])
196
  , ("OpOobCommand",
197
     [ pNodeNames
198
     , pOobCommand
199
     , pOobTimeout
200
     , pIgnoreStatus
201
     , pPowerDelay
202
     ])
203
  , ("OpNodeRemove", [ pNodeName ])
204
  , ("OpNodeAdd",
205
     [ pNodeName
206
     , pHvState
207
     , pDiskState
208
     , pPrimaryIp
209
     , pSecondaryIp
210
     , pReadd
211
     , pNodeGroup
212
     , pMasterCapable
213
     , pVmCapable
214
     , pNdParams
215
    ])
216
  , ("OpNodeQuery", dOldQuery)
217
  , ("OpNodeQueryvols",
218
     [ pOutputFields
219
     , pNodes
220
     ])
221
  , ("OpNodeQueryStorage",
222
     [ pOutputFields
223
     , pStorageType
224
     , pNodes
225
     , pStorageName
226
     ])
227
  , ("OpNodeModifyStorage",
228
     [ pNodeName
229
     , pStorageType
230
     , pStorageName
231
     , pStorageChanges
232
     ])
233
  , ("OpRepairNodeStorage",
234
     [ pNodeName
235
     , pStorageType
236
     , pStorageName
237
     , pIgnoreConsistency
238
     ])
239
  , ("OpNodeSetParams",
240
     [ pNodeName
241
     , pForce
242
     , pHvState
243
     , pDiskState
244
     , pMasterCandidate
245
     , pOffline
246
     , pDrained
247
     , pAutoPromote
248
     , pMasterCapable
249
     , pVmCapable
250
     , pSecondaryIp
251
     , pNdParams
252
     , pPowered
253
     ])
254
  , ("OpNodePowercycle",
255
     [ pNodeName
256
     , pForce
257
     ])
258
  , ("OpNodeMigrate",
259
     [ pNodeName
260
     , pMigrationMode
261
     , pMigrationLive
262
     , pMigrationTargetNode
263
     , pAllowRuntimeChgs
264
     , pIgnoreIpolicy
265
     , pIallocator
266
     ])
267
  , ("OpNodeEvacuate",
268
     [ pEarlyRelease
269
     , pNodeName
270
     , pRemoteNode
271
     , pIallocator
272
     , pEvacMode
273
     ])
274
  , ("OpInstanceCreate",
275
     [ pInstanceName
276
     , pForceVariant
277
     , pWaitForSync
278
     , pNameCheck
279
     , pIgnoreIpolicy
280
     , pInstBeParams
281
     , pInstDisks
282
     , pDiskTemplate
283
     , pFileDriver
284
     , pFileStorageDir
285
     , pInstHvParams
286
     , pHypervisor
287
     , pIallocator
288
     , pResetDefaults
289
     , pIpCheck
290
     , pIpConflictsCheck
291
     , pInstCreateMode
292
     , pInstNics
293
     , pNoInstall
294
     , pInstOsParams
295
     , pInstOs
296
     , pPrimaryNode
297
     , pSecondaryNode
298
     , pSourceHandshake
299
     , pSourceInstance
300
     , pSourceShutdownTimeout
301
     , pSourceX509Ca
302
     , pSrcNode
303
     , pSrcPath
304
     , pStartInstance
305
     , pOpportunisticLocking
306
     , pInstTags
307
     ])
308
  , ("OpInstanceMultiAlloc",
309
     [ pIallocator
310
     , pMultiAllocInstances
311
     , pOpportunisticLocking
312
     ])
313
  , ("OpInstanceReinstall",
314
     [ pInstanceName
315
     , pForceVariant
316
     , pInstOs
317
     , pTempOsParams
318
     ])
319
  , ("OpInstanceRemove",
320
     [ pInstanceName
321
     , pShutdownTimeout
322
     , pIgnoreFailures
323
     ])
324
  , ("OpInstanceRename",
325
     [ pInstanceName
326
     , pNewName
327
     , pNameCheck
328
     , pIpCheck
329
     ])
330
  , ("OpInstanceStartup",
331
     [ pInstanceName
332
     , pForce
333
     , pIgnoreOfflineNodes
334
     , pTempHvParams
335
     , pTempBeParams
336
     , pNoRemember
337
     , pStartupPaused
338
     ])
339
  , ("OpInstanceShutdown",
340
     [ pInstanceName
341
     , pForce
342
     , pIgnoreOfflineNodes
343
     , pShutdownTimeout'
344
     , pNoRemember
345
     ])
346
  , ("OpInstanceReboot",
347
     [ pInstanceName
348
     , pShutdownTimeout
349
     , pIgnoreSecondaries
350
     , pRebootType
351
     ])
352
  , ("OpInstanceMove",
353
     [ pInstanceName
354
     , pShutdownTimeout
355
     , pIgnoreIpolicy
356
     , pMoveTargetNode
357
     , pIgnoreConsistency
358
     ])
359
  , ("OpInstanceConsole",
360
     [ pInstanceName ])
361
  , ("OpInstanceActivateDisks",
362
     [ pInstanceName
363
     , pIgnoreDiskSize
364
     , pWaitForSyncFalse
365
     ])
366
  , ("OpInstanceDeactivateDisks",
367
     [ pInstanceName
368
     , pForce
369
     ])
370
  , ("OpInstanceRecreateDisks",
371
     [ pInstanceName
372
     , pRecreateDisksInfo
373
     , pNodes
374
     , pIallocator
375
     ])
376
  , ("OpInstanceQuery", dOldQuery)
377
  , ("OpInstanceQueryData",
378
     [ pUseLocking
379
     , pInstances
380
     , pStatic
381
     ])
382
  , ("OpInstanceSetParams",
383
     [ pInstanceName
384
     , pForce
385
     , pForceVariant
386
     , pIgnoreIpolicy
387
     , pInstParamsNicChanges
388
     , pInstParamsDiskChanges
389
     , pInstBeParams
390
     , pRuntimeMem
391
     , pInstHvParams
392
     , pOptDiskTemplate
393
     , pPrimaryNode
394
     , pRemoteNode
395
     , pOsNameChange
396
     , pInstOsParams
397
     , pWaitForSync
398
     , pOffline
399
     , pIpConflictsCheck
400
     , pHotplug
401
     ])
402
  , ("OpInstanceGrowDisk",
403
     [ pInstanceName
404
     , pWaitForSync
405
     , pDiskIndex
406
     , pDiskChgAmount
407
     , pDiskChgAbsolute
408
     ])
409
  , ("OpInstanceChangeGroup",
410
     [ pInstanceName
411
     , pEarlyRelease
412
     , pIallocator
413
     , pTargetGroups
414
     ])
415
  , ("OpGroupAdd",
416
     [ pGroupName
417
     , pNodeGroupAllocPolicy
418
     , pGroupNodeParams
419
     , pDiskParams
420
     , pHvState
421
     , pDiskState
422
     , pIpolicy
423
     ])
424
  , ("OpGroupAssignNodes",
425
     [ pGroupName
426
     , pForce
427
     , pRequiredNodes
428
     ])
429
  , ("OpGroupQuery", dOldQueryNoLocking)
430
  , ("OpGroupSetParams",
431
     [ pGroupName
432
     , pNodeGroupAllocPolicy
433
     , pGroupNodeParams
434
     , pDiskParams
435
     , pHvState
436
     , pDiskState
437
     , pIpolicy
438
     ])
439
  , ("OpGroupRemove",
440
     [ pGroupName ])
441
  , ("OpGroupRename",
442
     [ pGroupName
443
     , pNewName
444
     ])
445
  , ("OpGroupEvacuate",
446
     [ pGroupName
447
     , pEarlyRelease
448
     , pIallocator
449
     , pTargetGroups
450
     ])
451
  , ("OpOsDiagnose",
452
     [ pOutputFields
453
     , pNames ])
454
  , ("OpExtStorageDiagnose",
455
     [ pOutputFields
456
     , pNames ])
457
  , ("OpBackupQuery",
458
     [ pUseLocking
459
     , pNodes
460
     ])
461
  , ("OpBackupPrepare",
462
     [ pInstanceName
463
     , pExportMode
464
     ])
465
  , ("OpBackupExport",
466
     [ pInstanceName
467
     , pShutdownTimeout
468
     , pExportTargetNode
469
     , pShutdownInstance
470
     , pRemoveInstance
471
     , pIgnoreRemoveFailures
472
     , pExportMode
473
     , pX509KeyName
474
     , pX509DestCA
475
     ])
476
  , ("OpBackupRemove",
477
     [ pInstanceName ])
478
  , ("OpTestAllocator",
479
     [ pIAllocatorDirection
480
     , pIAllocatorMode
481
     , pIAllocatorReqName
482
     , pIAllocatorNics
483
     , pIAllocatorDisks
484
     , pHypervisor
485
     , pIallocator
486
     , pInstTags
487
     , pIAllocatorMemory
488
     , pIAllocatorVCpus
489
     , pIAllocatorOs
490
     , pDiskTemplate
491
     , pIAllocatorInstances
492
     , pIAllocatorEvacMode
493
     , pTargetGroups
494
     , pIAllocatorSpindleUse
495
     , pIAllocatorCount
496
     ])
497
  , ("OpTestJqueue",
498
     [ pJQueueNotifyWaitLock
499
     , pJQueueNotifyExec
500
     , pJQueueLogMessages
501
     , pJQueueFail
502
     ])
503
  , ("OpTestDummy",
504
     [ pTestDummyResult
505
     , pTestDummyMessages
506
     , pTestDummyFail
507
     , pTestDummySubmitJobs
508
     ])
509
  , ("OpNetworkAdd",
510
     [ pNetworkName
511
     , pNetworkAddress4
512
     , pNetworkGateway4
513
     , pNetworkAddress6
514
     , pNetworkGateway6
515
     , pNetworkMacPrefix
516
     , pNetworkAddRsvdIps
517
     , pIpConflictsCheck
518
     , pInstTags
519
     ])
520
  , ("OpNetworkRemove",
521
     [ pNetworkName
522
     , pForce
523
     ])
524
  , ("OpNetworkSetParams",
525
     [ pNetworkName
526
     , pNetworkGateway4
527
     , pNetworkAddress6
528
     , pNetworkGateway6
529
     , pNetworkMacPrefix
530
     , pNetworkAddRsvdIps
531
     , pNetworkRemoveRsvdIps
532
     ])
533
  , ("OpNetworkConnect",
534
     [ pGroupName
535
     , pNetworkName
536
     , pNetworkMode
537
     , pNetworkLink
538
     , pIpConflictsCheck
539
     ])
540
  , ("OpNetworkDisconnect",
541
     [ pGroupName
542
     , pNetworkName
543
     ])
544
  , ("OpNetworkQuery", dOldQuery)
545
  , ("OpRestrictedCommand",
546
     [ pUseLocking
547
     , pRequiredNodes
548
     , pRestrictedCommand
549
     ])
550
  ])
551

    
552
-- | Returns the OP_ID for a given opcode value.
553
$(genOpID ''OpCode "opID")
554

    
555
-- | A list of all defined/supported opcode IDs.
556
$(genAllOpIDs ''OpCode "allOpIDs")
557

    
558
instance JSON OpCode where
559
  readJSON = loadOpCode
560
  showJSON = saveOpCode
561

    
562
-- | Generates the summary value for an opcode.
563
opSummaryVal :: OpCode -> Maybe String
564
opSummaryVal OpClusterVerifyGroup { opGroupName = s } = Just (fromNonEmpty s)
565
opSummaryVal OpGroupVerifyDisks { opGroupName = s } = Just (fromNonEmpty s)
566
opSummaryVal OpClusterRename { opName = s } = Just (fromNonEmpty s)
567
opSummaryVal OpQuery { opWhat = s } = Just (queryTypeOpToRaw s)
568
opSummaryVal OpQueryFields { opWhat = s } = Just (queryTypeOpToRaw s)
569
opSummaryVal OpNodeRemove { opNodeName = s } = Just (fromNonEmpty s)
570
opSummaryVal OpNodeAdd { opNodeName = s } = Just (fromNonEmpty s)
571
opSummaryVal OpNodeModifyStorage { opNodeName = s } = Just (fromNonEmpty s)
572
opSummaryVal OpRepairNodeStorage  { opNodeName = s } = Just (fromNonEmpty s)
573
opSummaryVal OpNodeSetParams { opNodeName = s } = Just (fromNonEmpty s)
574
opSummaryVal OpNodePowercycle { opNodeName = s } = Just (fromNonEmpty s)
575
opSummaryVal OpNodeMigrate { opNodeName = s } = Just (fromNonEmpty s)
576
opSummaryVal OpNodeEvacuate { opNodeName = s } = Just (fromNonEmpty s)
577
opSummaryVal OpInstanceCreate { opInstanceName = s } = Just s
578
opSummaryVal OpInstanceReinstall { opInstanceName = s } = Just s
579
opSummaryVal OpInstanceRemove { opInstanceName = s } = Just s
580
-- FIXME: instance rename should show both names; currently it shows none
581
-- opSummaryVal OpInstanceRename { opInstanceName = s } = Just s
582
opSummaryVal OpInstanceStartup { opInstanceName = s } = Just s
583
opSummaryVal OpInstanceShutdown { opInstanceName = s } = Just s
584
opSummaryVal OpInstanceReboot { opInstanceName = s } = Just s
585
opSummaryVal OpInstanceReplaceDisks { opInstanceName = s } = Just s
586
opSummaryVal OpInstanceFailover { opInstanceName = s } = Just s
587
opSummaryVal OpInstanceMigrate { opInstanceName = s } = Just s
588
opSummaryVal OpInstanceMove { opInstanceName = s } = Just s
589
opSummaryVal OpInstanceConsole { opInstanceName = s } = Just s
590
opSummaryVal OpInstanceActivateDisks { opInstanceName = s } = Just s
591
opSummaryVal OpInstanceDeactivateDisks { opInstanceName = s } = Just s
592
opSummaryVal OpInstanceRecreateDisks { opInstanceName = s } = Just s
593
opSummaryVal OpInstanceSetParams { opInstanceName = s } = Just s
594
opSummaryVal OpInstanceGrowDisk { opInstanceName = s } = Just s
595
opSummaryVal OpInstanceChangeGroup { opInstanceName = s } = Just s
596
opSummaryVal OpGroupAdd { opGroupName = s } = Just (fromNonEmpty s)
597
opSummaryVal OpGroupAssignNodes { opGroupName = s } = Just (fromNonEmpty s)
598
opSummaryVal OpGroupSetParams { opGroupName = s } = Just (fromNonEmpty s)
599
opSummaryVal OpGroupRemove { opGroupName = s } = Just (fromNonEmpty s)
600
opSummaryVal OpGroupEvacuate { opGroupName = s } = Just (fromNonEmpty s)
601
opSummaryVal OpBackupPrepare { opInstanceName = s } = Just s
602
opSummaryVal OpBackupExport { opInstanceName = s } = Just s
603
opSummaryVal OpBackupRemove { opInstanceName = s } = Just s
604
opSummaryVal OpTagsGet { opKind = k } =
605
  Just . fromMaybe "None" $ tagNameOf k
606
opSummaryVal OpTagsSearch { opTagSearchPattern = s } = Just (fromNonEmpty s)
607
opSummaryVal OpTestDelay { opDelayDuration = d } = Just (show d)
608
opSummaryVal OpTestAllocator { opIallocator = s } =
609
  -- FIXME: Python doesn't handle None fields well, so we have behave the same
610
  Just $ maybe "None" fromNonEmpty s
611
opSummaryVal OpNetworkAdd { opNetworkName = s} = Just (fromNonEmpty s)
612
opSummaryVal OpNetworkRemove { opNetworkName = s} = Just (fromNonEmpty s)
613
opSummaryVal OpNetworkSetParams { opNetworkName = s} = Just (fromNonEmpty s)
614
opSummaryVal OpNetworkConnect { opNetworkName = s} = Just (fromNonEmpty s)
615
opSummaryVal OpNetworkDisconnect { opNetworkName = s} = Just (fromNonEmpty s)
616
opSummaryVal _ = Nothing
617

    
618
-- | Computes the summary of the opcode.
619
opSummary :: OpCode -> String
620
opSummary op =
621
  case opSummaryVal op of
622
    Nothing -> op_suffix
623
    Just s -> op_suffix ++ "(" ++ s ++ ")"
624
  where op_suffix = drop 3 $ opID op
625

    
626
-- | Generic\/common opcode parameters.
627
$(buildObject "CommonOpParams" "op"
628
  [ pDryRun
629
  , pDebugLevel
630
  , pOpPriority
631
  , pDependencies
632
  , pComment
633
  , pReason
634
  ])
635

    
636
-- | Default common parameter values.
637
defOpParams :: CommonOpParams
638
defOpParams =
639
  CommonOpParams { opDryRun     = Nothing
640
                 , opDebugLevel = Nothing
641
                 , opPriority   = OpPrioNormal
642
                 , opDepends    = Nothing
643
                 , opComment    = Nothing
644
                 , opReason     = []
645
                 }
646

    
647
-- | The top-level opcode type.
648
data MetaOpCode = MetaOpCode { metaParams :: CommonOpParams
649
                             , metaOpCode :: OpCode
650
                             } deriving (Show, Eq)
651

    
652
-- | JSON serialisation for 'MetaOpCode'.
653
showMeta :: MetaOpCode -> JSValue
654
showMeta (MetaOpCode params op) =
655
  let objparams = toDictCommonOpParams params
656
      objop = toDictOpCode op
657
  in makeObj (objparams ++ objop)
658

    
659
-- | JSON deserialisation for 'MetaOpCode'
660
readMeta :: JSValue -> Text.JSON.Result MetaOpCode
661
readMeta v = do
662
  meta <- readJSON v
663
  op <- readJSON v
664
  return $ MetaOpCode meta op
665

    
666
instance JSON MetaOpCode where
667
  showJSON = showMeta
668
  readJSON = readMeta
669

    
670
-- | Wraps an 'OpCode' with the default parameters to build a
671
-- 'MetaOpCode'.
672
wrapOpCode :: OpCode -> MetaOpCode
673
wrapOpCode = MetaOpCode defOpParams
674

    
675
-- | Sets the comment on a meta opcode.
676
setOpComment :: String -> MetaOpCode -> MetaOpCode
677
setOpComment comment (MetaOpCode common op) =
678
  MetaOpCode (common { opComment = Just comment}) op
679

    
680
-- | Sets the priority on a meta opcode.
681
setOpPriority :: OpSubmitPriority -> MetaOpCode -> MetaOpCode
682
setOpPriority prio (MetaOpCode common op) =
683
  MetaOpCode (common { opPriority = prio }) op