このガイドでは、ユーザーがプラットフォーム上で新しいタスクやアラートを作成または更新できるようにする、新しいチケットオーケストレータープラグインの作成方法について説明します。このガイドに従えば、開発者は技術的な問題なく、独自に新しいプラグインを作成できるようになるはずです。
前提条件
- Python 3.xのプログラミング経験(中級レベル)。
- Netskope Cloud Exchangeプラットフォームへのアクセス。
- プラグインを作成する必要のある製品またはソリューションへのAPIまたはPython SDKへのアクセス。
- 製品に対する最低限の権限を持つアカウント。
チケットオーケストレーターモジュール
Cloud Exchangeプラットフォームとその Ticket Orchestrator モジュールには、高度なカスタマイズを可能にする豊富な機能セットが付属しているため、以下に示すプラットフォームのさまざまな側面についてよく理解しておくことをお勧めします。
注:このモジュールは、Netskopeからサードパーティツールへのデータ共有をサポートしています。
Netskopeの概念と用語:
- コア:Cloud Exchangeのコアエンジンは、サードパーティ製プラグインとそのライフサイクルメソッドを管理します。プラットフォームと対話してさまざまなタスクを実行するための API エンドポイントがあります。
- モジュール:さまざまなワークフローを実行するために、モジュール固有のプラグインを呼び出す機能的なコード領域。Ticket Orchestratorは、Cloud Exchangeで動作するモジュールの1つです。
- プラグイン:プラグインとは、タスクやアラートを作成または更新するためのロジックを備えたPythonパッケージです。
- プラグイン構成:プラグイン構成とは、必要なパラメータで構成されたプラグインクラスオブジェクトであり、タスクやアラートの作成または更新のためにCloud Exchangeコアエンジンによってスケジュールされます。
- タスク:これらは、JiraやServiceNowなどのプラットフォームで作成または更新されるタスクです。タスククラスにはアラートクラスが含まれます。
- アラート:これらはプラットフォームに送信される通知です。
開発ガイドライン
- すべての Python コードのパッケージ ディレクトリ構造を使用します。
- プラグインパッケージに同梱されているすべてのサードパーティライブラリについて、既知の脆弱性がないか確認してください。
- 標準的なPythonのコーディング規約(https://peps.python.org/pep-0008/)に従うようにしてください。
- Flake8 Lint チェックを実行し、ドキュメント文字列チェックを有効にした状態で合格することを確認してください。1 行の最大長は 80 文字である必要があります。
- Pythonで記述されたスクリプトや統合機能については、必ず単体テストを作成してください。下記の「単体テスト」のセクションを参照してください。
- プラグインアーキテクチャでは状態の保存が可能ですが、状態管理のために巨大なオブジェクトを保存することは避けてください。
- Pythonコードに脆弱性がないか確認してください。
- プラグインのアイコンは10KB未満でなければなりません。必ず会社のロゴ(製品のロゴではなく)を透明なバックグランドで使用してください。 ロゴの推奨サイズは300×50ピクセル、またはそれに近いアスペクト比です。
- 独自のチェックポイントを実装するのではなく、 Cloud Exchangeコアが提供するチェックポイントを使用します。
- プラグインディレクトリ構造のガイドラインに従ってください。
- デスクトップリプションにリンクが含まれている場合は、プレーンテキストではなくハイパーリンクにする必要があります。
- タイムスタンプ値を人間が読みやすい形式に変換します(エポックからDateTimeオブジェクトへ)。
- validateメソッドに渡されるパラメータを適切に検証し、すべてのパラメータに対して適切なヘルプテキストを提供します。
- 障害や重大な状況(レート制限、ペイロードサイズの超過など)が発生した場合に通知を発生させ、プラグインの状態をユーザーに通知するための通知オブジェクト。
- Cloud Exchangeプラットフォームによって渡されるロガー オブジェクトを使用して、適切なログ メカニズムを実装してください。 運用チームがトラブルシューティングを行う際に役立つよう、十分なログ記録が行われるようにしてください。通知に機密データが記録されたり、漏洩したりしないようにしてください。
- プラグインディレクトリ名(sample_pluginなど)がmanifest.jsonと一致していることを確認してください。IDフィールド。
- ステータスコード429に対する再試行メカニズムを必ず追加してください。
- プラグインの機能開発においては、ページネーションを常に考慮に入れるべきです。
- User Agent should be added to the headers while making any API call. Format for the User Agent is netskope-ce-<ce_version>–<module>–<plugin_name>–<plugin_version>. Refer to the URE Microsoft Azure AD or URE Crowdstrike Identity Protect plugin for more details. Note that the plugin version should be dynamically fetched, and to fetch the netskope-ce-<version> string, use the method defined by core.
- API トークンとパスワード フィールドでは、strip() を使用しないでください。
- The log messages should start with <module> <app name> Plugin <configuration_name>. Example: Ticket Orchestrator ServiceNow Plugin <ServiceNow Configuration Name>: <log_message>“. This is a suggestion; you can avoid the configuration name: (logger.info(“<module> <plugin_name> Plugin: <message>”))
- エラーログを記録する際は、可能であれば例外のトレースバックも追加してください。使う: self.logger.error(error, details=traceback.format_exc())
- The Toast message should not contain the <app_name> <module> Plugin: in the message.
- API呼び出し中および呼び出し後に、適切な例外とステータスコードを捕捉するようにしてください。可能であれば、リクエストを行うためのヘルパーメソッドを作成し、必要に応じて適切なパラメータを指定してこのメソッドを呼び出すことができます。
- CHANGELOG.md ファイルには、追加、変更、修正などの適切なタグと、ユーザーにとって分かりやすいメッセージを追加して更新する必要があります。ファイル名がCHANGELOG.mdと完全に一致していることを確認してください。
- 障害や重大な状況(レート制限、ペイロードサイズの超過など)が発生した場合に通知を発生させ、プラグインの状態をユーザーに通知するための通知オブジェクト。
- すべてのパラメータに対して適切なヘルプテキスト(ツールチップ)を提供してください。可能であれば、ツールチップでそのパラメータの重要性を説明するようにしてください。
- プラグイン設定パラメータには必ず意味のある名前とデスクリプションを指定してください。
- パラメータには、適切な設定タイプ(テキスト、数値、パスワード、選択式、複数選択式)を指定してください。
- アウトバウンドリクエスト (API/SDK) を行う際に、 Cloud Exchangeプラットフォームによって渡されるプロキシ構成辞書と SSL 証明書検証フラグを必ず使用してください。
- 必須ではないパラメーターの値を取得するようにしてください .get() メソッドを使用し、 .get() を使うときにデフォルト値を提供します。 方法。
- ステータスコード429に対する再試行メカニズムを必ず追加してください。
- プラグインのドキュメントリプションにリンクが含まれている場合は、ドキュメントページにリダイレクトするハイパーリンクである必要があります。 詳細については、 Threat Exchange Trend Micro Vision Oneプラグインを参照してください。
- ロガーメッセージとトーストメッセージには、APIトークンとパスワードタイプのフィールド値を含めてはいけません。
- プラグインの機能開発においては、ページネーションを常に考慮に入れるべきです。
- All the logger statement should follow below format: logger.info(“<module> <plugin_name> Plugin: <message>”).
プラグインの作成
このセクションでは、プラグインをゼロから作成するプロセスについて説明します。
サンプルプラグインは NetskopeOSSの公開GitHubリポジトリ またはCloud Exchangeナレッジベース(こちら)からダウンロードできます:https://support.netskope.com/hc/en-us/articles/360052128734-Cloud-Threat-Exchange。
開発環境のセットアップ
Python
当システムはPython3(バージョン3.7以降)を使用しています。開発環境にPython3がインストールされていることを確認してください。Pytest は単体テストを実行するために使います。
含まれるPythonライブラリ
次の Python ライブラリはNetskope Cloud Exchangeプラットフォームに含まれています。
| 図書館名 | バージョン |
|---|---|
| aiofiles | 22.1.0 |
| amqp | 5.1.1 |
| anyio | 3.6.2 |
| asgiref | 3.6.0 |
| attrs | 22.2.0 |
| azure-core | 1.26.2 |
| azure-storage-blob | 12.14.1 |
| bcrypt | 4.0.1 |
| boto3 | 1.26.51 |
| botocore | 1.29.51 |
| billiard | 3.6.4.0 |
| celery | 5.2.7 |
| cabby | 0.1.23 |
| cachetools | 5.2.1 |
| celerybeat-mongo | 0.2.0 |
| certifi | 2022.12.7 |
| cffi | 1.15.1 |
| chardet | 5.1.0 |
| charset-normalizer | 3.0.1 |
| click | 8.1.3 |
| click-didyoumean | 0.3.0 |
| click-plugins | 1.1.1 |
| click-repl | 0.2.0 |
| colorama | 0.4.6 |
| colorlog | 6.7.0 |
| cryptography | 39.0.0 |
| cybox | 2.1.0.21 |
| defusedxml | 0.7.1 |
| dnspython | 2.3.0 |
| docker | 6.0.1 |
| fastapi | 0.89.1 |
| furl | 2.1.3 |
| google-api-core | 2.11.0 |
| google-auth | 2.16.0 |
| google-cloud-core | 2.3.2 |
| google-cloud-pubsub | 2.13.12 |
| google-cloud-pubsublite | 1.6.0 |
| google-cloud-storage | 2.7.0 |
| google-crc32c | 1.5.0 |
| google-resumable-media | 2.4.0 |
| googleapis-common-protos | 1.58.0 |
| grpc-google-iam-v1 | 0.12.6 |
| grpcio | 1.51.1 |
| grpcio-status | 1.51.1 |
| gunicorn | 20.1.0 |
| h11 | 0.14.0 |
| idna | 3.4 |
| importlib-metadata | 6.0.0 |
| isodate | 0.6.1 |
| jmespath | 1.0.1 |
| jsonpath | 0.82 |
| jsonschema | 4.17.3 |
| kombu | 5.2.4 |
| libcst | 0.3.21 |
| libtaxii | 1.1.119 |
| lxml | 4.9.2 |
| mongoengine | 0.25.0 |
| more-itertools | 9.0.0 |
| MarkupSafe | 2.1.2 |
| memory-profiler | 0.61.0 |
| mixbox | 1.0.5 |
| mongoquery | 1.4.2 |
| msrest | 0.7.1 |
| multidict | 6.0.4 |
| mypy-extensions | 0.4.3 |
| netskopesdk | 0.0.25 |
| numpy | 1.23.5 |
| oauthlib | 3.2.2 |
| onelogin | 3.1.0 |
| ordered-set | 4.1.0 |
| orderedmultidict | 1.0.1 |
| overrides | 6.5.0 |
| pandas | 1.5.0 |
| packaging | 23.0 |
| passlib | 1.7.4 |
| pycparser | 2.21 |
| prompt-toolkit | 3.0.36 |
| proto-plus | 1.22.2 |
| protobuf | 4.21.12 |
| psutil | 5.9.4 |
| pydantic | 1.10.4 |
| pyasn1 | 0.4.8 |
| pyasn1-modules | 0.2.8 |
| PyJWT | 2.6.0 |
| pymongo | 4.3.3 |
| pyparsing | 3.0.9 |
| python-dateutil | 2.8.2 |
| pyrsistent | 0.19.3 |
| python-multipart | 0.0.5 |
| python3-saml | 1.15.0 |
| pytz | 2022.7.1 |
| PyYAML | 6.0 |
| requests | 2.28.2 |
| requests-oauthlib | 1.3.1 |
| rsa | 4.9 |
| six | 1.16.0 |
| starlette | 0.22.0 |
| sniffio | 1.3.0 |
| s3transfer | 0.6.0 |
| stix | 1.2.0.11 |
| taxii2-client | 2.3.0 |
| typing-inspect | 0.8.0 |
| typing-utils | 0.1.0 |
| typing_extensions | 4.4.0 |
| urllib3 | 1.26.14 |
| uvicorn | 0.20.0 |
| vine | 5.0.0 |
| wcwidth | 0.2.6 |
| weakrefmethod | 1.0.3 |
| websocket-client | 1.4.2 |
| Werkzeug | 2.2.2 |
| xmlsec | 1.3.11 |
| zipp | 3.11.0 |
| requests-mock | 1.7.0 |
カスタムプラグインライブラリを含む
Netskopeは、プラグインが必要とするサードパーティ製のPythonライブラリをすべてプラグインパッケージ自体にバンドルすることを推奨しています。pipインストーラーは、このバンドルを実現するために使用します。ディレクトリを入力として受け取るスイッチが用意されています。 ディレクトリが指定されている場合、pipはそのディレクトリにパッケージをインストールします。
例えば、このコマンドは「cowsay」パッケージを「lib」ディレクトリにインストールします。
> pip install cowsay --target ./lib
これに関する公式ドキュメントについては、 https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-tを参照してください。
上記のlibフォルダからモジュールをインポートする際は、 絶対インポートではなく相対インポートを使用する必要があります。
IDE
推奨されるIDEはPyCharmまたはVisual Studio Codeです。
プラグインディレクトリ構造
このセクションでは、チケットオーケストレータープラグインの一般的なディレクトリ構造について説明します。
/sample_plugin/ ├── /utils/ ├── constants.py ├── __init__.py ├── Changelog.md ├── icon.png ├── main.py └── manifest.json
- __init__.py:Ticket Orchestratorのコードでは、すべてのプラグインパッケージはPythonモジュールとして扱われます。すべてのプラグインパッケージに空の「__init__.py」が含まれていることを確認してください。ファイル。
- CHANGELOG.md:このファイルにはプラグインの更新に関する詳細が含まれており、「追加」、「変更」、「修正」などの適切なタグと、ユーザーにとって分かりやすい適切なメッセージを追加して更新する必要があります。
- icon.png: プラグインのアイコンロゴ。これは、UI 上のプラグインのチクレットと設定カードに表示されます。ロゴには、推奨サイズ 300*50 ピクセルまたは同様のアスペクト比の透明なバックグランドが必要です。
- main.py:このPythonファイルには、create_tasks、update_tasks、sync_states、validate_steps、get_available_fields、get_default_mappings、get_queuesメソッドの具体的な実装を含むPluginクラスが含まれています。必要に応じて、get_fields も実装できます。
- manifest.json:プラグインパッケージのマニフェストファイル。設定可能なすべてのパラメータとそのデータ型に関する情報が含まれています。このファイルには、プラグインの統合に関する詳細情報も記載されています。
- utils/: このディレクトリは ユーティリティ関数を記述し、定数を定義するためのものです。 プラグインの要件に応じて、さまざまなファイルが含まれています。ここにサンプルファイルを示します。
- constants.py:このファイルには、フィールドマッピングなど、プラグイン全体で使用できる定数が含まれています。
ここに記載されているファイルは、プラグインの統合に必須ですが、特定の統合要件に基づいて他のファイルを追加することもできます。
注記
プラグインディレクトリ名を確認してください(例:sample_plugin) は manifest.json と一致しますIDフィールド。
CHANGELOG.md
これはプラグインの更新に関する詳細情報を含むファイルであり、追加、変更、修正などの適切なタグと、ユーザーにとって分かりやすい適切なメッセージで更新する必要があります。
- 追加: 新しい機能が追加されたときに使う
- 修正済み: バグやエラーが修正されたときに使用します。
- 変更済み: プラグインの既存の実装に変更があった場合
サンプルCHANGELOG.md
# 1.0.1 ## Fixed - Fixed pagination while fetching all available fields. # 1.0.0 ## Added - Initial release.
Manifest.json
これは、プラグインに関連するメタ情報を格納する JSON ファイルです。このファイルは、Ticket Orchestrator モジュールによって読み込まれ、UI にプラグインを表示するとともに、必要な構成パラメータ、プラグイン ID、プラグイン名など、プラグインに関する詳細情報を Ticket Orchestrator モジュールが把握できるようにします。
- Ticket Orchestratorがプラグインオブジェクトを適切にインスタンス化できるように、すべてのプラグインには必要な情報を含むこのファイルが含まれている必要があります。
- マニフェスト.json の一般的なパラメーターには次のものがあります。
- name: (文字列) プラグインの名前。(必須)
- id: (文字列) プラグインパッケージのID。Cloud Exchangeにインストールされているすべてのプラグインで、一意であることを確認してください。IDはプラグインパッケージのディレクトリ名と一致する必要があります。(必須)
- version: (文字列) プラグインのバージョン。MAJOR.MINOR.PATCH の使用 (例:1.0.1)バージョン管理方式を推奨しますが、特に制限はありません。(必須)
- デスクリプション: (文字列) プラグインのデスクリプション。 プラグインの機能と使用手順について説明した詳細なデスクリプションを提供します。 プラットフォーム上で課題/チケットを作成してください。このデスク リプションは、プラグイン設定カードに表示されます。 (必須)。デスクトップリプションにリンクが含まれている場合は、プレーンテキストではなくハイパーリンクにする必要があります。
- pulling_supported: (boolean) アラートを取得する可能性がある場合は true に設定します。例えば、Netskopeにはこの機能があります。
- receiving_supported: (ブール値) チケットを作成したり通知を送信したりする場合は、true に設定してください。
- 設定: (配列) プラグインに必要なすべてのパラメーターに関する情報 (名前、タイプ、ID など) を含む JSON オブジェクトの配列。ネストされた JSON オブジェクトによくあるパラメーターについては、以下で説明します。
- ラベル:パラメータの名前。これはプラグインの設定ページに表示されます。(必須)
- key: プラグイン設定が使用される Python dict オブジェクト内のキーとして使用される一意のパラメーター キー。 (必須)
- type: パラメータの値の型。使用可能な値は、「text」、「password」、「number」、「choice」、「multichoice」です。(必須)詳細については、下記のプラグイン設定パラメータの種類を参照してください。
- default: このパラメータのデフォルト値。この値は、チケットオーケストレーターのUIにあるプラグイン設定ページに表示されます。サポートされているデータ型は、「テキスト」、「数値」、「リスト」(複数選択型の場合)です。(必須)
- mandatory: このパラメータが必須かどうかを示すブール値。パラメータが必須の場合、Ticket Orchestrator UI ではパラメータに空の値を指定することはできません。許容される値は「true」と「false」です。(必須)
- デスクリプション: パラメータのヘルプ テキスト レベルのデスクリプション。パラメータと期待値に関する詳細を提供します。 この文字列は、プラグインの設定ページにヘルプテキストとして表示されます。(必須)
- 選択肢:キーと値をJSONキーとして含むJSONオブジェクトのリスト。このパラメータは「type」:「choice」および「multichoice」でのみサポートされています。
Plugin Configuration Parameter types
プラグインのmanifest.jsonファイルのconfigurationセクションに、必要なプラグイン設定パラメータがすべて記載されていることを確認してください。
Password Parameter
このパラメータは、API エンドポイントでの認証のためのシークレット/パスワードを保存するために使用されます。 パスワードを型とするパラメータは、プラグイン設定ページにパスワード入力欄が表示され、プラットフォームによって難読化および暗号化されます。
サンプルJSON:
"configuration": [
{
"label": "API Token",
"key": "api_token",
"type": "password"
},
]
プラグイン設定画面:

Text Parameter
このパラメータは、ベースURL、ユーザー名などの文字列情報を格納します。 このパラメータは、プラグイン設定ページに通常のテキスト入力欄として表示されます。
サンプルJSON:
"configuration": [
{
"label": "Tenant Name",
"key": "tenant_name",
"type": "text"
},
]
プラグイン設定画面:

Number Parameter
このパラメータは数値/浮動小数点値を保存するために使用されます。 このパラメータは、プラグイン設定ページに数値入力ボックスとして表示されます。(CTEより)
サンプルJSON:
"configuration": [
{
"label": "Maximum File hash list size in MB.",
"key": "max_size",
"type": "number"
},
]
プラグイン設定画面:

Choice Parameter
このパラメータは、列挙パラメータの値を格納するために使用されます。 このパラメータは、プラグイン設定ページにドロップダウンボックスとして表示されます。(ログシッパーから)
サンプルJSON:
{
"label": "Syslog Protocol",
"key": "syslog_protocol",
"type": "choice",
"choices": [
{
"key": "TLS",
"value": "TLS"
},
{
"key": "UDP",
"value": "UDP"
},
{
"key": "TCP",
"value": "TCP"
}
],
"default": "UDP",
"mandatory": true,
"description": "Protocol to be used while ingesting data."
},
プラグイン設定ビュー:
入力を選択した後:

Multichoice Parameter
このパラメータは、複数の選択肢の値を保存するために使用します。 このパラメータは、プラグイン設定ページにドロップダウンボックスとして表示され、複数の値を選択できるようになります。(チケットオーケストレーターより)
サンプルJSON:
"configuration": [
{
"label": "Severity",
"key": "severity",
"type": "multichoice",
"choices": [
{
"key": "Unknown",
"value": "unknown"
},
{
"key": "Low",
"value": "low"
},
{
"key": "Medium",
"value": "medium"
},
{
"key": "High",
"value": "high"
},
{
"key": "Critical",
"value": "critical"
}
],
"default": [
"critical",
"high",
"medium",
"low",
"unknown"
],
"mandatory": false,
"description": "Only indicators with matching severity will be saved."
}
]
プラグイン設定ビュー:

Toggle Parameter
このパラメータはブール値を格納します。トグル有効はTrue、トグル無効はFalseです。
使う システム プロキシ ('proxy'): Use system proxy configured in Settings.(Default: False)
プラグイン設定ビュー:
1. トグルがオフの場合
2. トグルがオンの場合。

注記
このパラメータはCoreによって提供されるものであり、プラグインのmanifest.jsonファイルから追加することはできません。
main.py
このPythonファイルには、プラグインのコア実装が含まれています。
標準輸入品
rom netskope.integrations.itsm.plugin_base import (
PluginBase,
ValidationResult,
MappingField,
)
from netskope.integrations.itsm.models import (
FieldMapping,
Queue,
Task,
TaskStatus,
Alert,
)
PluginBase 変数
PluginBase は、プラグインのライフサイクル中に使用できる変数へのアクセスを提供します。 メソッド。 以下は変数の一覧です。
| 変数名 | Usage | デスクリプション |
|---|---|---|
| self.logger | self.logger.error(“メッセージ”) self.logger.warn(“メッセージ”) self.logger.info(“メッセージ”) | コアによって提供されるロガーハンドル。このオブジェクトを使用して重要なイベントを記録します。 ログはCT監査ログに表示されます。ロガーオブジェクトのドキュメントを参照してください。 |
| self.configuration | self.configuration.get()<attribute-key-name> | プラグインインスタンスの設定オブジェクトのJSON表現。これを使用して、認証クレデンシャル、サーバーの詳細などの構成属性にアクセスします。manifest.json に記載されている属性のキー名を使用します。 |
| self.last_run_at | self.last_run_at: の場合self.last_run_at.timestamp() この形式は、最後の実行時間をエポック形式に変換するためのものです。 | プラグインのプルメソッドが最後に正常に実行された時刻のタイムスタンプを提供します。Cloud Exchangeコアは、pull()の実行が成功するたびにチェックポイント時刻を保持します。初回実行時の値はNoneになります。オブジェクトのデータ型はdatetimeです。 |
| self.storage | Cloud Exchangeは、プラグインに状態を維持するためのメカニズムを提供します。このオブジェクトは、後続の呼び出し時に必要となる状態を保持します。 このオブジェクトのデータ型はPythonの辞書型です。 | |
| self.notifier | self.notifier.info(“message”) self.notifier.warn(“message”) self.notifier.error(“message”) | このオブジェクトは、Cloud Exchangeコアの通知機能のハンドルを提供します。このオブジェクトを使用して、プラットフォームに通知をプッシュします。 通知はチケットオーケストレーターのユーザーインターフェースに表示されます。ユーザーが読んで必要な行動を取れるよう、メッセージ は要約された情報が含まれていることを確認してください。 例えばNetskopeプラグインの通知機能は、push()メソッドが製品の8MB制限を超えた場合に通知します。 |
| self.proxy | requests.get(url=url,プロキシ=self.proxy) | システムのプロキシ設定が設定されている場合はそのハンドル、設定されていない場合は {}。 |
| self.ssl_validation | requests.get(url=url,verify=self.ssl_validation) | REST API呼び出しに対してSSL検証を強制するかどうかを示すブール値。 |
プラグインクラス
- プラグインクラスはPluginBaseクラスを継承する必要があります。PluginBase クラスはNetskope .integrations.cto.plugin_baseで定義されています。
- Plugin クラスが、create_tasks、update_tasks、sync_states、validate_steps、get_available_fields、get_default_mappings、および get_queues メソッドの実装を提供していることを確認してください。
- プラグインの機能開発においては、ページネーションを常に考慮に入れるべきです。
- プラグインクラスには、サードパーティAPIとの接続と認証を確立するために必要なすべてのパラメータが含まれます。
- PLUGIN_NAME、LIMITなどの定数は宣言する必要があります。
"""Sample plugin implementation.
This is a sample implementation of base PluginBase class, which explains the concrete implementation of the base class.
"""
from netskope.integrations.itsm.plugin_base import (
PluginBase,
ValidationResult,
MappingField,
)
from netskope.integrations.itsm.models import (
FieldMapping,
Queue,
Task,
TaskStatus,
Alert,
)
PLUGIN_NAME = "<module> <plugin_name> Plugin"
class Plugin(PluginBase):
"""SamplePlugin class having concrete implementation for creating and updating tasks or alerts.
This class is responsible for implementation of the create_tasks, update_tasks, sync_states, validate_steps, get_available_fields, get_default_mappings, and get_queues methods with proper return types.
Hence it's lifecycle execution can be scheduled by the CTO core engine.
"""
def create_tasks
- このメソッドは、API エンドポイントを使用してターゲット プラットフォームでチケットを作成したり通知を送信したりするロジックを実装します。
- 例外が発生した場合、またはステータスコードが成功でない場合は、適切なログを出力してください。
- 必要な詳細情報を含むTaskオブジェクトを必ず返してください。
- 各フィールドのボディ構造とスキーマを確認し、それに応じてAPI呼び出しでボディを送信します。
- 接続とHTTPレスポンスコードに関するすべての例外を処理し、エラーが発生した場合は、エラーメッセージとロガーとともに例外を発生させます。
- API呼び出しを行う際には、ユーザーエージェントをヘッダーに追加する必要があります。ユーザーエージェントのフォーマット: Netskope -ce-<ce_version>-<module>-<plugin_name>-<plugin_version>。
- この方法では、「 システムプロキシ」トグルの「proxy」変数は API呼び出し時に である必要があります。
- ステータスコード429(リクエストが多すぎます)の処理方法を確認してください。推奨されるアプローチは以下のとおりです。
- ステータスコード429(リクエストが多すぎる)の場合に再試行メカニズムを実装する
"""Create an issue/ticket on platform."""
def create_task(self, alert: Alert, mappings: Dict, queue: Queue) -> Task:
"""Create an issue/ticket on platform."""
params = self.configuration["auth"]
project_id, issue_type = queue.value.split(":")
# Filter out the mapped attributes based on given project and issue_type
create_meta = self._get_createmeta()
mappings = self._filter_mappings(create_meta, mappings)
body = self._generate_body(create_meta, mappings)
... # Set fields with nested structure
headers = {
"Accept": "application/json",
"Content-Type": "application/json",
}
response = requests.post(
f"{params['url'].strip('/')}/rest/api/3/issue",
json=body,
auth=HTTPBasicAuth(params["email"], params["api_token"]),
headers=add_user_agent(headers),
proxies=self.proxy,
)
if response.status_code == 201:
result = response.json()
# Need result for to create link in Task()
return Task(
id,
status,
link,
)
def update_tasks
- このメソッドは、API エンドポイントを使用してターゲット プラットフォームのタスクを更新するロジックを実装します。
- create_tasks と似ていますが、既存のタスクを変更する点が異なります。
- 例外が発生した場合、またはステータスコードが成功でない場合は、適切なログを出力してください。
- 必要な詳細情報を含む Task オブジェクトを返すようにしてください。
def update_task(
self, task: Task, alert: Alert, mappings: Dict, queue: Queue
) -> Task:
"""Add a comment in existing issue."""
params = self.configuration["auth"]
comment = {
"body": self._get_atlassian_document(
f"New alert received at {str(alert.timestamp)}."
)
}
response = requests.post(
f"{params['url'].strip('/')}/rest/api/3/issue/{task.id}/comment",
headers=add_user_agent(),
json=comment,
auth=HTTPBasicAuth(params["email"], params["api_token"]),
proxies=self.proxy,
)
if response.status_code == 201:
return task
elif response.status_code == 404:
self.logger.info(
f"{PLUGIN_NAME}: Issue with ID {task.id} no longer exists on platform"
f" or the configured user does not have permission to add"
f"comment(s)."
)
return task
else:
raise requests.HTTPError(
f"{PLUGIN_NAME}: Could not add comment. The existing issue on the platform with ID {task.id}."
)
def sync_states
- 対象プラットフォームでチケットのステータスが変更された場合、最新のステータスを取得し、Netskope Cloud Exchangeの現在のステータスを更新します。
- チケットのステータスが当社の TaskStatus モデルと一致する場合、そのステータスが特定のチケットに反映されます。そうでない場合は、ステータスはOtherになります。
- APIエンドポイントがサポートする最大ペイロードサイズを超えた場合は必ず対処してください。 このケースに対処する方法は複数考えられます。
- API エンドポイントがペイロード サイズが固定された複数のリクエストをサポートしている場合は、データをチャンクで送信します。
- ステータスコード429(リクエストが多すぎます)の処理方法を確認してください。推奨されるアプローチは以下のとおりです。
- ステータスコード429(リクエストが多すぎる)の場合に再試行メカニズムを実装する
def sync_states(self, tasks: List[Task]) -> List[Task]:
"""Sync all task states."""
params = self.configuration["auth"]
task_ids = [task.id for task in tasks]
task_statuses = {}
body = {
"jql": f"key IN ({','.join(task_ids)})",
"maxResults": 100,
"fields": ["status"], # We only need status of tickets
"startAt": 0,
"validateQuery": "none",
}
while True:
response = requests.post(
f"{params['url'].strip('/')}/rest/api/3/search",
headers=add_user_agent(),
json=body,
auth=HTTPBasicAuth(params["email"], params["api_token"]),
proxies=self.proxy,
)
response.raise_for_status()
if response.status_code == 200:
json_res = response.json()
body["startAt"] += json_res["maxResults"]
if len(json_res["issues"]) == 0:
break
for issue in json_res["issues"]:
task_statuses[issue.get("key")] = (
issue.get("fields", {}).get("status", {}).get("name")
).lower()
for task in tasks:
if task_statuses.get(task.id):
task.status = STATE_MAPPINGS.get(
task_statuses.get(task.id), TaskStatus.OTHER
)
else:
task.status = TaskStatus.DELETED
return tasks
def validate_steps
これはPluginBaseクラスの抽象メソッドです。
- このメソッドは、プラグイン構成を作成する際に渡されたプラグイン構成および認証パラメータを検証します。
- このメソッドは、新しい設定が作成または更新された場合にのみ呼び出されます。
- プラグイン設定の検証メソッドでは、空欄の検証と型チェックの検証を別々に行う必要があります。
- 必須パラメータがすべて適切なデータ型で渡されていることを検証します。
- 認証パラメータと API エンドポイントを検証して、プラグインのライフサイクルがスムーズに実行されるようにします。
- 検証中に、API トークンとパスワード フィールドを除く、ベース URL、電子メール、ユーザー名などの構成パラメータに対して、strip() を使用します。
- 検証の成功または失敗を示す成功フラグと、検証失敗の理由を含む検証メッセージを含むValidationResultオブジェクト(ValidationResultデータモデルを参照)を返します。
def validate_step(
self, name: str, configuration: dict
) -> ValidationResult:
"""Validate a given configuration step."""
if name == "auth":
return self._validate_auth(configuration)
elif name == "params":
return self._validate_params(configuration)
else:
return ValidationResult(
success=True, message="Validation successful."
)
def get_available_fields
- このメソッドは、ドロップダウンリストから設定が選択されたときに、UIにレンダリングされるフィールドのリストをキューに返します。
- このメソッドは、サポートされているフィールドのみを返す必要があります。
- 膨大な数のフィールドの場合にプラグインが失敗しないように、必ずページ分割された API を使用してください。
def get_available_fields(self, configuration: dict) -> List[MappingField]:
"""Get list of all the available fields for issues/tickets."""
params = configuration["auth"]
response = requests.get(
f"{params['url'].strip('/')}/rest/api/3/field",
auth=HTTPBasicAuth(params["email"], params["api_token"]),
headers=add_user_agent(),
proxies=self.proxy,
)
response.raise_for_status()
if response.status_code == 200:
return list(
map(
lambda item: MappingField(
label=item.get("name"), value=item.get("key")
),
response.json(),
)
)
else:
raise requests.HTTPError(
f"{PLUGIN_NAME}: Could not fetch available fields from platform."
)
def get_default_mappings
ユーザーがデフォルトフィールドをターゲットプラットフォームに送信したい場合は、このメソッドを実装する必要があります。そうでない場合は、空のリストを返します。
def get_default_mappings(self, configuration: dict) -> List[FieldMapping]:
"""Get default mappings."""
return [
FieldMapping(
extracted_field="custom_message",
destination_field="summary",
custom_message="Netskope $appCategory alert: $alertName",
),
FieldMapping(
extracted_field="custom_message",
destination_field="description",
custom_message=(
"Alert ID: $id\nApp: $app\nAlert Name: $alertName\n"
"Alert Type: $alertType\nApp Category: $appCategory\nUser: $user"
),
),
]
def get_queues
- これにより、チケットを作成する必要のあるすべての宛先プロジェクトが取得されます。
- チケットが作成されなかった場合は、必要なログステートメントとともに適切な例外を発生させる。
- プロジェクトがたくさんある可能性があるため、ページネーションを使います。
def get_queues(self) -> List[Queue]:
"""Get list of projects as queues."""
params = self.configuration["auth"]
start_at, is_last = 0, False
projects = []
issue_types = self.configuration["params"]["issue_type"]
issue_types = list(map(lambda x: x.strip(), issue_types.split(",")))
total_ids = []
while not is_last:
response = requests.get(
f"{params['url'].strip('/')}/rest/api/3/project/search",
params={"startAt": start_at, "maxResults": 50},
headers=add_user_agent(),
auth=HTTPBasicAuth(params["email"], params["api_token"]),
proxies=self.proxy,
)
response.raise_for_status()
if response.status_code == 200:
json_res = response.json()
is_last = json_res["isLast"]
start_at += json_res["maxResults"]
# Create combination of projects and issue types
for project in json_res.get("values"):
total_ids.append(project.get("id"))
# batches of 650 Project ids if we pass more than that
# it will throw 500 server error
if is_last or (start_at % 650) == 0:
total_project_ids = ",".join(total_ids)
meta = self._get_createmeta(
self.configuration, {"projectIds": total_project_ids},
)
projects_list = meta.get("projects")
for project in projects_list:
if not project:
continue
for issue_type in project.get("issuetypes"):
# Issue type is defined as a "key:value" string
# Value of queue is defined as "project_id:issue_type" string
if issue_type.get("name") not in issue_types:
continue
projects.append(
Queue(
label=f"{project.get('name')} - {issue_type.get('name')}",
value=f"{project.get('id')}:{issue_type.get('name')}",
)
)
total_ids = [] # restart the batch ids
else:
raise requests.HTTPError(
f"{PLUGIN_NAME}: Could not fetch projects from platform."
)
return projects
データモデル
このセクションでは、データモデルとその特性を一覧表示します。
キューデータモデル
- キューはプラットフォーム内のプロジェクトです
- このキューには、チケットの作成や通知の送信を行うことができます。
| 名前 | タイプ | デスクリプション |
|---|---|---|
| label | string | UIに表示されるプロジェクト名。 |
| value | string | 値を鍵として使うことができます。 |
| default_mapping | Dict | これは、デフォルトマッピングと重複排除のデフォルトマッピングに使用できます。 |
from netskope.integrations.itsm.models import Queue
project_id, issue_type = queue.value.split(":")
アラートデータモデル
- アラートとは、サードパーティプラットフォームに送信できる通知のことです。
- アラートは create_tasks と update_tasks で使用されます。
| 名前 | タイプ | デスクリプション |
|---|---|---|
| id | string | アラートオブジェクトの一意のID。 |
| configuration | string | 認証クレデンシャルやサーバーなどの構成属性にアクセスするための JSON 表現。 |
| alertName | string | アラートの名前。 |
| alertType | string | アラートの種類。 |
| アプリ | string | アプリ名。 |
| appCategory | string | アプリのカテゴリ。 |
| user | string | ユーザー名。 |
| タイプ | string | レコードの種類。 |
| timeStamp | datetime | 事象が発生した正確な時刻。 |
| rawAlert | dict | アラート用の追加フィールド。 |
from netskope.integrations.itsm.models import Alert
comment = {
"body": self._get_atlassian_document(
f"New alert received at {str(alert.timestamp)}."
)
}
タスクデータモデル
- タスクとは、プラットフォーム上で作成または更新されるチケットのことです。
- タスクには、その状態を決定するtaskStatusというプロパティがあります。
- タスクはcreateTask、updateTask、syncStatesメソッドを使います。
| 名前 | タイプ | デスクリプション |
|---|---|---|
| id | string | タスクオブジェクトの一意のID。 |
| status | TaskStatus | チケットのステータス。例えば、「進行中」など。 |
| dedupeCount | int | 重複チケット数。 |
| link | str | チケットのリンク。 |
| deletedAt | datetime | タスクが削除されたとき。 |
| configuration | str | 認証クレデンシャルやサーバーなどの構成属性にアクセスするための JSON 表現。 |
| createdAt | datetime | タスクが作成されたとき。 |
| businessRule | str | データをフィルタリングするためのクエリ。 |
| alert | 警告 | 通知。上記(アラートデータモデル)で述べたとおりです。 |
from netskope.integrations.itsm.models import Task
return Task(
id=result.get("key"),
status=STATE_MAPPINGS.get(issue_status, TaskStatus.OTHER),
link=(
f"{self.configuration['auth']['url'].strip('/')}/browse/"
f"{result.get('key')}"
),
)
フィールドマッピングデータモデル
このモデルは、アラート フィールドをターゲット プラットフォーム フィールドにマッピングするために使用されます。
| 名前 | タイプ | デスクリプション |
|---|---|---|
| extracted_field | string | 作成したいフィールドまたはカスタムメッセージ |
| destination_field | string | 対象プラットフォーム分野 |
| custom_message | string | カスタムメッセージを追加したい場合。 |
from netskope.integrations.itsm.models import FieldMapping
return [
FieldMapping(
extracted_field="custom_message",
destination_field="summary",
custom_message="Netskope $appCategory alert: $alertName",
)
ログ記録
Ticket Orchestratorは、ログ記録用のロガーオブジェクトのハンドルを提供します。
- コード内でprint文を使用することは避けてください。
- このオブジェクトは、タイムスタンプフィールドを使用して中央のCloud Exchangeデータベースにログを記録します。 サポートされているログレベルは、情報、警告、エラーです。
- API認証情報やその他の機密情報がログメッセージに漏洩しないようにしてください。
- Cloud Exchangeプラットフォームによって渡されるロガー オブジェクトを使用して、適切なログ メカニズムを実装してください。
- 運用チームがトラブルシューティングを行う際に役立つよう、十分なログ記録が行われるようにしてください。
- 通知やログに機密データが記録されたり、漏洩したりしないようにしてください。
self.logger.error(
f"{PLUGIN_NAME}: Error log-message goes here."
)
self.logger.warn(
f"{PLUGIN_NAME}: Warning log-message goes here."
)
self.logger.info(
f"{PLUGIN_NAME}: Info log-message goes here."
)
通知
Ticket Orchestrator は通知オブジェクトのハンドルを提供します。このハンドルは Ticket Orchestrator UI 上で通知を生成するために使用できます。
- このオブジェクトは、 Cloud Exchangeプラットフォームから Plugin オブジェクトに渡されます。 すべてのプラグイン統合は ユーザーに即座に通知する必要のある障害が発生した場合はいつでもこのオブジェクトを使用できます。 通知のタイムスタンプは、 Cloud Exchangeプラットフォームによって管理されます。
- このオブジェクトは、障害の深刻度に応じて色分けされた通知をUI上に表示します。サポートされている通知の重要度は、情報、警告、エラーです。
- 通知メッセージに、API認証情報やその他の機密情報が含まれていないことを確認してください。
- 障害や重大な状況(レート制限、ペイロードサイズの超過など)が発生した場合に通知を発生させ、プラグインの状態をユーザーに通知するための通知オブジェクト。
self.notifier.info(
f"{PLUGIN_NAME}: Info notification-message goes here."
)
self.notifier.error(
f"{PLUGIN_NAME}: Error notification-message goes here."
)
self.notifier.warn(
f"{PLUGIN_NAME}: Warning notification-message goes here."
)
Testing
Linting
ビルドプロセスの一環として、いくつかのリンターを実行して、 プログラミングエラー、スタイルエラー、および潜在的なセキュリティ問題を検出します。
Flake8
これは基本的なリンターです。すべての依存関係を保持しなくても実行でき、よくあるエラーを検出できます。また、標準的なpython pep8フォーマットスタイルを強制するために使うこのlinterも使っています。 まれに、このリンターから返されるエラーや警告を無効にする必要が生じる場合があります。エラーを無効にしたい行に、次のようなインラインコメントを追加することでこれを実現します。
# noqa: <error-id>
例:
example = lambda: 'example' # noqa: E731
インラインコメントを追加する場合は、必ず無効にするエラーコードも含めるようにしてください。そうすれば、同じ行に他のエラーがあった場合、それらが報告されます。
詳細情報: https://flake8.pycqa.org/en/latest/user/violations.html#in-line-ignoring-errors
Flake8リンターでは、PEP8スタイルのドキュメント文字列チェックも有効になっています。したがって、すべての関数/モジュールに適切なドキュメンテーション文字列が追加されていることを確認してください。
単体テスト
単体テストを実施し、コードの小さな単位を隔離された状態で、かつ決定論的にテストするようにしてください。単体テストでは、外部 API との通信や モックの使用を避けるようにしてください。 単体テストによってコードカバレッジが70%以上であることを確認してください。
環境設定
単体テストを行うには、統合または自動化スクリプトをプラグインディレクトリ構造で開発する必要があります。 セットアップを実行するために必要なすべてのPythonモジュール依存関係をインストールするには、PIPを使用します。 テストを実行する前に、Cloud Exchangeコアリポジトリのrequirements.txtに記載されている必要な依存関係をすべてインストールしてください。
ユニットテストを作成する
ユニットテストは、 <your_plugin_name>_test.pyという名前の別の Python ファイルに記述してください。単体テストファイル内では、各単体テスト関数は test_<your test case> という名前を付ける必要があります。単体テストの書き方やフォーマットに関する詳細は、 PyTest ドキュメントをご覧ください。
Mocking
モックにはpytest-mockを使います。 pytest-mockはデフォルトで有効になっており、上記の基本環境にインストールされています。モッカーオブジェクトを使うには、テスト関数のパラメータとしてパスするだけです。モッカーは プラグインクラスオブジェクトと外部APIの両方をモックできます。
例:CLSから
def test_push(mocker, common_config):
"""To test push method of CLSPlugin."""
cls_plugin = CLS_Plugin(
,
common_config,
None,
None,
logger,
source="",
mappings=,
)
# Initialize required classes
syslogger = logging.getLogger(
"SYSLOG_LOGGER_{}".format(threading.get_ident())
)
mocker.patch(
"netskope.plugins.Default.cls.main.CLSPlugin.init_handler",
return_value=syslogger,
)
try:
cls_plugin.push(data, data_type, subtype)
except Exception as e:
assert False, f"Push raised exception: {e}"
ユニットテストを実行する
$ PYTHONPATH=. pytest
Cloud Exchangeへのプラグイン展開
プラグインをパッケージ化する
Cloud Exchangeは、開発されたプラグインをzipおよびtar.gz形式で提供することを想定しています。
パッケージを圧縮するには、以下のコマンドを実行してください。
zip -r sample_plugin.zip sample_plugin
以下のコマンドを実行してtar.gzパッケージを生成します。
tar -zcvf sample_plugin.tar.gz sample_plugin
プラグインをアップロードする
この zip または tar.gz をCloud Exchangeプラットフォームにデプロイするには:
- Cloud Exchangeプラットフォームにログインします。
- Settings > Plugin .
へ移動してください。
- Add New Plugin .
をクリックしてください
- Browseをクリックしてください。
- zipファイルまたはtar.gzファイルをSelect 。

- Uploadをクリックしてください。
Add a Repo
Cloud Exchangeプラットフォームにプラグインをデプロイするには、 Cloud Exchangeプラットフォームにリポジトリを追加します。
- Cloud Exchangeプラットフォームにログインします。
- Settings > Plugin Repository .
へ移動してください。
- Configure New Repository .
をクリックしてください
- リポジトリ名、リポジトリURL、ユーザー名、および個人アクセストークンを入力してください。
- Saveをクリックしてください。

- Settings > Pluginsへ移動してください。
- Select Repository name リポジトリのドロップダウンから。

Deliverables
Plugin Guide
- プラグインガイドは、リリースごとに必ず更新してください。
- プラグインガイドには以下の内容を含める必要があります。
- Compatibility.
- リリースノート
- Description.
- Prerequisites.
- Permissions.
- 製品の認証。
- Netskope ITSMプラグインの設定。
- 弊社が開発したサードパーティ製チケットオーケストレータープラグインの設定。
- ビジネスルールの設定。
- キューの設定。
- Validation.
デモビデオ
Ticket Orchestratorプラグインの開発が成功したら、デモビデオを作成し、プラグインのエンドツーエンドのワークフローを示してください。

