RESTful result code

MisterE2002

Patron
Joined
Sep 5, 2015
Messages
211
So i am using "http://<host>/api/docs/" to create a Python script to start a jail. The code works, but i like to know if it failed.
The correct name is 'rsync_backups' and i use 'rsync_backupss'

Code:
def start_jail(host, api_key):
  global failure
  api_call = 'http://{}/api/v2.0/jail/start'.format(host)
  headers = {"Content-Type":"application/json", "Authorization": "Bearer " + api_key}
  json_string = '"' + JAIL_NAME + '"'
 
  try:
    resp = requests.post(api_call, json=json.loads(json_string), headers=headers)
    
    if resp.status_code != 200:
      logger.error('Action "{}" on host "{}" failed with error: {}'.format('start', host, resp.status_code))
      failure = True
    else:
      logger.info('Action "{}" on host "{}" successful'.format('start', host))
  except Exception as e:
    logger.error('Action "{}" on host "{}" failed with error: {}'.format('start', host, e))
    failure = True


The API documentation only mentions 200 and 401.
But the middlewared.log is throwing. How do i catch those errors?

middlewared.service_exception.CallError: [ENOENT] 'rsync_backupss' jail does not exist
 

ClassicGOD

Contributor
Joined
Jul 28, 2011
Messages
145
I don't have Core install anymore to test but:
401 and 200 are protocol status codes. You'll get 401 if you are unauthorized, probably will get 404 if the endpoint does not exist but if you are authorized and endpoint is correct you will probably get a response with status code 200 and a body containing the error if an error occurred.
 

MisterE2002

Patron
Joined
Sep 5, 2015
Messages
211
Is the body not the 5xxxxx number i getting? Which seems some kind of sequential number. Only at the last curl i passed the correct jail name.


[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
53904
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
53907
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
53908
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
53909
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
53910
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
53911
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"\""
53912
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
53913
[root@rsync-backups /]# echo $?
0
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backups\""
53916
[root@rsync-backups /]# Killed
 
Last edited:

ClassicGOD

Contributor
Joined
Jul 28, 2011
Messages
145
That should be the body but it's strange that it only returns a number. I don't have Core system anymore but I have not notice this behavior from Scale API.

Try adding -i to curl it should return also response status code and headers. For example if you try start non-existing service Scale API will respond with status 404 returning the requested service name as a message in the body. While correct call will result in status 200 with service status in the body (true for running, false for stopped).
 

MisterE2002

Patron
Joined
Sep 5, 2015
Messages
211
Are you really sure this works in Scale? They share a lot of code and seems weird that they differentiate at this point.
API's are quite useless if you can not verify if they do their job correctly. And i will wait some time before migrating to Scale.

[root@rsync-backups /]# curl -i -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
HTTP/1.1 200 OK
Server: nginx
Date: Mon, 03 Jan 2022 22:06:24 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 5
Connection: keep-alive
Strict-Transport-Security: max-age=0; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Permissions-Policy: geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()
Referrer-Policy: strict-origin
X-Frame-Options: SAMEORIGIN

54181



[root@rsync-backups /]# curl -i -X POST 'http://nas/api/v2.0/jail/does_not_exist' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backups\""
HTTP/1.1 404 Not Found
Server: nginx
Date: Mon, 03 Jan 2022 22:07:22 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 14
Connection: keep-alive
Strict-Transport-Security: max-age=0; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Permissions-Policy: geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()
Referrer-Policy: strict-origin
X-Frame-Options: SAMEORIGIN

404: Not Found
 

ClassicGOD

Contributor
Joined
Jul 28, 2011
Messages
145
Are you really sure this works in Scale?
Definitely works on Scale but I'm not testing the same endpoint as there are no jails on Scale.

You are getting 200 OK with the wrong jail name but getting 404 with the correct one or there is a copy/paste error in your post? If you do that definitely seems wrong.

[edit] scratch that - i didn't notice different URL.

ps. I recommend Postman for testing REST Apis.
 

MisterE2002

Patron
Joined
Sep 5, 2015
Messages
211
Are you getting the same results for these calls?
Seems to work a bit better but only a "result" boolean would be easier. But not the best example to be fair.


test@linux-5q3o:~/bin> curl -X POST 'http://nas/api/v2.0/service/start' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d '{"service": "nfs","service-control": {"ha_propagate": false}}'
true

test@linux-5q3o:~/bin> curl -X POST 'http://nas/api/v2.0/service/stop' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d '{"service": "nfs","service-control": {"ha_propagate": false}}'
false

test@linux-5q3o:~/bin> curl -X POST 'http://nas/api/v2.0/service/start' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d '{"service": "does_not_exist","service-control": {"ha_propagate": false}}'
{
"message": "'does_not_exist'",
"traceback": "Traceback (most recent call last):\n File \"/usr/local/lib/python3.9/site-packages/middlewared/restful.py\", line 575, in do\n result = await self.middleware.call(methodname, *method_args, **method_kwargs)\n File \"/usr/local/lib/python3.9/site-packages/middlewared/main.py\", line 1256, in call\n return await self._call(\n File \"/usr/local/lib/python3.9/site-packages/middlewared/main.py\", line 1213, in _call\n return await methodobj(*prepared_call.args)\n File \"/usr/local/lib/python3.9/site-packages/middlewared/schema.py\", line 975, in nf\n return await f(*args, **kwargs)\n File \"/usr/local/lib/python3.9/site-packages/middlewared/plugins/service.py\", line 113, in start\n service_object = await self.middleware.call('service.object', service)\n File \"/usr/local/lib/python3.9/site-packages/middlewared/main.py\", line 1256, in call\n return await self._call(\n File \"/usr/local/lib/python3.9/site-packages/middlewared/main.py\", line 1213, in _call\n return await methodobj(*prepared_call.args)\n File \"/usr/local/lib/python3.9/site-packages/middlewared/plugins/service.py\", line 254, in object\n return self.SERVICES[name]\nKeyError: 'does_not_exist'\n"
}
 

HarryMuscle

Contributor
Joined
Nov 15, 2021
Messages
161
Is the body not the 5xxxxx number i getting? Which seems some kind of sequential number. Only at the last curl i passed the correct jail name.


[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
53904
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
53907
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
53908
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
53909
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
53910
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
53911
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"\""
53912
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
53913
[root@rsync-backups /]# echo $?
0
[root@rsync-backups /]# curl -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backups\""
53916
[root@rsync-backups /]# Killed
The number is most likely the job ID that's being returned. Some tasks are done right away when an API call is made while others are done via a job where the API call only starts the process. If you want to know the result you probably have to look up if the job was successful using this job ID. Not sure which API checks that though. Make sure to give some time for the job to complete before checking it's status.

Thanks,
Harry
 

MisterE2002

Patron
Joined
Sep 5, 2015
Messages
211
Thanks ClassicGOD and HarryMuscle for their help.

Indeed this seems a "job" number.
Browsing <ip_nas>/api/docs the only call seems to get the state of *all* jobs. This means that we have to implement a lot of logic.
* is the request ending with a (valid) number
* request jobs list
* find the job in the results
* check the "state"
* and maybe also add some recursion (with delay) if the job is still running

For now i think i just assume the calls succeed.


test@linux-5q3o:~>curl -i -X POST 'http://nas/api/v2.0/jail/restart' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token" -d "\"rsync_backupss\""
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 04 Jan 2022 09:34:20 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 5
Connection: keep-alive
Strict-Transport-Security: max-age=0; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Permissions-Policy: geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()
Referrer-Policy: strict-origin
X-Frame-Options: SAMEORIGIN

55597



test@linux-5q3o:~> curl -i -X GET 'http://nas/api/v2.0/core/get_jobs' -H 'accept: */*' -H 'Content-Type: application/json' -H "Authorization: Bearer $token"

{
"id": 55597,
"method": "jail.restart",
"arguments": [
"rsync_backupss"
],
"logs_path": null,
"logs_excerpt": null,
"progress": {
"percent": null,
"description": null,
"extra": null
},
"result": null,
"error": "[ENOENT] 'rsync_backupss' jail does not exist",
"exception": "Traceback (most recent call last):\n File \"/usr/local/lib/python3.9/site-packages/middlewared/job.py\", line 367, in run\n await self.future\n File \"/usr/local/lib/python3.9/site-packages/middlewared/job.py\", line 405, in __run_body\n rv = await self.middleware.run_in_thread(self.method, *([self] + args))\n File \"/usr/local/lib/python3.9/site-packages/middlewared/utils/run_in_thread.py\", line 10, in run_in_thread\n return await self.loop.run_in_executor(self.run_in_thread_executor, functools.partial(method, *args, **kwargs))\n File \"/usr/local/lib/python3.9/concurrent/futures/thread.py\", line 52, in run\n result = self.fn(*self.args, **self.kwargs)\n File \"/usr/local/lib/python3.9/site-packages/middlewared/schema.py\", line 979, in nf\n return f(*args, **kwargs)\n File \"/usr/local/lib/python3.9/site-packages/middlewared/plugins/jail_freebsd.py\", line 1328, in restart\n uuid, _, iocage = self.check_jail_existence(jail)\n File \"/usr/local/lib/python3.9/site-packages/middlewared/plugins/jail_freebsd.py\", line 1060, in check_jail_existence\n raise CallError(f'{jail!r} jail does not exist', errno=errno.ENOENT)\nmiddlewared.service_exception.CallError: [ENOENT] 'rsync_backupss' jail does not exist\n",
"exc_info": {
"type": "CallError",
"extra": null
},
"state": "FAILED",
"time_started": {
"$date": 1641288860913
},
"time_finished": {
"$date": 1641288861060
}
}
 

ClassicGOD

Contributor
Joined
Jul 28, 2011
Messages
145
I was just writing my reply when you wrote yours, lol.

Looks like you are correct in your assumptions. Your responses for service/start and stop endpoint look very similar to ones from Scale so it looks like the jail endpoint is more complicated. Probably because Jails can take quite a long time to start and the request could time out if it was waiting for it. But some status like "starting" would be nice.
 
Top