I am having some issues with my script.
It seems when i do an API call to update a switchport, the voicevlan parameter is not allowed to be NULL.
https://dashboard.meraki.com/api_docs#update-a-switch-port
As i am using standarized functions it is possible i call the function with or without a voicevlan function.
So does the voicevlan parameter in that api call has to have a value ? Or can i use 0 or should i change the request body whenever a parameter is not used ?
Could you share a snippet of the script you're using, sanitized to exclude private information?
@MeredithW Would this thread be better off in the API sub-forum?
I am using powershell:
function Update-MerakiSwitchPort { param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $endpointurl, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $apikey, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $port, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $serial, [Parameter(Mandatory = $false)] [String] $name, [Parameter(Mandatory = $false)] [String] $tags, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $enabled, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $type, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [String] $vlan, [Parameter(Mandatory = $false)] [String] $voiceVlan, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [String] $allowedVlans, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $poeEnabled, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [String] $isolationEnabled, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $rstpEnabled, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $stpGuard, #[Parameter(Mandatory = $false)] [String] $accessPolicyNumber, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $linkNegotiation, #[Parameter(Mandatory = $false)] [String] $portScheduleId, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $udld, #[Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [String] $macWhitelist, #[Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [String] $stickyMacWhitelist, [Parameter(Mandatory = $false)] [String] $stickyMacWhitelistLimit ) if ($port) { if ($serial) { $api = @{ "method" = "PUT" "endpoint" = $endpointurl "url" = '/devices/' + $serial + '/switchPorts/' + $port } $header = @{ "X-Cisco-Meraki-API-Key" = $apikey "Content-Type" = 'application/json' } $body = @{ "name" = $name "tags" = $tags "enabled" = $enabled "type" = $type "vlan" = $vlan "voiceVlan" = $voiceVlan "allowedVlans" = $allowedVlans "poeEnabled" = $poeEnabled "isolationEnabled" = $isolationEnabled "rstpEnabled" = $rstpEnabled "stpGuard" = $stpGuard #"accessPolicyNumber" = $accessPolicyNumber "linkNegotiation" = $linkNegotiation #"portScheduleId" = $portScheduleId "udld" = $udld #"macWhitelist" = $macWhitelist #"stickyMacWhitelist" = $stickyMacWhitelist "stickyMacWhitelistLimit" = $stickyMacWhitelistLimit } | ConvertTo-Json $uri = $api.endpoint + $api.url try { $request = Invoke-RestMethod -Method $api.method -Uri $uri -Headers $header -Body $body } catch { $_.Exception Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__ -foreground red Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription -foreground red check_faultcodes -codevalue $_.Exception.Response.StatusCode.value__ Pause } return $request } else { Write-Host "Serial not entered." -ForegroundColor Red } } else { Write-Host "Port not entered." -ForegroundColor Red } }
I believe this is only required on access ports. Can you try removing it and run the script on a trunk port?
It runs alright for most of the ports which contain data and voice vlan, on access ports that have only a data vlan it breaks. I am pretty sure it used to work some months ago in the same setup.
I want to have a generic function for trunk, access and access+voice ports. So it would be nice to pass a NULL value to a voice vlan whenever it is not used.
Somehow that is not allowed anymore ?
Have you tried passing an empty string for the voice vlan instead of NULL when you want there to be no vlan specified? It could be that it is rejecting a NULL value when you have an access port.
I have done a bit of experimenting (in Python) and so far, I have been unable to clear a voice vlan assignment from a switch port. I have tried sending None/NULL and sending no data for that tag, which results in no change to the existing voice vlan setting. Trying to send an empty string results in an error. Sending 0 gives an error. At this point, it looks like there is no way to remove a voice vlan assignment via the API.
I will keep trying this and report back if I find a way to do it.
I have done some more testing and this is what I have found.
If you modify an existing trunk port (that has never been an access) and change it to an access port and specify None/NULL as the voice vlan, it will update and there will be no voice vlan value.
If you take an access port with a voice vlan value and change it to a trunk, the voice vlan goes away, since it is not used.
If you take that same trunk port and change it back to an access and specify the voice vlan as None/NULL, the voice vlan value will be whatever it was before you made it a trunk. It remembers that past value and uses it.
At this point I have not found a way to erase that voice vlan value with the API. You can change it, but not remove it. The only way I can do it is in the dashboard.
The upshot is that the only way I can see that you can have an access port set by the API with no voice vlan value is if you are modifying a trunk that has never been set to an access with a voice vlan value or if you modify an access port that does not have a voice vlan value set.
It is just my opinion, but I see this as a bug in the endpoint. Meraki should change the endpoint so that there is a method to clear a voice vlan value for access ports. If there is some value that will accomplish this with the current endpoint, they need to document it.
Hey @sebas ,
I've been taking a quick look at this this morning can you do me a favour and try and #"stickyMacWhitelistLimit" to remove this parameter as well and try and run.
I have tested the following and it is working to remove the value in voice Vlan
$jsonbodyUpPort = '
{
"name":"test voice Vlan Update",
"tags":null,
"enabled":true,
"poeEnabled":true,
"type":"access",
"vlan":10,
"voiceVlan":null,
"allowedVlans": "all",
"isolationEnabled": false,
"rstpEnabled": true,
"stpGuard": "BPDU guard",
"accessPolicyNumber": null,
"linkNegotiation": "Auto negotiate",
"portScheduleId": null,
"udld": "Alert only"
}
'
I have run my tests using only the fields you specified and I still cannot clear the voice vlan if one currently exists or if one previously existed when a trunk port was previously an access port. Anybody else have better luck?
Hi @CBurkhead
what does your changelog say against your change. Here is my call which is removing a the voice valn when it's populated then changed to a Trunk port.
Old Value
{"number":5,"name":"test voice Vlan Update","tags":null,"enabled":true,"poeEnabled":true,"type":"trunk","vlan":1,"voiceVlan":20,"allowedVlans":"all","isolationEnabled":false,"rstpEnabled":true,"stpGuard":"bpdu guard","accessPolicyNumber":null,"linkNegotiation":"Auto negotiate","portScheduleId":null,"udld":"Alert only","macWhitelist":null,"stickyMacWhitelist":null,"stickyMacWhitelistLimit":null}
New Value
{"number":5,"name":"test voice Vlan Update","tags":null,"enabled":true,"poeEnabled":true,"type":"trunk","vlan":10,"voiceVlan":null,"allowedVlans":"all","isolationEnabled":false,"rstpEnabled":true,"stpGuard":"bpdu guard","accessPolicyNumber":null,"linkNegotiation":"Auto negotiate","portScheduleId":null,"udld":"Alert only","macWhitelist":null,"stickyMacWhitelist":null,"stickyMacWhitelistLimit":null}
A trunk port does not use the voice vlan information. If you view a trunk port in the dashboard it does not even show the voice vlan information. Try one of the following:
Change an access port with a voice vlan value and remove the value for the voice vlan via the API.
Take an access port with a voice vlan and change it to a trunk port. Then change the port to an access port with no voice vlan information.
Hey I know it doesn't use the voice vlan info. What i'm saying is I can even remove the value in that scenario.
My 1st test was an access port with voice vlan removing.
Here is my change log with the changes you requested.
Show your script and we might be able to see what's going wrong.
1 creating access port with voice vlan
via API
PUT /api/v0/devices/****-****-****/switchPorts/5
old
{"number":5,"name":"test voice Vlan Update","tags":null,"enabled":true,"poeEnabled":true,"type":"trunk","vlan":10,"voiceVlan":null,"allowedVlans":"all","isolationEnabled":false,"rstpEnabled":true,"stpGuard":"bpdu guard","accessPolicyNumber":null,"linkNegotiation":"Auto negotiate","portScheduleId":null,"udld":"Alert only","macWhitelist":null,"stickyMacWhitelist":null,"stickyMacWhitelistLimit":null}
new
{"number":5,"name":"test voice Vlan Update","tags":null,"enabled":true,"poeEnabled":true,"type":"access","vlan":20,"voiceVlan":25,"allowedVlans":"all","isolationEnabled":false,"rstpEnabled":true,"stpGuard":"bpdu guard","accessPolicyNumber":null,"linkNegotiation":"Auto negotiate","portScheduleId":null,"udld":"Alert only","macWhitelist":null,"stickyMacWhitelist":null,"stickyMacWhitelistLimit":null}
2 changing to trunk port
via API
PUT /api/v0/devices/****-****-****/switchPorts/5
old
{"number":5,"name":"test voice Vlan Update","tags":null,"enabled":true,"poeEnabled":true,"type":"access","vlan":20,"voiceVlan":25,"allowedVlans":"all","isolationEnabled":false,"rstpEnabled":true,"stpGuard":"bpdu guard","accessPolicyNumber":null,"linkNegotiation":"Auto negotiate","portScheduleId":null,"udld":"Alert only","macWhitelist":null,"stickyMacWhitelist":null,"stickyMacWhitelistLimit":null}
new
{"number":5,"name":"test voice Vlan Update","tags":null,"enabled":true,"poeEnabled":true,"type":"trunk","vlan":10,"voiceVlan":25,"allowedVlans":"all","isolationEnabled":false,"rstpEnabled":true,"stpGuard":"bpdu guard","accessPolicyNumber":null,"linkNegotiation":"Auto negotiate","portScheduleId":null,"udld":"Alert only","macWhitelist":null,"stickyMacWhitelist":null,"stickyMacWhitelistLimit":null}
3 changing back to access port and removing voice vlan.
via API
PUT /api/v0/devices/****-****-****/switchPorts/5
old
{"number":5,"name":"test voice Vlan Update","tags":null,"enabled":true,"poeEnabled":true,"type":"trunk","vlan":10,"voiceVlan":25,"allowedVlans":"all","isolationEnabled":false,"rstpEnabled":true,"stpGuard":"bpdu guard","accessPolicyNumber":null,"linkNegotiation":"Auto negotiate","portScheduleId":null,"udld":"Alert only","macWhitelist":null,"stickyMacWhitelist":null,"stickyMacWhitelistLimit":null}
new
{"number":5,"name":"test voice Vlan Update","tags":null,"enabled":true,"poeEnabled":true,"type":"access","vlan":10,"voiceVlan":null,"allowedVlans":"all","isolationEnabled":false,"rstpEnabled":true,"stpGuard":"bpdu guard","accessPolicyNumber":null,"linkNegotiation":"Auto negotiate","portScheduleId":null,"udld":"Alert only","macWhitelist":null,"stickyMacWhitelist":null,"stickyMacWhitelistLimit":null}
OK, I know what is happening, I just don't know why. I am pulling the current configuration of a port and I get the following:
{'number': 5, 'name': '', 'tags': None, 'enabled': True, 'poeEnabled': False, 'type': 'trunk', 'vlan': 1, 'voiceVlan': 10, 'allowedVlans': 'all', 'isolationEnabled': False, 'rstpEnabled': True, 'stpGuard': 'disabled', 'accessPolicyNumber': None, 'linkNegotiation': 'Auto negotiate', 'portScheduleId': None, 'udld': 'Alert only', 'macWhitelist': None, 'stickyMacWhitelist': None, 'stickyMacWhitelistLimit': None}
I am then changing the data to update the type, vlan, name, and voice vlan. The data being sent to the PUT is:
{'number': 5, 'name': 'Test Port', 'tags': None, 'enabled': True, 'poeEnabled': False, 'type': 'access', 'vlan': 2, 'voiceVlan': None, 'allowedVlans': 'all', 'isolationEnabled': False, 'rstpEnabled': True, 'stpGuard': 'disabled', 'accessPolicyNumber': None, 'linkNegotiation': 'Auto negotiate', 'portScheduleId': None, 'udld': 'Alert only', 'macWhitelist': None, 'stickyMacWhitelist': None, 'stickyMacWhitelistLimit': None}
The data that comes back following the PUT, with a response code of 200 is:
{'number': 5, 'name': 'Test Port', 'tags': None, 'enabled': True, 'poeEnabled': False, 'type': 'access', 'vlan': 2, 'voiceVlan': 10, 'allowedVlans': 'all', 'isolationEnabled': False, 'rstpEnabled': True, 'stpGuard': 'disabled', 'accessPolicyNumber': None, 'linkNegotiation': 'Auto negotiate', 'portScheduleId': None, 'udld': 'Alert only', 'macWhitelist': None, 'stickyMacWhitelist': None, 'stickyMacWhitelistLimit': None}
Notice how all of the values were updated, except the voice vlan? That is why it seems like something is not working behind the scenes.
The other thing that seems odd is that in every other API PUT endpoint I have used, I have always had to convert the Python list/dictionary holding the data to a JSON string to replace the single quotes with ", None with null, False with false, and True with true for it to be accepted. This time the JSON compatible string still gets the response 200, but no changes at all are made to the data. Sending the Python dictionary in the PUT makes the changes listed above.
i've opened up a support case too.
I had this working some months back, looks like they've changed the API ?
i even tried to set the voicevlan to "null" in stead of $NULL but still a 400.
This working:
I hope you have better luck with that case than I have. The last time I tried to open a case for an API issue, I was told that "API problems are beyond the scope of regular Meraki support". They actually pointed me here to get some help.
are you able to provide how your json looks which is being sent in the script
$body = @{ "name" = $name "tags" = $tags "enabled" = $enabled "type" = $type "vlan" = $vlan "voiceVlan" = $voiceVlan "allowedVlans" = $allowedVlans "poeEnabled" = $poeEnabled "isolationEnabled" = $isolationEnabled "rstpEnabled" = $rstpEnabled "stpGuard" = $stpGuard #"accessPolicyNumber" = $accessPolicyNumber "linkNegotiation" = $linkNegotiation #"portScheduleId" = $portScheduleId "udld" = $udld #"macWhitelist" = $macWhitelist #"stickyMacWhitelist" = $stickyMacWhitelist "stickyMacWhitelistLimit" = $stickyMacWhitelistLimit } | ConvertTo-Json
this is how mine looks when sent to api.meraki and works fine
{
"name":"test voice Vlan Update",
"tags":null,
"enabled":true,
"poeEnabled":true,
"type":"access",
"vlan":10,
"voiceVlan":null,
"allowedVlans": "all",
"isolationEnabled": false,
"rstpEnabled": true,
"stpGuard": "BPDU guard",
"accessPolicyNumber": null,
"linkNegotiation": "Auto negotiate",
"portScheduleId": null,
"udld": "Alert only"
}
"stickyMacWhitelistLimit": "2",
that shouldn't be defined as originally requested
The "voiceVlan" tag is also not a string. It is a number, so "" and "null" would not be accepted. A value of null should work based on what @MarkD_BT has already posted.
I have found my problem in getting the ports to update. It actually stemmed from the other thing that was bothering me about this, not seeming to need to convert the Python dictionary with the data to a JSON string. I realized that the test script I was running specified the API key for the header, but not the content type. I added {'Content-Type':'application/json'} to the header and all of the sudden I was getting the 400 error that others have been seeing. Once I removed the macWhitelist , stickyMacWhitelist, and stickyMacWhitelistLimit tags from the port data I had already read, I got my response 200 and the port updated, including the clearing of the voice vlan!
So, here is a word of warning to all Python programmers that are not using meraki.py, make sure specify your content type for any endpoints that use PUT or POST. You would think that application/json would be the default for this API, but clearly it will let you pass data to it without a content type and you can actually make changes. Perhaps not all the ones you want to, but a good many. It also can give you response codes that make it looks like your call was successful, when it really wasn't.
Hey @CBurkhead
Have you got it sorted and working now? I do alot of testing with a test network to make sure the api only touches what I want it to had a few that would have caused problems. Can say always test before rollout I remember alerts being a strange one where everything has to be assigned correctly couldn't leave out of the call or would implement strange behaviour.
I was actually working on this so that I could hopefully help @sebas figure out what was going wrong with his script. Once I got into it, then I was trying to figure out if there was something wrong with the API or with my code. I was using a test script that I use when testing new endpoints or when I just need some quick info. It is just a single main procedure with basic setup and the API calls and no formalized error handling.
That was part of why I ran into problems. Most of the endpoints I had been using in it recently were GET and so I had only specified the API key in the header. When I am doing a formal script I have functions that I will copy from other scripts that either have the exact endpoint I need or that I will modify. They already have all the header information, so I don't have to think about it.
I know what you mean about alerts. I have done some quick scripts to set various values, but I am getting ready to work on a script that will allow all of the alert options for appliance, switch, and wireless to be enabled, disabled, and modified. I know that will be complicated and will have more than a few pitfalls.
Thank you for the help on this.
I do have:
'Content-Type': 'application/json',
in my script but I am having this exact same issue. I can update the voice vlan to any integer value but I cannot clear it via the API. I always get a return value of 200 but it does not clear the existing value.
Just figured it out. Had to convert 'None' to None. json.dumps will automatically convert None to null.
Using Python:
if new_dict['type'] == 'access':
new_dict['voiceVlan'] = None if new_dict['voiceVlan'] == 'None' else new_dict['voiceVlan']
Hi @MarkD_BT ,
Removing that parameter does not improve things.
You guys are talking python, i am using powershell, maybe that differs ?
I've tried $voicevlan=$null, $voicevlan="", $voicevlan=null all without result.
Did you try my function in powershell ?
Hi @sebas
I Use powershell and working in powershell. You can call the variable $null but what's important is the format and information it contains. The info in your output json is the most important part and should look like my example.
Hi @MarkD_BT ,
Did you try my function ?
I changed $voicevlan to [int] but i can't get the json to have a null value ?
Sorry not in the office today so not been at my machine quick dirty way to solve your issue would to do this
it's how your defining your parameters where it's causing the issue would have to look into (google) how you can set the value there as a true null.
$body = @{
"name" = $name
"tags" = $tags
"enabled" = $enabled
"type" = $type
"vlan" = $vlan
"voiceVlan" = $null
"allowedVlans" = $allowedVlans
"poeEnabled" = $poeEnabled
"isolationEnabled" = $isolationEnabled
"rstpEnabled" = $rstpEnabled
"stpGuard" = $stpGuard
#"accessPolicyNumber" = $accessPolicyNumber
"linkNegotiation" = $linkNegotiation
#"portScheduleId" = $portScheduleId
"udld" = $udld
#"macWhitelist" = $macWhitelist
#"stickyMacWhitelist" = $stickyMacWhitelist
#"stickyMacWhitelistLimit" = $stickyMacWhitelistLimit
} | ConvertTo-Json
Hi @MarkD_BT ,
I've managed to fix this by only adding the voicevlan to the body when it is specified:
$bodyhashtable = @{
"enabled" = $enabled
"type" = $type
"vlan" = $vlan
"allowedVlans" = $allowedVlans
"poeEnabled" = $poeEnabled
"isolationEnabled" = $isolationEnabled
"rstpEnabled" = $rstpEnabled
"stpGuard" = $stpGuard
"linkNegotiation" = $linkNegotiation
"udld" = $udld
}
# If name defined enter variable, else reset to null
if ($name) { $bodyhashtable += @{"name" = $name } } else { $bodyhashtable += @{"name" = $null } }
# If tags defined enter variable, else reset to null
if ($tags) { $bodyhashtable += @{"tags" = $tags } }
else { $bodyhashtable += @{"tags" = $null } }
# If voicevlan defined enter variable, else reset to null
if ($voicevlan -gt 0) { $bodyhashtable += @{"voiceVlan" = $voicevlan } }
else { $bodyhashtable += @{"voiceVlan" = $null } }
$body = $bodyhashtable | ConvertTo-Json
Thanks !
Hi @MarkD_BT ,
Another question, what value do you get returned for accessPolicyNumber when having "sticky mac whitelist" selected ?
hi @sebas
We dont have any acl's on our switchports here as only used for Meraki Ap's. With my quick testing of the Update port API it was throwing error's. So doing some testing of the different parameters I saw that having the following in
macWhitelist
stickyMacWhitelist
stickyMacWhitelistLimit
Caused these errors so i just stripped the entries out as wasn't required. Not really looking into why these were causing a error. More than likely a formatting thing.
I don't need to use it at the moment so wasn't really worried about it yet 😄
Hi Mark,
no improvement when removing the mac limit.