Can't create an unmanaged subnet with MAAS API

Hello,

I’m trying to create a subnet that is not managed by maas.

According to the documentation, in the call POST /MAAS/api/2.0/subnets/, there’s a parameter managed (int), which can be set to 0 (false) or 1 (true).

It doesn’t matter which value I assign to it, it will be ignored.

How to reproduce it:


import oauth2
import httplib2
import uuid
import os
import binascii
import json

API_KEY = "xxxxxxxxxxxx:xxxxxxxxxxxxxxxxx:xxxxxxxxxxxxxxx"
[consumer_key, key, secret] = API_KEY.split(':')

BASE_URL = 'http://<ip>:5240/MAAS/api/2.0'

def api_maas(uri, method, data=None, message=None):
    token_uri = 'oauth_token_secret={}&oauth_token={}'
    token_str = token_uri.format(secret, key)
    resource_token = oauth2.Token.from_string(token_str)
    consumer_token = oauth2.Consumer(consumer_key, '')

    request = oauth2.Request.from_consumer_and_token(
        consumer_token,
        token=resource_token,
        http_url=BASE_URL,
        parameters={'oauth_nonce': uuid.uuid4().hex}
    )

    request.sign_request(
        oauth2.SignatureMethod_PLAINTEXT(),
        consumer_token, resource_token)

    url = '%s%s' % (BASE_URL, uri)
    headers = request.to_header()

    http = httplib2.Http()
    if data is not None:
        body, c_t = encode_multipart_formdata(data)
        headers['content-type'] = c_t
    else:
        body = None
    (r, c) = http.request(url, method, body=body, headers=headers)

    if r.status >= 200 and r.status < 300:
        return c


def encode_multipart_formdata(fields):
    boundary = binascii.hexlify(os.urandom(16)).decode('ascii')

    body = (
        "".join("--%s\r\n"
                "Content-Disposition: form-data; name=\"%s\"\r\n"
                "\r\n"
                "%s\r\n" % (boundary, field, value)
                for field, value in fields.items()) +
        "--%s--\r\n" % boundary
    )

    content_type = "multipart/form-data; boundary=%s" % boundary

    return body, content_type


data = {
    'cidr': '172.16.0.0/24',
    'name': 'test',
    'description': '',
    'fabric': '0',
    'vid': 0,
    'gateway_ip': '172.16.0.1',
    'rdns_mode': 0,
    'allow_dns': 1,
    'allow_proxy': 0,
    'dns_servers': '8.8.8.8',
    'managed': 0
}

c = api_maas('/subnets/', 'POST', data=data)

print(json.dumps(json.loads(c), separators=(',',':'), sort_keys=True, indent=4))

This is the output:

{
    "active_discovery":false,
    "allow_proxy":true,
    "cidr":"172.16.0.0/24",
    "dns_servers":[
        "8.8.8.8"
    ],
    "gateway_ip":"172.16.0.1",
    "id":126,
    "managed":true,
    "name":"test",
    "rdns_mode":0,
    "resource_uri":"/MAAS/api/2.0/subnets/126/",
    "space":"undefined",
    "vlan":{
        "dhcp_on":false,
        "external_dhcp":null,
        "fabric":"fabric-0",
        "fabric_id":0,
        "id":5001,
        "mtu":1500,
        "name":"untagged",
        "primary_rack":null,
        "relay_vlan":null,
        "resource_uri":"/MAAS/api/2.0/vlans/5001/",
        "secondary_rack":null,
        "space":"undefined",
        "vid":0
    }
}

The output shows: managed: true

Hmm, what happens if you try False instead of 0?

@sparkiegeek
Same result!

As a note! If I enable dhcp on that vlan, I get an error because I haven’t set a dynamic reserved range, which is expected! BUT, after that error, the subnet is in managed: False state, which is weird

Thanks, I’m looking into this, along with https://bugs.launchpad.net/maas/+bug/1830619 which seems very similar.

After digging into the code and running some tests, I believe the bug is in the API documentation.

Instead of passing an integer, you should pass a boolean. Note that ‘0’ explicitly does not work (is interpreted as True), which is what your script will end up sending due to encode_multipart_formdata using %s. Instead if you pass in 'false' it is correctly interpreted as False