Hi,
I'm try to combine networks with the Network Combine API. I put the name of the combined network, the ids of networks, but the API send me an error : "'networkIds' must be an array", but for me it's already an array. Someone have an idea to help me with this ?
This is my request body :
Solved! Go to solution.
I believe the cause of the problem is your header "application.json". Try changing it to "application/json".
Fyi, these are the headers I use:
headers = {
'Accept': "*/*",
'Content-Type': "application/json",
'cache-control': "no-cache",
'X-Cisco-Meraki-API-Key': api_key
}
What kind of networks are those? The reason I ask is because of this note:
Only one network of each type can be combined. For example, multiple wireless networks cannot be combined with other networks. An administrator would need to move all wireless devices into a single wireless network prior to the combination. The same applies for multiple switch networks, all devices would need to be moved into a single switch network. Only one MX can be added to a Combined network.
At this time, VPN Concentrators cannot be added to a combined Dashboard network
I just tested combining a switch and a wireless network with that API in postman and it worked fine for me. The body syntax was identical to yours.
I combined an MX with a switch network, the two are bound on a template. one MX and 1 switch.
When i combined the network with the dashboard GUI, it's work.
Okay, I just tested combining a switch network and an mx network via API and it worked. Both were bound to the same template.
Are you using postman or something else?
I'm using Postman for troubleshoot. Initially, i call the API in a Python script
I don't understand where is the problem with my 'networkIds'...
I don't understand either, seems fine. If you share your code I can give it a test here.
Python functions :
def combine_networks(mx_networkid,temp_networkid):
params_combine = {"name" : mx_network_name,
"networkIds" : [mx_networkid,temp_networkid]
}
api_path_combine = "organizations/" + OrgID + "/networks/combine"
url_combine = serveur + api_path_combine
r_combine = requests.request("POST", url_combine, headers=headers, params=params_combine)
print ("les network sont combines")
print (r_combine)
return;
and the Postman URL :
{{baseUrl}}/organizations/{{organizationId}}/networks/combine?name =MAG&networkIds= ['N_691865492754826821' , 'N_691865492754826857']
Thanks for your help
That is the same error I have been seeing using a different endpoint (/networks/{networkId}/events) for the includedEventTypes parameter. No matter how I specify the data, I am told that that it must be an array. Hopefully, the answer for this problem will be the same for this other endpoint.
Okay, there's two things wrong with your code.
You haven't json dumped your data payload.
And you named your payload "params" instead of "data".
This is the working code:
def combine_networks(mx_networkid, temp_networkid):
params_combine = {
"name": mx_network_name,
"networkIds": [
mx_networkid,
temp_networkid ]
}
params_json = json.dumps(params_combine)
api_path_combine = "organizations/" + OrgID + "/networks/combine"
url_combine = serveur + api_path_combine
r_combine = requests.request("POST", url_combine, headers=headers, data=params_json)
print(str(r_combine.status_code) + " - " + r_combine.text)
return r_combine.status_code
I removed the print("les networks sont combines") line because it's actually not true. Whether or not the networks have succesfully combined depends on whether the call was successful or not. If r_combine.status_code equals 200 then it was successful. So perhaps you can return that value, or evaluate it in your function and return 1 or 0 based on the result.
Thank you a lot for your correction on my code, it's still on draft phase
I don't json dumped my data on the other API call i do,
if i json dumped my data payload with keyword "data" i receive :
400 - {"errors":["The following required parameters are missing: 'name' and 'networkIds'"]}
print (params_json) : {"name": "5895", "networkIds": ["N_691865492754826870", "N_691865492754826891"]}
If i keep the json but with "params" keyword, same error
I use python 3.7.0 on Windows, requests 2.21.0
Thanks
Hmm strange. It works fine for me. Can you share your full code? I'll try to debug it again.
My code try to resolve a client problem : he put 1000 switchs into one network but he have already one network per site with the mx. So my code :
1 - find the mx network with the site id
2 - get the list port of the switch
3 - create a network for the switch
4 - Remove the switch of the big network
5 - put in the switch network i create
6 - Bind it to the global template (Combined template, mx network already in)
7 - Combine the two networks
8 - push the port config on switch to avoid problems on site
My code is still under construction, but if you find the combine problems, it's help me a lot :
#Migration_Switchs_Meraki
import json
import sys
import requests
import csv
#Déclaration des variables pour l'ensemble des Requests
headers = {
'Content-Type' : 'application.json',
'X-Cisco-Meraki-API-Key': "*"
}
serveur = "https://api.meraki.com/api/v0/"
#Déclaration des variables globales
OrgID = "*"
switch_networkid = "N_69186549275482820"
switch_info = []
#Récupération des informations de l'inventaire
with open('inventory.csv') as csvfile:
inventory = csv.reader(csvfile, delimiter=',')
for line in inventory:
info = {
"serial" : line[1],
"name" : line[7],
}
switch_info.append(info)
csvfile.close()
del switch_info[0]
#récupération_des_premiers_caractères_du nom
#print (switch_info[0]['name'][:4])
#Récupération des networks pour recherche du network MX Magasin
api_path_mx_network = "organizations/" + OrgID + "/networks"
url_mx_network = serveur + api_path_mx_network
mx_networks = requests.request("GET", url_mx_network, headers=headers)
if mx_networks.status_code == 200:
print ("Liste des reseaux : [OK]")
else:
print ("Liste des reseaux : [NOK]")
sys.exit()
networks_data = mx_networks.json()
#Départ de déclaration des fonctions
#Fonction de recherche de l'ID Site dans les noms de Network Meraki MX
def find_mx_network(mag_id):
for n in networks_data:
if mag_id in n['name']:
mx_networkid = n['id']
templateid = n['configTemplateId']
print ("Le nom du réseau MX Magasin : " + n['name'])
break
return mx_networkid,templateid;
#Fonction de listing de configuration des ports du switch migré
def listport(switch_serial):
api_path_list_port = "devices/" + str(switch_serial) + "/switchPorts"
url_list_port = serveur + api_path_list_port
s_list_port = requests.request("GET", url_list_port, headers=headers)
if s_list_port.status_code == 200:
print ("Liste des ports switch : [OK]")
else:
sys.exit()
s_list_port_data = s_list_port.json()
return s_list_port_data;
#Fonction de création du network temporaire du switch migré
def create_network(switch_id_name):
params_create_net = {"name" : switch_id_name,
"type" : "switch",
"timeZone" : "Europe/Paris"
}
api_path_cnet = "organizations/" + OrgID + "/networks"
url_cnet = serveur + api_path_cnet
cnet = requests.request("POST", url_cnet, headers=headers, params=params_create_net)
if cnet.status_code == 201:
print ("Creation network temporaire : [OK]")
else:
print ("Creation network temporaire : [NOK]")
sys.exit()
cnet_data = cnet.json()
return cnet_data;
#Fonction de suppression du switch du network SWITCH
def remove_switch(switch_serial):
api_path_remove = "networks/" + switch_networkid + "/devices/" + switch_serial + "/remove"
url_remove = serveur + api_path_remove
r_remove_switch = requests.request("POST", url_remove, headers=headers)
print ("le switch est supprimé du network global")
print (r_remove_switch)
return;
#Fonction d'ajout dans le network temporaire
def add_switch(temp_networkid,switch_serial):
params_addswitch = {"serial" : switch_serial}
api_path_addswitch = "networks/" + temp_networkid + "/devices/claim"
url_add = serveur + api_path_addswitch
r_add_switch = requests.request("POST", url_add, headers=headers, params=params_addswitch)
print ("le switch est ajoute au network temporaire")
print (r_add_switch)
return;
#Fonction de liaison du network temporaire avec le template global
def Template_bind(templateid, networkid):
params_btemp = {"configTemplateId" : templateid,
"autoBind": "True"
}
api_path_btemp = "networks/" + networkid + "/bind"
url_btemp = serveur + api_path_btemp
btemp = requests.request("POST", url_btemp, headers=headers, params=params_btemp)
print ("le reseau temporaire est binde :")
print (btemp)
return;
#Fonction de combinaison du network temporaire avec le network MX
def combine_networks(mx_networkid,temp_networkid,dest_network_name):
params_combine = {"name" : dest_network_name,
"networkIds" : [
mx_networkid,
temp_networkid
]
}
params_combine_json = json.dumps(params_combine)
api_path_combine = "organizations/" + OrgID + "/networks/combine"
url_combine = serveur + api_path_combine
r_combine = requests.request("POST", url_combine, headers=headers, data=params_combine_json)
print(str(r_combine.status_code) + " - " + r_combine.text)
return r_combine.status_code;
#Ajout de la config initial au switch migré
def update_config(list_port,switch_serial):
for p in list_port:
api_path_update_config = "devices/" + switch_serial + "/switchPorts/" + str(p['number'])
url_update_config = serveur + api_path_update_config
params_update_config = {"name" : p['name'],
"tags" : p['tags'],
"enabled" : p['enabled'],
"type" : p['type'],
"vlan" : p['vlan'],
"voiceVlan" : p['voiceVlan'],
"allowedVlans" : p['allowedVlans'],
"poeEnabled" : p['poeEnabled'],
"isolationEnabled" : p['isolationEnabled'],
"rstpEnabled" : p['rstpEnabled'],
"stpGuard" : p['stpGuard'],
"accessPolicyNumber" : p['accessPolicyNumber'],
"linkNegotiation" : p['linkNegotiation'],
"portScheduleId" : p['portScheduleId'],
"udld" : p['udld'],
"macWhitelist" : p['macWhitelist'],
"stickyMacWhitelist" : p['stickyMacWhitelist'],
"stickyMacWhitelistLimit" : p['stickyMacWhitelistLimit']
}
r_update = requests.request("PUT", url_update_config, headers=headers, params=params_update_config)
print ("Interface " + str(p['number']) + " est mise à jour")
print (r_update)
return;
for device in switch_info:
mag_id = device['name'][:4]
dest_network_name = mag_id + "-Magasins"
#récupération de l'ID du network MX
mx_networkid, templateid = find_mx_network(mag_id)
#Stockage de la liste des ports actuelle
list_port = listport(device['serial'])
#
#Création du network temporaire
cnet_data = create_network(mag_id)
#
#Récupération de l'ID
temp_networkid = cnet_data['id']
#
#Suppression du switch d'un network
remove_switch(device['serial'])
#
#Ajout du switch au network temporaire
add_switch(temp_networkid,device['serial'])
#
#Liaison au template global
Template_bind(templateid, temp_networkid)
#
#Combinaison des réseaux
combine_networks(mx_networkid,temp_networkid,dest_network_name)
#
#Update de la config de ports
update_config(list_port,device['serial'])
Thanks,
I believe the cause of the problem is your header "application.json". Try changing it to "application/json".
Fyi, these are the headers I use:
headers = {
'Accept': "*/*",
'Content-Type': "application/json",
'cache-control': "no-cache",
'X-Cisco-Meraki-API-Key': api_key
}
Thanks !!!!
All of that for a little "/", thank you !
@MaCATTEAU wrote:Thanks !!!!
All of that for a little "/", thank you !
It's always that missing or wrong punctuation that gets you. My particular nemesis is using greater than instead of less than. 🙂