Rescale API with python Hands-on


Rescale API with PythonによるJobの投入
========================================

## [STEP0] pre-conditions

### rescale で実行する "hellow world" の作成

シェルに以下のコマンドを貼り付けてください。`sample.sh` が作成されます。


```bash:command
uploadFile='sample.sh'
cat << EOF > ${uploadFile}
#!/bin/sh
date
pwd
ls -l
echo "Hello World"
sleep 300
EOF
```

ファイルができたことを確認します。


```bash:command
ls -l
```

### API Key と プラットフォームの設定

- `apikey` にはブラウザー上で作成したRescaleAPIをコピーしはりつけます
- `platform` は、 USのプラットフォームの場合 `https://platform.rescale.com/` 日本の場合 `https://platform.rescale.jp/` を入れます

以下 python での操作となります。

```bash:command
python
```

```python
apikey=<'作成したAPI KEY'>
platform="https://platform.rescale.com/"
```

---------------------------

## [STEP1] ファイルのアップロード

もし python を起動していなければ、シェルからpythonを起動してください

```bash:command
python
```


### APIを使用したファイルのアップロード　


```python
import requests

my_token = 'Token' + ' ' + apikey
url = platform + "api/v3/files/contents/"

print(my_token)
print(url)

res_contents = requests.post(
    url, 
    headers={'Authorization': my_token}, 
    files={'file': open('./sample.sh', 'rb')}
) 
print(res_contents)
```

`<Response [201]>` が 返ってきていれば上手くアップロードされています。

### 返り値の確認 (JSON)

次に返り値を確認します。"201" が返ってきていれば、<変数>.text に返り値も格納されています。


```python:python
import json
res_contents_dict = json.loads(res_contents.text)
json = json.dumps(res_contents_dict, indent=2, separators=(',', ': '))
print(json)
```

結果例を示します。
下記のような結果が返ってきていることを確認してください。

```json:response
{
  "encodedEncryptionKey": "mFau50Xqf0hiakfjYDP+Vw9BheVwyf57E9AJYQwmjig=",
  "dateUploaded": "2017-09-10T09:00:30.144586Z",
  "viewInBrowser": true,
  "decryptedSize": 54,
  "downloadUrl": "https://platform.rescale.jp/api/v3/files/BcTKRc/contents/",
  "relativePath": "sample.sh",
  "pathParts": {
    "container": "jpprod-rescale-platform",
    "path": "user/user_QbQWc/sample-36bb7981-22fc-4b81-935e-0b95c3c670ca.sh"
  },
  "isUploaded": true,
  "storage": {
    "connectionSettings": {
      "region": "ap-northeast-1"
    },
    "encryptionType": "default",
    "id": "pCTMk",
    "storageType": "S3Storage"
  },
  "id": "BcTKRc",
  "md5": "d6a0dc9d44d56fd476d62e257eeef2f1",
  "owner": "daisuke@rescale.com",
  "path": "user/user_QbQWc/sample-36bb7981-22fc-4b81-935e-0b95c3c670ca.sh",
  "name": "sample.sh",
  "typeId": 1,
  "isDeleted": false
}
```

### ファイルIDの取得

上記のJSONから "id" を抜き出します。


```python:python
file_id = res_contents_dict["id"]
print(file_id)
```

"BcTKRc" など ランダムな文字列が返ってきていれば良いです。


-----------------------

## [STEP2] ジョブの作成

もし python を起動していなければ、シェルからpythonを起動してください

```bash:command
python
```

### ジョブを定義するJSONの作成

ブラウザを操作してジョブ投入条件を設定するかわりにAPIでは、JSONファイルにその条件を記述します。


```python:python
my_job_json = '''
{
  "name": "hello_world!!",
  "jobanalyses": [
    {
      "useRescaleLicense": false,
      "command": "./sample.sh",
      "analysis": {
        "code": "user_included_platformmpi",
        "version": "platformmpi-9.1.4"
      },
      "hardware": {
        "coresPerSlot": 2,
        "slots": 1,
        "coreType": {
          "code": "hpc-3"
        }
      }
    }
  ],
  "isLowPriority": "true"
}
'''
```

### FileIdの挿入

上記ではファイルIDの情報が不足しているため、先ほどアップロードしたファイルIDをここで挿入してやります。


```python:python
import json

my_job_dict = json.loads(my_job_json)
my_job_dict['jobanalyses'][0]['inputFiles'] = [0]
my_job_dict['jobanalyses'][0]['inputFiles'][0] = {'id': file_id}

json = json.dumps(my_job_dict, indent=2, separators=(',', ': '))
print(json)
```

下記のようなJSONファイルができあがっていればOKです。

```json:response
{
  "name": "hello_world!!",
  "jobanalyses": [
    {
      "useRescaleLicense": false,
      "inputFiles": [
        {
          "id": "BcTKRc"
        }
      ],
      "analysis": {
        "code": "user_included_platformmpi",
        "version": "platformmpi-9.1.4"
      },
      "command": "./sample.sh",
      "hardware": {
        "coreType": {
          "code": "hpc-3"
        },
        "coresPerSlot": 2,
        "slots": 1
      }
    }
  ],
  "isLowPriority": "true"
}
```

### ジョブの作成

このAPIはジョブ条件を保存するだけであって、ジョブは実行されません。つまり課金はされません。
(ストレージの容量、データ転送等は除く)


```python:python
import requests

my_token = 'Token' + ' ' + apikey
url = platform + "api/v3/jobs/"

print("Token: ", my_token)
print("url: ", url)
print("expected value: <class 'dict'>): ", type(my_job_dict))

res_jobs = requests.post(
    url,
    headers = {'Authorization':  my_token},
    json = my_job_dict
)

print(res_jobs)
```

### 返り値の確認 (JSON)


```python:python
import json

res_jobs_dict = json.loads(res_jobs.text)
myjson = json.dumps(res_jobs_dict , indent=2, separators=(',', ': '))
print(myjson)
```

下記のようなJSONが返ってきていればOKです。


```json:response
{
  "isTemplateDryRun": false,
  "cidrRule": "0.0.0.0/0",
  "jobvariables": [],
  "paramFile": null,
  "projectId": null,
  "monteCarloIterations": null,
  "caseFile": null,
  "archiveFilters": [],
  "remoteVizConfig": null,
  "isLowPriority": true,
  "sshPort": 22,
  "id": "wAXvm",
  "cluster": null,
  "expectedRuns": null,
  "owner": "XXXXXX@XXXX.com",
  "name": "hello_world!!",
  "includeNominalRun": false,
  "resourceFilters": [],
  "jobanalyses": [
    {
      "useRescaleLicense": false,
      "flags": {},
      "preProcessScript": null,
      "onDemandLicenseSeller": null,
      "inputFiles": [
        {
          "decompress": true,
          "dateUploaded": "2017-09-10T09:00:30.144586Z",
          "viewInBrowser": true,
          "decryptedSize": 54,
          "downloadUrl": "https://platform.rescale.jp/api/v3/files/BcTKRc/contents/",
          "encodedEncryptionKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
          "relativePath": "sample.sh",
          "isUploaded": true,
          "storage": {
            "connectionSettings": {
              "region": "ap-northeast-1"
            },
            "encryptionType": "default",
            "id": "pCTMk",
            "storageType": "S3Storage"
          },
          "id": "BcTKRc",
          "md5": "d6a0dc9d44d56fd476d62e257eeef2f1",
          "owner": "daisuke@rescale.com",
          "path": "user/user_QbQWc/sample-36bb7981-22fc-4b81-935e-0b95c3c670ca.sh",
          "name": "sample.sh",
          "typeId": 1,
          "pathParts": {
            "container": "jpprod-rescale-platform",
            "path": "user/user_QbQWc/sample-36bb7981-22fc-4b81-935e-0b95c3c670ca.sh"
          },
          "isDeleted": false
        }
      ],
      "command": "./sample.sh",
      "envVars": {},
      "preProcessScriptCommand": "",
      "analysis": {
        "name": "Bring Your Own MPI Software",
        "versionName": "Platform MPI 9.1.4",
        "code": "user_included_mpi",
        "version": "platformmpi-9.1.4"
      },
      "templateTasks": [],
      "hardware": {
        "coreType": {
          "compute": "7.33",
          "displayOrder": 1,
          "features": [
            "ssh",
            "cluster_status"
          ],
          "storageIo": "2 GB/s read, 1GB/s write",
          "defaultWalltime": null,
          "storage": 36,
          "mustBeRequested": false,
          "color": "#ff4242",
          "name": "Onyx",
          "remoteVizAllowed": true,
          "isDefault": true,
          "code": "hpc-3",
          "acronym": "O",
          "baseClockSpeed": "2.9",
          "io": "10 Gb/s",
          "processorInfo": "Intel Xeon E5-2666 v3 (Haswell)",
          "hasSsd": true,
          "description": "HPC++",
          "cores": [
            1,
            2,
            4,
            8,
            18
          ],
          "walltimeRequired": false,
          "memory": 3300,
          "gpuCounts": [
            0,
            0,
            0,
            0,
            0
          ],
          "isPrimary": true
        },
        "coresPerSlot": 2,
        "isReusable": false,
        "walltime": 750,
        "coreSummary": {
          "gpusPerNode": 0,
          "storagePerNode": 80000,
          "numberOfNodes": 1.0,
          "memoryPerNode": 7500
        },
        "type": "compute",
        "slots": 1
      },
      "postProcessScript": null,
      "postProcessScriptCommand": ""
    }
  ],
  "publicKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
```

### JOB-ID の取得

上記のJSONから "id" を抜き出します。


```python:python
job_id = res_jobs_dict["id"]
print(job_id)
```

"wAXvm" のようなランダムな文字列が返ってきていればOKです。

### Browserで内容をもう一度確認

下記コマンドを実行して、表示されたURLをブラウザに貼り付けてください。


```python:python
url = platform + "jobs/" + job_id
print(url)
```

---------------------------

## [STEP3] ジョブの実行


もし python を起動していなければ、シェルからpythonを起動してください

```bash:command
python
```

### Submit your JOB

このAPIにより、始めてジョブが実行されます。つまり課金が始まります


```python:python
import requests

my_token = 'Token' + ' ' + apikey
url = platform + "api/v3/jobs/" + job_id + "/submit/"

print("Token: ", my_token)
print("url: ", url)

res_submit = requests.post(
    url,
    headers = {'Authorization': my_token}
)

print(res_submit)
```

"Response [200]" が返ってきていればOKです。JSONによる返り値はありません

---------------------------

## [STEP4] ジョブのモニター

もし python を起動していなければ、シェルからpythonを起動してください

```bash:command
python
```

### ジョブの状態確認


```python:python
import requests

my_token = 'Token' + ' ' + apikey
url = platform + "api/v3/jobs/" + job_id + "/statuses/"

print("Token: ", my_token)
print("url: ", url)

res_monitor = requests.get(
    url,
    headers = {'Authorization': my_token}
)

print(res_monitor)
```

### 返り値の確認 (JSON)


```python:python
import json

res_monitor_dict = json.loads(res_monitor.text)
myjson = json.dumps(res_monitor_dict , indent=2, separators=(',', ': '))
print(myjson)
```

下記のように、"statusReason" に "Completed successfully"　があれば、ジョブが完了しています。

```json:response
{
  "results": [
    {
      "statusDate": "2017-09-10T09:22:26.036547Z",
      "statusReason": "Completed successfully",
      "id": "YZpnm",
      "jobId": "wAXvm",
      "status": "Completed"
    },
    {
      "statusDate": "2017-09-10T09:17:16.003000Z",
      "statusReason": null,
      "id": "KxwAa",
      "jobId": "wAXvm",
      "status": "Executing"
    },
    {
      "statusDate": "2017-09-10T09:13:51.274000Z",
      "statusReason": null,
      "id": "ikqnm",
      "jobId": "wAXvm",
      "status": "Validated"
    },
    {
      "statusDate": "2017-09-10T09:13:50.876000Z",
      "statusReason": null,
      "id": "UHwAa",
      "jobId": "wAXvm",
      "status": "Started"
    },
    {
      "statusDate": "2017-09-10T09:13:47.069093Z",
      "statusReason": null,
      "id": "WZpnm",
      "jobId": "wAXvm",
      "status": "Queued"
    },
    {
      "statusDate": "2017-09-10T09:13:40.149523Z",
      "statusReason": null,
      "id": "HxwAa",
      "jobId": "wAXvm",
      "status": "Pending"
    }
  ],
  "count": 6,
  "previous": null,
  "next": null
}
```