ATOM

Qmonus SDKは、オブジェクト指向プログラミングを実現するための汎用データモデルとしてATOM(Asynchronous Tree propagational Object Model)を提供しています。Frontalが提供するクラスエディタ上でフィールドやメソッド、ステートマシンなどを記述することですぐにクラスとして利用することができます。

Note

Pythonネイティブな通常のクラスを記述して利用したい場合は、プラグインモジュールで定義して利用することができます。


ATOMには、以下の特徴があります。

  • 自律的に駆動するステートマシンを持つことができる。
  • ステート遷移は、トランザクションサービスによって管理され、一貫性が保証される。
  • 参照フィールドの値変化が自律伝搬する。
  • メソッド呼び出しが自律伝搬する。
  • 継承メカニズムがあり、サブクラス化できます。
  • シナリオのグローバル変数として利用できる。
  • 永続化モードでは、データベーススキーマ自動生成とORM(Object-Relational Mapping)が自動で行われ、CRUDメソッドがバンドルされる。
  • フィールド、メソッド、およびステートマシンは、開発者が自由に定義できます。


ATOM定義が保存されると、永続化モードやAPI生成モードに従って、永続化するデータベーステーブルやCRUDするためのRestful APIが自動的に生成され、すぐに利用することができます。


ATOMで定義する情報

メタ情報定義

メタ情報定義では、ATOMクラス名や各種動作モードを定義します。

category
ATOMが属する分類を指定してください。この情報はクラスエディタでの階層表示のためのタグとしてのみ使用され、ATOMの動作にはなんら影響しません。
name
ATOMの名前を指定します(クラス名と同義)。ユニークである必要があります。
extends
継承するスーパーATOMを列挙します。
persistence
永続化モードを指定します。Trueの場合、ATOM定義を保存するとクラス構造に対応するデータベーステーブルが自動的に生成され、データベースに対するCRUDの組込みメソッドが実装されます。デフォルトはTrueです。Falseの場合は、ATOMインスタンスをデータベースに永続化することはできません。クラスメソッドで機能だけを提供するようなファンクションクラスの場合は、Falseを利用すると良いでしょう。
abstract
抽象クラスモードを指定します。デフォルトはFalseです。Trueの場合、インスタンス化は可能ですが、永続化することはできません。また、後述するapi_generationTrueの場合、生成されるAPIはそのクラスのサブクラスをCRUDできるものになります。
api_generation
API自動生成モードを指定します。API自動生成モードでは、ATOMをCRUDするためのRestful APIが自動的に生成されます。デフォルトはTrueです。ATOMのCRUDをシナリオの中で行うケースが多く見受けられるため、将来デフォルト値はFalseに変更となる可能性があります。
scope
API自動生成モードで生成されるAPIルーティングのスコープを指定します。デフォルトはsecureです。
api_basepath
API自動生成モードで生成されるAPIルーティングの基底パスを指定します。デフォルトは/apisです。
api_auto_response
API自動生成モードで生成されたCUD要求に対して自動応答するモードを指定します。デフォルトはTrueです。Falseの場合、開発者はCUDに対応する特別なメソッドをオーバーライドして応答を作成する必要があります。


フィールド定義

フィールドは、ATOMクラスの属性定義です。

instance field
ATOMインスタンスの識別子フィールドです。Qmonusメタクラスによって自動的に割り当てられるため、開発者が設定することはできません。
identifier field
ATOMインスタンスを一意に識別するIDを定義するフィールドです。定義されていない場合、内部割当されるinstance fieldが唯一のIDになります。永続化モードでは、データベーステーブルの本フィールドにユニークインデックスが自動付与されることに注意してください。
local fields
ATOMインスタンスが保持するローカルフィールドです。基本データ型に加え、他のATOMやATOMのリストを保持することが可能です。また、状態属性の場合は、状態マシンを設定することができます。
設定項目 必須 初期値 説明
field_name None フィールド名を指定します。Pythonの識別子として利用できる文字列である必要があります。
field_type string フィールドの型を指定します。フィールドに設定可能な基本型は以下の通りです。これらに加えて定義されている他のATOMやATOMのリストが指定できます。
string integer number boolean DateTime object array MU array<MU>
field_alias - None フィールドの別名を指定します。field_aliasを設定した場合、後述する組込メソッド(localfields yaml_format api_format dictionary)の結果はフィールド名ではなくエイリアスで出力されます。
field_dbtype - None フィールドのデータベースカラムの型を指定します。ATOMはデータベーススキーマの生成にModelサービスを利用しています。
Docs » Scenario » モデルを参照してください。
field_length - None フィールドの値長を指定します。
field_persistence True フィールドの永続化モードを指定します。Falseの場合は本フィールドはデータベースカラムとして生成されません。
field_nullable True フィールドの値にNullを許容するかを指定します。
field_immutable False フィールドの値が不変かを指定します。Trueの場合、本フィールドの値変更は許可されません。
field_unique False フィールドが複合ユニークインデックスの対象かを指定します。
field_format - None フィールドのフォーマットをjsonschemaもしくは正規表現で指定します。
field_default - None フィールドのデフォルト値を指定します。
field_enum - None フィールドに代入できる値がリテラル候補値である場合にリテラル列挙型で指定します。
field_fsm - None フィールドが状態属性である場合に状態マシンを定義します。
field_metadata - {} フィールドに任意のメタ情報を定義します。メタ情報はdict型のデータを設定してください。
localfields組込メソッドではメタ情報によるフィルタリングが利用できます。ATOMインスタンスをAPIのリクエストbodyに変換する際に役立ちます。


  • 状態マシン

ローカルフィールドには、状態マシンを定義することができます。状態マシンは、有限数の状態値をキーとして使用するマップによって管理され、各状態値は、以下の属性を保有します。

属性 必須 説明
status 状態名です。
execution_method 状態を確定するためのアクションです。
success_transition - アクションに成功したら次に自律遷移する状態です。
failure_transition - アクションに失敗したら次に自律遷移する状態です。
status_value - アクションが成功した場合の状態値です。指定しない場合は、statusが状態値となりますがこの設定で収束値を強制できます。
シングルステートで更新系の状態を管理したい場合などに利用します。
pre_statuses - アクション実行時点の事前状態を強制するオプションです。状態名をリストで管理します。
status_type - FrontalのATOM編集画面に表示される状態遷移図にヒントを与えるための属性です。ランタイムにはなんら影響を及ぼさないパラメータです。


field_fsm: 
  pending_create: 
    execution_method: create
    success_transition: active 
    failure_transition: pending_delete
  active: 
    execution_method: create_confirm
    failure_transition: pending_delete 
  pending_delete: 
    execution_method: delete
    success_transition: deleted 
  deleted: 
    execution_method: delete_confirm
  pending_update:
    execution_method: update
    success_transition: updated
    failure_transition: pending_cancel_update
  updated:
    execution_method: update_confirm
    failure_transition: pending_cancel_update
    status_value: active
  cancel_update:
    execution_method: cancel
    success_transition: updated


reference fields
異なるATOMクラスのフィールドを外部参照するフィールドです。ATOMインスタンスツリー上で参照先のオブジェクトフィールドが変化すると自動伝播されてきます。データベース定義において本フィールドには外部キーが設定されます。
設定項目 必須 初期値 説明
field_name None フィールド名を指定します。Pythonの識別子として利用できる文字列である必要があります。
field_type string フィールドの型を指定します。フィールドに設定可能な基本型は以下の通りです。これらに加えて定義されている他のATOMやATOMのリストが指定できます。
string integer number boolean DateTime object array MU array<MU>
field_dbtype - None フィールドのデータベースカラムの型を指定します。ATOMはデータベーススキーマの生成にModelサービスを利用しています。
Docs » Scenario » モデルを参照してください。
field_length - None フィールドの値長を指定します。
field_persistence True フィールドの永続化モードを指定します。Falseの場合は本フィールドはデータベースカラムとして生成されません。
field_unique False フィールドが複合ユニークインデックスの対象かを指定します。
field_metadata - {} フィールドに任意のメタ情報を定義します。メタ情報はdict型のデータを設定してください。
localfields組込メソッドではメタ情報によるフィルタリングが利用できます。ATOMインスタンスをAPIのリクエストbodyに変換する際に役立ちます。
ref_class None 参照するATOMクラスを指定します。
ref_class_field None 参照するATOMクラスのフィールド名を指定します。


magic fields
マジックフィールドは、field_nameでアクセスした場合は、ローカルフィールドと同等の振る舞いをしますが、field_magic_nameでアクセスした場合は、特殊な動きをするフィールドです。field_magic_nameを参照した場合は、そのタイミングで動的に値を導出します。前述した参照フィールドは、包含関係上の上位オブジェクトから自身に対しての最短パスでのみ値が伝播されるため、上位オブジェクトが包含する別のオブジェクトのフィールドを得ることはできません。マジックフィールドは、同じルートオブジェクト配下にコンポジットされているATOMインスタンスであればどこに存在していても動的に参照して導出できるフィールドです。これは、同階層にいる直接関係を持たないインスタンスをサーチして情報を得る必要がある場合に役立ちます。
設定項目 必須 初期値 説明
field_name None フィールド名を指定します。Pythonの識別子として利用できる文字列である必要があります。
field_type string フィールドの型を指定します。フィールドに設定可能な基本型は以下の通りです。これらに加えて定義されている他のATOMやATOMのリストが指定できます。
string integer number boolean DateTime object array MU array<MU>
field_dbtype - None フィールドのデータベースカラムの型を指定します。ATOMはデータベーススキーマの生成にModelサービスを利用しています。
Docs » Scenario » モデルを参照してください。
field_immutable False フィールドの値が不変かを指定します。Trueの場合、本フィールドの値変更は許可されません。
field_length - None フィールドの値長を指定します。
field_nullable True フィールドの値にNullを許容するかを指定します。
field_enum - None フィールドに代入できる値がリテラル候補値である場合にリテラル列挙型で指定します。
field_persistence True フィールドの永続化モードを指定します。Falseの場合は本フィールドはデータベースカラムとして生成されません。
field_metadata - {} フィールドに任意のメタ情報を定義します。メタ情報はdict型のデータを設定してください。
localfields組込メソッドではメタ情報によるフィルタリングが利用できます。ATOMインスタンスをAPIのリクエストbodyに変換する際に役立ちます。
ref_class None 動的に参照するATOMクラスを指定します。
ref_class_field None 動的に参照するATOMクラスのフィールド名を指定します。*(アスタリスク)を指定した場合は、動的にマッチングしたATOMインスタンス自体を取得できます。
ref_class_match_field - 動的に参照したATOMインスタンスに対してマッチングするフィールド名を指定します。指定しない場合は、field_nameが自動適用されます。
field_magic_name None 導出属性としてのフィールド名を指定します。
field_magic_script - 動的なインスタンスサーチによって取得された値をカスタムスクリプトで加工できます。


Note

マジックフィールドの挙動は複雑で分かりにくいものです。field_nameでアクセスした場合は、ローカルフィールドのように代入されている値を返却しますが、field_magic_nameでアクセスした場合には、代入されている値をキーに動的に参照したいインスタンスをサーチして狙ったフィールドの値を取得します。
インスタンスのサーチは、自身が所属しているインスタンスツリー全体に対して、ref_classのインスタンスを検出し、該当インスタンスのref_class_match_fieldの値がマジックフィールドに代入されている値と一致するものを検索していきます。一致するインスタンスを発見したら参照インスタンスとみなし、ref_class_fieldの値を取得します。さらにfield_magic_scriptが設定されていれば取得した値をカスタムスクリプトに渡して実行し、その結果を値として返却します。

クラウドサービスにおけるIaaSやPaaSのオーケストレーションを実現するためにマイクロサービスのラッパーをATOMで作成するケースがあります。これらはATOMのコンポジット構成でCRUDを行うメソッドを実装、状態マシンを適切に設定し、ATOMのメソッド伝搬機構を利用することで宣言的にパラメータを与え、リソースツリーをターゲットサービス上に生成することができます。しかしながらリソースツリーをトップダウンで処理するだけではAPIのパラメータを補完仕切れないケースがあります。これは、リソース生成時に関連オブジェクト(兄弟や従兄弟に当たるようなリソースインスタンス)の情報を指定しないと生成できないリソースも存在するからです。そのようなユースケースにおいても特殊なプログラミングを避け、宣言的コンフィギュレーションベースで実現するためにマジックフィールドが提供されています。


Tip

field_uniqueモードを指定すると、複合ユニークインデックスが生成されます。マジックフィールドは導出フィールドであり、データベースには保存されないため、ローカルフィールドと参照フィールドのみ指定することができます。

Tip

ATOMオブジェクトのフィールドにメタデータを設定できます。メタデータは、単一階層のKeyValue辞書である必要があります。組込みメソッドlocalfieldsは、metadataキーワード引数によってフィルタリングされたディクショナリを取得できます。さらに、POST/PUTキーとブール値のメタデータは特別なメタデータとして扱われ、API自動生成処理に暗黙的に使用されます。これにより、開発者はPOSTで提供されるものとPUTで提供されるものをカスタマイズできます。

  • フィールドにメタデータを定義したサンプルATOM
category: sample
name: Tenant
attributes:
  identifier:
    field_name: tenantId
    field_type: string
    field_persistence: true
    field_immutable: true
    field_metadata:
      POST: false
      PUT: false
    field_default: uuid.uuid1().hex
  local_fields:
    - field_name: name
      field_type: string
      field_persistence: true
      field_nullable: true
      field_immutable: false
      field_unique: false
      field_metadata:
        POST: true
        PUT: false
    - field_name: region
      field_type: string
      field_persistence: true
      field_nullable: true
      field_immutable: false
      field_unique: false
      field_metadata:
        POST: true
        PUT: false
    - field_name: description
      field_type: string
      field_persistence: true
      field_nullable: true
      field_immutable: false
      field_unique: false
      field_metadata:
        POST: true
        PUT: true
  ref_fields: []
methods:
  class_methods: []
  instance_methods: []
persistence: true
abstract: false
api_generation: false


  • 上記のATOMのメタデータフィルタの使用例
>>> t = atom.Tenant(name="sampleTenant", region="jp", description="simple Tenant")↵
... print(t.localfields())↵
... ↵
↵
{'tenantId': '13a1a528339e11eb89d7acde48001122', 'name': 'sampleTenant', 'region': 'jp', 'description': 'simple Tenant'}
>>> print(t.localfields(metadata={"POST": True}))↵
... ↵
↵
{'name': 'sampleTenant', 'region': 'jp', 'description': 'simple Tenant'}
>>> print(t.localfields(metadata={"PUT": True}))↵
... ↵
↵
{'description': 'simple Tenant'}
>>>

Note

ATOMのローカルフィールドの変更は8世代の履歴管理が暗黙的に行われています。履歴管理は、最初のsaveメソッドが実行されて永続化された後に開始されます。履歴情報を使用してオブジェクト属性をロールバックできます。ロールバックは、discard_changesプロパティメソッドを実行することで実現できます。履歴は_changesというインビジブルフィールドで管理され、JSONシリアライズの対象となるため、トランザクションサーバにもバックアップされます(変更のロールバックなどで利用できる)。saveメソッドによるデータベースへの保存対象にはならないため、メモリ空間とトランザクション空間にのみ存在する履歴情報であることに注意してください。

>>> t = atom.Tenant(name="sample", description="1st commit")↵
... await t.save()↵
>>> print(t._changes)↵
... ↵
↵
{}
>>> t.description = "2nd commit"↵
... print(t.description)↵
... print(t._changes)↵
... ↵
↵
2nd commit
{'description': ['1st commit']}
>>> t.description = "3rd commit"↵
... print(t.description)↵
... print(t._changes)↵
... ↵
↵
3rd commit
{'description': ['1st commit', '2nd commit']}
>>> t.discard_changes↵
... print(t.description)↵
... print(t._changes)↵
... ↵
↵
2nd commit
{'description': ['1st commit']}
>>> t.discard_changes↵
... print(t.description)↵
... print(t._changes)↵
... ↵
↵
1st commit
{'description': []}
>>>


メソッド定義

class method
クラスメソッドを実装定義します。ATOMのクラスメソッドは、通常のPythonにおけるクラスメソッドと同じでメソッド本体を自由に実装できます。インスタンスツリーのようなクラスを横断する概念はないため、ATOMの特徴であるメソッド伝搬の概念も適用されないピュアなクラスメソッドです。ただし、メソッドが実行される名前空間は、Qmonus SDKのビルトイン空間となるため、Docs » リファレンス » ビルトインオブジェクトに記載されている組込オブジェクトが利用できます。
instance method
インスタンスメソッドを実装定義します。ATOMのインスタンスメソッドは、通常のPythonにおけるインスタンスメソッドとは異なり、インスタンスツリー上で伝搬する特性を与えることができます。
設定項目 必須 初期値 説明
propagation_mode True メソッド伝搬有効化モードです。Trueの場合、包含するインスタンスに実装されている同一名称のメソッドを呼び出して伝搬します。
topdown True トップダウンでメソッド実行するかボトムアップにメソッド実行するかを指定します。Trueの場合、自身のメソッド実行が完了してから子のメソッドを実行します。Falseの場合は、子のメソッド実行が完了してから自身のメソッドを実行します。
リソースツリーの生成時は親から、削除時は子から実行することで依存解決ができます。
multiplexable_number 1 ATOMリスト型のフィールドに格納されている複数のインスタンスに対してメソッド呼び出しを多重実行できます。
field_order ascend 伝搬する順序を指定します。ascend descend parallelが指定できます。
ascend: fieldの定義昇順順でシリアルに伝搬します。
descend: fieldの定義降順でシリアルに伝搬します。
parallel: 全ての伝搬フィールドに並列で伝搬します。
リソースツリーの生成時は昇順、削除時は降順となるケースが多いでしょう。
timeout - 300 メソッドのタイムアウトを指定します。
auto_rollback True メソッドの実行に失敗した場合に自動ロールバックを行うかを指定します。
メソッドの実行が正常に完了すると状態マシンに従い、success_transitionへ自律的に遷移していきますが、メソッドの実行が失敗した場合は、トランザクションを一旦中断し、処理を停止します。Trueの場合は、中断すると同時にトランザクションのcancelを起動し、状態マシンに従ってfailure_transitionへの遷移が継続します。


Caution

Qmonus SDKのランライムは、asyncioベースのIOループでシングルスレッドで動作しています。非同期処理を行うメソッドにおいては、async/awaitキーワードの記述漏れに十分注意してください。尚、インスタンスメソッドの伝搬は、非同期メソッドに対してのみ行われる点に注意してください。


特殊メソッド

ATOMでは、以下の組込み演算子は、オーバーライドされています。

特殊メソッド 使用イメージ 説明
__iter__ [print(i) for i in atomObj] オブジェクトの属性値をイテレーションします。
__repr__ print(atomObj) オブジェクトの文字列表現を返します。
__iadd__ atomObj+=response.body JSON化できるオブジェクト( dict str bytes)とのインプレース加算は、一致するキーに値をマージします。
__eq__ print(atomObj1==atomObj2) クラス及び、すべての属性値が一致する場合は真を返します。
__ne__ print(atomObj1!=atomObj2) クラス及び、すべての属性値が一致していない場合は真を返します。
__contains__ print(atomObj2 in atomObj1) オブジェクトが含まれている場合は真を返します。


ATOMに実装されているプロパティメソッド

ATOMでは、以下のプロパティメソッドが実装されています。

プロパティメソッド 説明
dictionary ATOMインスタンスを辞書型(dict)に変換して返却します。
api_format API自動生成モードで生成されるGET APIの表現形式を辞書型(dict)に変換して返却します。
yaml_format api_formatをyaml変換した文字列を返却します。
discard_changes ATOMインスタンスのローカルフィールド値を1世代ロールバックします。
referring ATOMインスタンスが参照されているかどうかをbool値で返却します。
プラグイン開発者が使用することはありません。
transaction_endpoint ATOMインスタンスが利用するトランザクションのエンドポイントを返却します。
プラグイン開発者が使用することはありません。
xcommand ATOMインスタンスに現在指令されているトランザクションコマンドを返却します。
プラグイン開発者が使用することはありません。
forwarding ATOMインスタンスの現在のトランザクション状態が正常ルートかどうかを返却します。
プラグイン開発者が使用することはありません。
ignore_error_mode ATOMインスタンスの現在のトランザクションが強制リカバリもしくは強制キャンセルかどうかを返却します。
プラグイン開発者が使用することはありません。
isTransactionOwner ATOMインスタンスがトランザクションのオーナーかどうかを返却します。
プラグイン開発者が使用することはありません。
serialize ATOMインスタンスのJSONシリアライズ表現を返却します。
プラグイン開発者が使用することはありません。
callback ATOMのメソッド内でのloggingやprintをフックするコールバック関数を返却します。
プラグイン開発者が使用することはありません。


ATOMに実装されている組込みメソッド

ATOMでは、以下の組込みメソッドが実装されています。

組込メソッド 種別 説明
fieldnames class method ATOMクラスに定義されているfield名のリストを返却します。キーワード引数でメタデータを指定したフィルタリングができます。
retrieve class method ATOMインスタンスをデータベースから検索ロードします。
load class method ATOMインスタンスをデータベースからロードします。
getServiceConfig class method Configサービスからリソースを取得します。
FSM class method ステートマシンオブジェクトを取得します。
プラグイン開発者が使用することはありません。
class_hierarchy class method クラスの包含ツリーを返却します。
プラグイン開発者が使用することはありません。
instantiation classmethod パラメータ宣言情報(yaml json dict形式をサポート)からATOMインスタンスツリーを生成します。
rollback_image instance method 前述したdiscard_changesプロパティメソッドを実行した場合のロールバックイメージのATOMインスタンスを取得します。
localfields instance method ローカルフィールドのキーと値を辞書型(dict)に変換して返却します。
search_instance instance method インスタンスツリーから目的のインスタンスをサーチします。
プラグイン開発者が使用することはありません。
save instance method ATOMインスタンスをデータベースに永続化します。
destroy instance method ATOMインスタンスをデータベースから削除します。
begin instance method トランザクションを開始します。
プラグイン開発者が使用することはありません。
checkpoint instance method トランザクションのチェックポイントを実行します。
プラグイン開発者が使用することはありません。
savepoint instance method トランザクションのセーブポイントを実行します。
プラグイン開発者が使用することはありません。
abort instance method トランザクションを中断します。
プラグイン開発者が使用することはありません。
commit instance method トランザクションをコミットします。
プラグイン開発者が使用することはありません。
global_counter instance method グローバルカウンタを払い出します。後方互換の為、残置していますが非推奨です。Counter組込オブジェクトの利用を推奨しています。
timeout_backoff instance method メソッド伝搬によるトランザクションタイムアウトをバックオフさせます。
プラグイン開発者が使用することはありません。
instance_hierarchy instance method インスタンスの包含ツリーを返却します。
プラグイン開発者が使用することはありません。


ATOMオブジェクトのインスタンス化について

ATOMを定義すると、動的にatomという共通コンテキストに格納されます。atomコンテキストについてはDocs » 名前空間 » リファレンス » 共通コンテキストを参照してください。ATOMのインスタンス化は、initializeというインタフェースメソッドの実装に合わせて適切に行う必要があります。initializeメソッドを実装しない場合とinitializeを同期メソッドで定義した場合は、通常のオブジェクトインスタンス同様にインスタンス化できます。initializeメソッドを非同期で実装した場合は、awaitを使用してインスタンス化する必要があります。

initializeインタフェースメソッドは、オブジェクトの初期化動作をプラグイン開発者がカスタマイズするためのもので、ATOMインスタンス化時に暗黙的に呼び出されます。

# initializeメソッドが定義されていない場合、もしくは非同期でない場合のインスタンス化
object = atom.SyncObject()

# initializeメソッドが非同期で定義されている場合のインスタンス化
object = await atom.AsyncObject()

Tip

インスタンス化は、通常のPythonクラス同様にコンストラクタを用いて実行できますが、instantiationクラスメソッドを用いるとyamljson形式のパラメータ定義からインスタンス化することができます。尚、本組込みクラスメソッドは、Qmonus SDK v20.12 and laterで使用できます。

# 通常のコンストラクタを用いてインスタンス化し、インスタンスツリーを構成する例
>>> tenant = atom.Tenant(tenant_name="hoge")↵
... nw = atom.Network(name="nw1")↵
... subnet1 = atom.Subnet(name="subnw1")↵
... subnet2 = atom.Subnet(name="subnw2")↵
... nw.subnets = [subnet1, subnet2]↵
... tenant.networks = [nw]↵
... print(tenant)↵
... ↵
↵
Tenant(instance='VGVuYW50OmM1NTE3OTE4MzUxYzExZWI4NjY2YWNkZTQ4MDAxMTIy', xid=None, xname=None, tenant_id=None, tenant_name='hoge', description=None, networks=[Network(instance='TmV0d29yazpjNTUxODE1NjM1MWMxMWViODY2NmFjZGU0ODAwMTEyMg==', xid=None, xname=None, id=None, name='nw1', description=None, status=None, tenant_id=None, subnets=[Subnet(instance='U3VibmV0OmM1NTE4YjkyMzUxYzExZWI4NjY2YWNkZTQ4MDAxMTIy', xid=None, xname=None, id=None, name='subnw1', description=None, status=None, tenant_id=None, network_id=None), Subnet(instance='U3VibmV0OmM1NTE5NmZhMzUxYzExZWI4NjY2YWNkZTQ4MDAxMTIy', xid=None, xname=None, id=None, name='subnw2', description=None, status=None, tenant_id=None, network_id=None)])])
# yamlでパラメータ定義して、インスタンスツリーを生成する例
>>> yamltext = """↵
... Tenant:↵
...   tenant_name: hoge↵
...   networks:↵
...   - Network:↵
...       name: nw1↵
...       subnets:↵
...       - Subnet:↵
...           name: subnw1↵
...       - Subnet:↵
...           name: subnw2↵
... """↵
... ↵
↵
>>> tenant = await atom.Tenant.instantiation(yamltext)↵
... print(tenant)↵
... ↵
↵
Tenant(instance='VGVuYW50OjBlOWQyN2Q4MzUxZTExZWI4NjY2YWNkZTQ4MDAxMTIy', xid=None, xname=None, tenant_id=None, tenant_name='hoge', description=None, networks=[Network(instance='TmV0d29yazowZTljYmI5YTM1MWUxMWViODY2NmFjZGU0ODAwMTEyMg==', xid=None, xname=None, id=None, name='nw1', description=None, status=None, tenant_id=None, subnets=[Subnet(instance='U3VibmV0OjBlOWNhMjU0MzUxZTExZWI4NjY2YWNkZTQ4MDAxMTIy', xid=None, xname=None, id=None, name='subnw1', description=None, status=None, tenant_id=None, network_id=None), Subnet(instance='U3VibmV0OjBlOWNhZmM0MzUxZTExZWI4NjY2YWNkZTQ4MDAxMTIy', xid=None, xname=None, id=None, name='subnw2', description=None, status=None, tenant_id=None, network_id=None)])])
>>>

Tip

ATOM自身のinstantiationクラスメソッド呼び出しを行うためには入力されたyamlがどのATOMクラスをルートとする定義データかを知っている必要があります。ルートクラスを知る必要がない場合や、ルートクラスが複数ある(yamlがリスト構造)の場合は、atomコンテキストのクラスメソッドであるatom.instantiationを利用することでインスタンスもしくはインスタンスリストを生成することができます。詳しくはDocs » 名前空間 » リファレンス » 共通コンテキストatomコンテキストを参照ください。

Caution

yamlでインスタンスツリー生成したいがスキーマがわからないという方は以下のようにしてスキーマを確認できます。隠しクラスメソッドの_jsonschemasは、ATOMのフィールド定義からJSONスキーマを生成します。

>>> print(MU(atom.Tenant._jsonschemas(ignore_mgmt_field=True, ignore_atom_field=False, target_method=POST)).yaml_format)↵
... ↵
↵
properties:
  Tenant:
    additionalProperties: false
    properties:
      description:
        type:
        - string
        - 'null'
      networks:
        items:
          properties:
            Network:
              additionalProperties: false
              properties:
                description:
                  type:
                  - string
                  - 'null'
                name:
                  type: string
                subnets:
                  items:
                    properties:
                      Subnet:
                        additionalProperties: false
                        properties:
                          description:
                            type:
                            - string
                            - 'null'
                          name:
                            type: string
                          network_id:
                            type:
                            - string
                            - 'null'
                          tenant_id:
                            type:
                            - string
                            - 'null'
                        required:
                        - name
                        type: object
                    required:
                    - Subnet
                    title: Subnet
                    type: object
                  type: array
                tenant_id:
                  type:
                  - string
                  - 'null'
              required:
              - name
              type: object
          required:
          - Network
          title: Network
          type: object
        type: array
      tenant_name:
        type:
        - string
        - 'null'
    type: object
required:
- Tenant
title: Tenant
type: object
>>>


ATOMオブジェクトの永続化について

前述したATOMに実装されている組込みメソッドにおいて最も利用頻度の高い永続化に関する操作について補足します。ATOMは、定義された際にModelサービスと連携し、データベース上に自身のインスタンスを格納するためのテーブルを生成してSQLAlchemyによるORマップクラスに暗黙的にバインドし、以下のCRUDを実現するための組み込みメソッドを提供します。

組込メソッド 種別 引数 データベース操作
save instance method conn=None, tran=None, propagation_mode=True, **kwargs UPSERT
destroy instance method conn=None, tran=None, propagation_mode=True, delay_seconds=0 DELETE
load class method key, conn=None, shallow=False GET
retrieve class method conn=None, shallow=False, order_by=[], offset=0, limit=None, **kwargs SELECT

CRUD operation can be confirmed in the interactive shell as follows:

>>> hello = atom.Hello(msg="Hello, ATOM!!")↵
... await hello.save()↵
... ↵
↵
>>> select * from Hello;↵
| instance | xid | xname | msg |
SGVsbG86OWU3NmFiMTIzNDVjMTFlYmJjMDdhY2RlNDgwMDExMjI= NULL NULL Hello, ATOM!!
>>> instances = await atom.Hello.retrieve()↵
... print(instances)↵
... ↵
↵
[Hello(instance='SGVsbG86OWU3NmFiMTIzNDVjMTFlYmJjMDdhY2RlNDgwMDExMjI=', xid=None, xname=None, msg='Hello, ATOM!!')]
>>> instances[0].msg = "Hello, ATOM!! Nice to meet you."↵
>>> await instances[0].save()↵
... ↵
>>> hello = await atom.Hello.load(instances[0].instance)↵
... print(hello)↵
... ↵
↵
Hello(instance='SGVsbG86OWU3NmFiMTIzNDVjMTFlYmJjMDdhY2RlNDgwMDExMjI=', xid=None, xname=None, msg='Hello, ATOM!! Nice to meet you.')
>>> await hello.destroy()↵
... instances = await atom.Hello.retrieve()↵
... print(instances)↵
... ↵
↵
[]
>>> select * from Hello;↵
Empty set
>>>


Tip

Scenarioなどのコマンドでデータベースセッションをオープンし、ATOMのCRUDを排他的スコープで操作したい場合は、オープンした接続やデータベーストランザクションをconntranキーワード引数で渡すことができます。

async with model.aiodb() as conn:  
   async with conn.begin() as tran:
       await object.save(conn=conn, tran=tran)

Tip

ATOMオブジェクトの情報変更は、通常インスタンスフィールドに新しい値を代入してsaveメソッドを呼び出すことで変更できますが、saveメソッドにキーワード引数でフィールド名=値を指定すると、対応するフィールド情報のみを変更して保存する動作になります。

>>> hello = atom.Hello(msg="Hello, ATOM!!")↵
... await hello.save()↵
... ↵
↵
>>> select * from Hello;↵
| instance | xid | xname | msg |
SGVsbG86YmY4YTQ3ODAzNDVlMTFlYmJjMDdhY2RlNDgwMDExMjI= NULL NULL Hello, ATOM!!
>>> await hello.save(msg="Hello, ATOM!! Nice to meet you.")↵
... ↵
↵
>>> select * from Hello;↵
| instance | xid | xname | msg |
SGVsbG86YmY4YTQ3ODAzNDVlMTFlYmJjMDdhY2RlNDgwMDExMjI= NULL NULL Hello, ATOM!! Nice to meet you.
>>> ↵


Tip

loadメソッドは、Qmonus SDKが自動的に割り当てるインスタンスIDでロードできます。また、identifier fieldが定義されている場合は対応するフィールドの値を指定することにより、インスタンスを取得できます。

>>> e = atom.Employee(employeeCode="E5813836", name="Qmonus太郎", Affiliation="Devops")↵
>>> await e.save()↵
>>> print(e)↵
... ↵
↵
Employee(instance='RW1wbG95ZWU6YWMxZWMwODAzNDVmMTFlYmJjMDdhY2RlNDgwMDExMjI=', xid=None, xname=None, employeeCode='E5813836', name='Qmonus太郎', Affiliation='Devops')
>>> o = await atom.Employee.load("RW1wbG95ZWU6YWMxZWMwODAzNDVmMTFlYmJjMDdhY2RlNDgwMDExMjI=")↵
... print(o)↵
... ↵
↵
Employee(instance='RW1wbG95ZWU6YWMxZWMwODAzNDVmMTFlYmJjMDdhY2RlNDgwMDExMjI=', xid=None, xname=None, employeeCode='E5813836', name='Qmonus太郎', Affiliation='Devops')
>>> o = await atom.Employee.load("E5813836")↵
... print(o)↵
... ↵
↵
Employee(instance='RW1wbG95ZWU6YWMxZWMwODAzNDVmMTFlYmJjMDdhY2RlNDgwMDExMjI=', xid=None, xname=None, employeeCode='E5813836', name='Qmonus太郎', Affiliation='Devops')
>>>


Tip

retrieveメソッドはkwargsで値を指定して検索する機能があります。 以下のように指定することで特定の条件に合ったインスタンスを取得することができます。

>>> hello = atom.Hello(msg="Hello, ATOM!!")↵
... await hello.save()↵
... ↵
↵
>>> select * from Hello;↵
| instance | xid | xname | msg |
SGVsbG86YjJhNWY0N2NjNjQ4MTFlZWJiODUyZTBmNjRkOGEyYzI= NULL NULL Hello, ATOM!!
>>> instances = await atom.Hello.retrieve(msg="Hello, WORLD!!")↵
... print(instances)↵
... ↵

[]
>>> instances = await atom.Hello.retrieve(msg="Hello, ATOM!!")↵
... print(instances)↵
... ↵

[Hello(instance='SGVsbG86YjJhNWY0N2NjNjQ4MTFlZWJiODUyZTBmNjRkOGEyYzI=', xid=None, xname=None, msg='Hello, ATOM!!')]

Tip

ATOMインスタンスツリーが巨大化すると、retrieveloadメソッドは、すべての子インスタンスをデータベースからメモリ上にロードするため、パフォーマンスが低下します。子インスタンスの検索ロードを抑止して最上位階層のオブジェクトのみを検索する場合は、キーワード引数でshallow=Trueを指定することで処理の低下を回避します。子オブジェクトをロードする必要があり、パフォーマンスを維持したい場合は、offset/limitキーワード引数を使用して再帰的に読み取るなどスループットを維持する読み取りを行うよう設計してください。

Tip

destroyメソッドは、delay_secondsを指定することで実行を遅らせることができます。遅延実行タスクは、スケジュールサービスへのタスク予約によって実現されます。予約情報を削除することでキャンセルできます。

Note

データベースへのATOMオブジェクトの永続化については、上記の通りですが、Scenarioのグローバル変数としてATOMを利用する場合や、ATOMのメソッド実行によって自律駆動するトランザクションのスナップショットでは、JSONシリアライズによってメモリストアにATOMオブジェクトが保存されます。これらは、Qmonus SDKによって処理され、プラグイン開発者はそれを意識する必要はありません。ただし、内部動作は理解しておく必要があります。ATOMはjson.dumpsで直列化文字列を生成できます。また、JSON文字列からのインスタンスの復元は、組込み関数deserializeによって実行されます。トランザクションサーバへの名前空間スナップショットとインスタンスリカバリは、このメカニズムを使用しています。詳細は、Docs » リファレンス » ビルトインオブジェクトに記述されているdeserializeを参照してください。


フィールド値の伝搬について

ATOMオブジェクトの参照フィールドは、上位オブジェクトの特定のフィールドへの参照であり、参照フィールドの値は、上位オブジェクトのフィールドの値の更新と連動して自動的に反映されます。

Example class diagram

>>> tenant = atom.Tenant(tenant_name="SampleTenant")↵
... nw = atom.Network(name="SampleNetwork")↵
... subnet1 = atom.Subnet(name="SampleSubnet1")↵
... subnet2 = atom.Subnet(name="SampleSubnet2")↵
... nw.subnets = [subnet1, subnet2]↵
... tenant.networks = [nw]↵
... ↵
↵
>>> print(tenant.tenant_id)↵
... print(tenant.networks[0].tenant_id)↵
... print(tenant.networks[0].subnets[0].tenant_id)↵
... print(tenant.networks[0].subnets[1].tenant_id)↵
... ↵
↵
None
None
None
None
>>> tenant.tenant_id = uuid.uuid1().hex↵
... print(tenant.tenant_id)↵
... print(tenant.networks[0].tenant_id)↵
... print(tenant.networks[0].subnets[0].tenant_id)↵
... print(tenant.networks[0].subnets[1].tenant_id)↵
... ↵
↵
1d20dec034fc11eb8551acde48001122
1d20dec034fc11eb8551acde48001122
1d20dec034fc11eb8551acde48001122
1d20dec034fc11eb8551acde48001122
>>>


メソッド実行の伝搬について

ATOMオブジェクトのインスタンスメソッドが実行され、そのオブジェクトに別のATOMオブジェクトが包含され、実行されたメソッドと同じ名前のメソッドがある場合、伝播が実行されます。伝播アクションは、対応するインスタンスメソッドの伝播モードがTrueの場合のみです。伝播のターゲットが複数存在する場合、多重化可能数を指定して並列に実行することもできます。伝播順序は、デフォルトでクラス定義で宣言されたフィールド順序ですが、メソッドのフィールド順序属性で昇順または降順を指定できます。


トランザクション自動アシスト

状態マシンに関連付けられたATOMインスタンスメソッドが実行されると、トランザクションが暗黙的に発行され、非同期伝播ツリー操作全体のアトミック性が保証されます。


トランザクションタイムアウトのバックオフ

ATOMトランザクションは、開始時にデフォルトのタイムアウト10分で生成されます。メソッド呼び出しが発生すると、トランザクションの残りの寿命が、メソッドの定義時に指定できるタイムアウト値で満たされるかどうかを判断し、不足分を自動的に延長します。ツリーの伝播中に、このメカニズムが動作します。


クラウドリソースのオーケストレーションでは、状態変化の待ち時間が多く、最悪の値を積み上げてタイムアウトを設計した場合、非常に長い時間となります。この場合、トランザクションを駆動しているシナリオサーバがダウンして自律的に中断できない場合、トランザクションサーバがタイムアウトを検出するまで長時間サイレント障害となります。ATOMのタイムアウトバックオフ機構は、不足分を自動延長することでサイレント障害の時間を大幅に短縮します。


シナリオからATOMトランザクションメソッドを呼び出す方法

ATOMの状態マシンに関連づけられたメソッドを呼び出すと、呼び出されたルートオブジェクトでトランザクションを開始します。シナリオ側でトランザクションをオーナーしてそのトランザクションスコープでATOMの状態遷移を駆動させる場合は以下のようにATOMインスタンスに対してトランザクションIDをセットする必要があります。


API自動生成について

API自動生成モードでATOMを定義すると、CRUDに必要なRESTful APIが自動生成されます。包含関係がある場合は、子オブジェクトを操作するためのAPIも生成されます。デフォルトでは、APIが要求された場合、次の組み込みメソッドのみが機能します。

HTTP Method Request Path Built-in Action description
POST /{api_base_path}/{class} save ATOMオブジェクトのインスタンス化を行いデータベースに永続化します。
PUT /{api_base_path}/{class}/{instance} save ATOMオブジェクトインスタンスの情報を変更し、データベースに永続化します。
DELETE /{api_base_path}/{class}/{instance} destroy ATOMオブジェクトインスタンスをデータベースから削除します。
GET /{api_base_path}/{class}/{instance} load ATOMオブジェクトインスタンスをデータベースから取得します。
GET /{api_base_path}/{class} retrieve ATOMオブジェクトインスタンスをデータベースから検索します。

Note

identifier fieldを定義している場合、APIパスの{instance}部分は{identifier}となります。

上記のPOST、PUT、およびDELETEの組込みのCUD動作に加え、プラグイン開発者は動作をカスタマイズできます。プラグイン開発者は、以下のインタフェースメソッドをオーバーライドすることにより、API呼び出し時の動作を拡張できます。

Instance Method description
create POST時に暗黙的に呼び出される特別なインタフェースメソッド。
update PUTで暗黙的に呼び出される特別なインタフェースメソッド。
delete DELETEで暗黙的に呼び出される特別なインタフェースメソッド


ATOM Development Journey







Learn More

ATOM demonstration movie