Linode Forum
Linode Community Forums
 FAQFAQ    SearchSearch    MembersMembers      Register Register 
 LoginLogin [ Anonymous ] 
Post new topic  Reply to topic
Author Message
PostPosted: Sat Aug 31, 2013 10:21 am 
Offline
Junior Member

Joined: Tue Jun 21, 2011 12:55 am
Posts: 33
Website: http://www.vrnw.org
Hi,
I was recently looking into either modifying the already existing php class for the Linode API, or writing my own and was going to use the api.spec() call to load the parameters into an array. I have since done this, but noticed the call to api.spec() returns parameters in an order not consistent with the documentation. To me, it would make sense to either re-order the parameters, or add a parameter index to the returned parameters for the various methods, so that when the methods are dynamically generated with the php script, their calls will match the order given for the parameters in the documentation.

Here's an example of what I'm talking about with the method linode.config.create().

This is what the documentation states for its parameters, and presumably, their proper order.

Code:
LinodeID - numeric (required) 
KernelID - numeric (required)
The KernelID for this profile. Found in avail.kernels()
Label - string (required)
The Label for this profile
Comments - string, default: '' (optional)
Comments you wish to save along with this profile
RAMLimit - numeric, default: '0' (optional)
RAMLimit in MB. 0 for max.
DiskList - string, default: ',,,,,,,,' (optional)
A comma deliminted list of DiskIDs; position reflects device node. The 9th element for specifying the initrd.
RunLevel - string, default: 'default' (optional)
One of 'default', 'single', 'binbash'
RootDeviceNum - numeric, default: '1' (optional)
Which device number (1-8) that contains the root partition. 0 to utilize RootDeviceCustom.
RootDeviceCustom - string, default: '' (optional)
A custom root device setting.
RootDeviceRO - boolean, default: 'true' (optional)
Enables the 'ro' kernel flag. Modern distros want this.
helper_disableUpdateDB - boolean, default: 'true' (optional)
Enable the disableUpdateDB filesystem helper
helper_xen - boolean, default: 'true' (optional)
Enable the Xen filesystem helper. Corrects fstab and inittab/upstart entries depending on the kernel you're booting. You want this.
helper_depmod - boolean, default: 'true' (optional)
Creates an empty modprobe file for the kernel you're booting.
devtmpfs_automount - boolean, default: 'true' (optional)
Controls if pv_ops kernels should automount devtmpfs at boot.


Here are the parameters from the raw JSON output of https://api.linode.com/?api_action=api.spec

Code:
"linode.config.create":{"DESCRIPTION":"Creates a Linode Configuration Profile.","PARAMETERS":{"RootDeviceCustom":{"DESCRIPTION":"A custom root device setting.","default":"","TYPE":"string","REQUIRED":false},"Comments":{"DESCRIPTION":"Comments you wish to save along with this profile","default":"","TYPE":"string","REQUIRED":false},"devtmpfs_automount":{"DESCRIPTION":"Controls if pv_ops kernels should automount devtmpfs at boot. ","default":true,"TYPE":"boolean","REQUIRED":false},"helper_disableUpdateDB":{"DESCRIPTION":"Enable the disableUpdateDB filesystem helper","default":true,"TYPE":"boolean","REQUIRED":false},"Label":{"DESCRIPTION":"The Label for this profile","TYPE":"string","REQUIRED":true},"ConfigID":{"DESCRIPTION":"","TYPE":"numeric","REQUIRED":true},"DiskList":{"DESCRIPTION":"A comma deliminted list of DiskIDs; position reflects device node.  The 9th element for specifying the initrd.","default":",,,,,,,,","TYPE":"string","REQUIRED":false},"RunLevel":{"DESCRIPTION":"One of 'default', 'single', 'binbash' ","default":"default","TYPE":"string","REQUIRED":false},"RootDeviceRO":{"DESCRIPTION":"Enables the 'ro' kernel flag. Modern distros want this. ","default":true,"TYPE":"boolean","REQUIRED":false},"RootDeviceNum":{"DESCRIPTION":"Which device number (1-8) that contains the root partition.  0 to utilize RootDeviceCustom.","default":1,"TYPE":"numeric","REQUIRED":false},"helper_xen":{"DESCRIPTION":"Enable the Xen filesystem helper.  Corrects fstab and inittab\/upstart entries depending on the kernel you're booting.  You want this.","default":true,"TYPE":"boolean","REQUIRED":false},"RAMLimit":{"DESCRIPTION":"RAMLimit in MB.  0 for max.","default":0,"TYPE":"numeric","REQUIRED":false},"LinodeID":{"DESCRIPTION":"","TYPE":"numeric","REQUIRED":true},"helper_depmod":{"DESCRIPTION":"Creates an empty modprobe file for the kernel you're booting. ","default":true,"TYPE":"boolean","REQUIRED":false},"KernelID":{"DESCRIPTION":"The KernelID for this profile.  Found in avail.kernels()","TYPE":"numeric","REQUIRED":true}},"THROWS":"NOTFOUND,VALIDATION"}}


Other methods are similar to this in their parameter order. I could, of course, make the script re-order the parameters for each method, or write a JSON or XML file with the methods and parameters, but that would be defeating the point of the api.spec() call in my opinion.

Also, though this isn't nearly as big a problem, on a couple of the methods the REQUIRE element for a parameter will say no or yes, rather than true or false. The methods are avail.linodeplans() and linode.disk.createfromstackscript().

Blake


Top
   
PostPosted: Sat Aug 31, 2013 2:27 pm 
Offline
Senior Member
User avatar

Joined: Sat Aug 30, 2008 1:55 pm
Posts: 1739
Location: Rochester, New York
You should be loading it all into an associative array and then indexing by the key, not the position of the value. There isn't a "proper" ordering for the keys, per se, and the order in which things are returned from the api.spec call (or any call) is not guaranteed.

If some sort of sane ordering is desired, sorting alphabetically seems to be a reasonable way to go: https://api.linode.com/?api_action=api.spec&api_responseFormat=human

I dare say that the REQUIRED element inconsistency is a big problem, though :-)

_________________
Code:
/* TODO: need to add signature to posts */


Top
   
PostPosted: Sat Aug 31, 2013 3:00 pm 
Offline
Junior Member

Joined: Tue Jun 21, 2011 12:55 am
Posts: 33
Website: http://www.vrnw.org
Hi,
The php function json_decode() will create an associative array from json data.
http://www.php.net/manual/en/function.json-decode.php

At the very least, required parameters should come first, I can sort the array in that manor if needed, that's no problem. Checking to see if the REQUIRED element of a parameter array is set to true or "yes", then moving that parameter before non-required ones in the array should solve that problem easily. My lack of understanding comes from the fact that the api.spec call itself doesn't order the parameters in the order given by the documentation, which could lead to confusion after a class has been written to dynamically create methods based on the returned data from api.spec. Since the order of the parameters is different, the order of the parameters in the call to the dynamically created function will be different, regardless of weather or not an associative array is being used. I could be wrong, but the parameter itself, LinodeID, or whatever, would need to be used to create the URL to perform the API call. The only way to create it correctly and match it up with the dynamically created method would be proceeding through a loop in the parameters array for the method called.

Take user.getapikey for example. In the returned parameters given in the api.spec call, password will come before the username. To sort the array in such a way that each function would make sense according to the given documentation about the Linode API, you'd have to set something up to manually sort the parameters so they'd match up. After all, I'd think it's sensible to use the provided documentation as a guide when calling your functions in a program, even if the actual parameter orders wouldn't actually matter when making a call to the API. If the API's documentation changed at some point, you'd want to change the sorting of the array rather than relying on api.spec to dynamically create the array with its correct sorting in the first place, which I think was one of its goals, as manually sorting parameters for correct use of dynamic methods would be just as bad as using a created file for generation of the methods and parameters in the Linode API specification, in my opinion.

If as you say, the return order from any API call isn't guaranteed, that would mean the order of the parameters given for each method to api.spec could be ordered different each time, as could the methods themselves. From my experience, this hasn't been the case, though I could be wrong, of course.

Blake


Top
   
PostPosted: Sat Aug 31, 2013 6:21 pm 
Offline
Senior Member
User avatar

Joined: Sat Aug 30, 2008 1:55 pm
Posts: 1739
Location: Rochester, New York
I don't know how PHP does things, but at least in the Python world, we can use keyword arguments:

Code:
def example_function(foo, bar, baz, quux=None, quuux=1):
  ...


So that I can then do:

Code:
example_function(foo='a', bar='b', baz='c', quux=2, quuux=3)
example_function(baz='c', quux=2, foo='a', bar='b', quuux=3)


and it'll Do The Right Thing, regardless of the order in which I specify the arguments. In essence, it turns into a dictionary (an associative array). This would be the safest way to do it, if PHP has similar notation. The user of the API bindings can feed in the arguments in whatever order they want, and it will work just fine.

I'd hazard a guess that it can't be easily changed on Linode's side, either. I believe api.spec is dynamically-generated, so it's going into the JSON generator as nested associative arrays, which are unordered in many languages. At best, they'd have to subclass the JSON generator for this specific API call to make it have this behavior.

_________________
Code:
/* TODO: need to add signature to posts */


Top
   
PostPosted: Sat Aug 31, 2013 6:34 pm 
Offline
Junior Member

Joined: Tue Jun 21, 2011 12:55 am
Posts: 33
Website: http://www.vrnw.org
Hi,
I don't believe php has similar notation, but even if it did, that would still require making use of something outside of the generated api.spec code, if I understood what you posted correctly.

I do believe something like an INDEX element for the parameters could probably be easily added, the first could be 1, the second, 2, and so on. The INDEX element could comply with the order of the parameters stated in the documentation. Perhaps I'm wrong, though.

I see what you were talking about when you stated the REQUIRED element not remaining a Boolean can be a problem. I'm trying to compare the value with 1, true, and "yes" in php to evaluate it to true, just in case all those values are there, and I can't seem to determine precisely what I'm doing wrong with my expression. It's rather frustrating. Oh well, I'll figure it out eventually.

Blake


Top
   
PostPosted: Sun Sep 01, 2013 9:00 am 
Offline
Senior Member
User avatar

Joined: Sat Aug 30, 2008 1:55 pm
Posts: 1739
Location: Rochester, New York
In the Python end of things, it's actually somewhat easier:

Code:
def example_function(**kwargs):
    # kwargs is now a dictionary of all the keyword arguments
    ...

example_function(foo=1, bar=2, baz=3)


At that point, the only problem is case sensitivity.

You might be able to do something similar by setting up an associative array before calling your function, and then passing that in. It's a rather C-like thing to do.

My worry with an 'index' element is that the order in the documentation can change: if something is added in the middle, then everything below it will be renumbered, and everything that uses your API bindings will break. (Right now, of course, the documentation lists parameters in the order they were added to the methods, but someone could clean that up some day...)

_________________
Code:
/* TODO: need to add signature to posts */


Top
   
PostPosted: Sun Sep 01, 2013 9:12 am 
Offline
Junior Member

Joined: Tue Jun 21, 2011 12:55 am
Posts: 33
Website: http://www.vrnw.org
Hi,
Your python method certainly does seem easier, for sure. I wish there was something like that in php, but I don't think there is. The only way I'd know of to do that is to call the dynamic function with two arguments for one, so you'd put in both the parameter and its argument, rather than just calling in the arguments. For example:

Code:
<?php
function sample_function($parameter, $arg)
{
return "My parameter is $parameter and my argument is $arg.\r\n";
}
echo sample_function("parameter", 4);
?>


You're probably right about the INDEX element breaking things, I didn't consider that.

Blake


Top
   
PostPosted: Sun Sep 01, 2013 9:26 am 
Offline
Senior Member
User avatar

Joined: Sat Aug 30, 2008 1:55 pm
Posts: 1739
Location: Rochester, New York
Wow. I think you're right: PHP doesn't have them. A bummer.

Are classes available? You might be able to do something like this:

Code:
my_doohickey = api.new('linode.config.create')
my_doohickey.set('KernelID', 123)
my_doohickey.set('Label', 'Atlantic Records')
...
my_doohickey.commit()


It's ugly, but it would probably work. (If you can have dynamic attributes/methods, that would make things nicer-looking.)

_________________
Code:
/* TODO: need to add signature to posts */


Top
   
PostPosted: Sun Sep 01, 2013 12:00 pm 
Offline
Junior Member

Joined: Tue Jun 21, 2011 12:55 am
Posts: 33
Website: http://www.vrnw.org
Hi,
I could do that, but I don't think I could create dynamic classes, only dynamic methods to call from within a class. So, that won't work, either. Good idea, though.

Blake


Top
   
Display posts from previous:  Sort by  
Post new topic  Reply to topic


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  
RSS

Powered by phpBB® Forum Software © phpBB Group