Friday, August 17, 2018

A Guide to IP Subnetting

Counting In Binary

In order to understand IP subnetting it is important to first understand how to count in binary. As a first step, lets start with something familiar; counting in decimal.

Consider the progression of the following number sequence:

00
01
02
..
08
09
10
11
12
..
18
19
20

In decimal, there are 10 characters available for use: the numbers 0 through 9. Starting at zero, we count by incrementing the first column by one until we reach the final character of 9. At this point the first column resets to zero and the second column is incremented by one. We then iterate the characters in the first column until we again reach 9, at which point we repeat the process.

Counting in binary works identically. The only difference is that in binary we only have two characters available: the numbers 0 and 1. Consider the following binary number sequence. See if you can spot the pattern. The decimal conversion is represented within the () next to each binary sequence.

0000 (0)
0001 (1)
0010 (2)
0011 (3)
0100 (4)
..
1100 (12)
1101 (13)
1110 (14)
1111 (15)

 

Quickly Converting Between Binary and Decimal

Quickly, what is binary 1100 1010 in decimal? If you are unfamiliar with the shortcut, then you probably took the approach of starting a 0000 0000 and counting up. This approach is painful. Luckily there is an easier way if you keep the following fact in mind: each column in a binary sequence has a place value. This is similar to decimal, where each column has a place value which is a mulltiple of 10. In binary, the difference is that columns have place values which are powers of 2.

The following table illustrates this:

| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1
|-----|----|----|----|---|---|---|---
|  1  |  1 |  0 |  0 | 1 | 0 | 1 | 0

As seen above, each column is assigned the value which is a power of 2. In other words, each column is double the value of the previous column. I have mapped the number 1100 1010 into this table. Using the place values of each column we can convert to decimal simply by adding the value of each column where a “1” is present. In this case we have 128 + 64 + 8 + 2 = 202. Easy.

In order to convert decimal to binary we would use a similar approach. If I want to convert 202 to binary then I would start subtracting large powers of 2 from 202 and tracking them in a table. So, 202 - 128 = 74. Mark a “1” in the 128 column. 74 - 64 = 10. Mark a “1” in the 64 column. 10 is less than 32 and 16, but we can do 10 - 8 = 2. Mark a “1” in the 8 column. 2 is less than 4, but we can do 2 - 2 = 0. Mark a “1” in the 2 column. Done. We end up with the same table as above:

|128 | 64 | 32 | 16 | 8 | 4 | 2 | 1
|----|----|----|----|---|---|---|---
|  1 |  1 |  0 |  0 | 1 | 0 | 1 | 0

 

Hexadecimal

Typically, binary is written using groups of 4 characters:

1010 1111 1011 0000

Breaking the binary sequence into groups of 4 helps with readability. Hexadecimal, or hex, is largely used as shorthand for binary and is used to shorten 4 binary characters into a single hex character. We know that a group of 4 binary characters can hold a maximum value of 15 (8 + 4 + 2 + 1 = 15), therefore hex must contain 16 characters if it is to be used as shorthand for groups of 4 binary characters. The characters of hex are the first 10 characters of decimal (0-9) plus an additional 6 characters (a=10, b=11, c=12, d=13, e=14, f=15) to make up the difference. Again, counting in hex works identically to that of decimal and binary:

00 (0)
01 (1)
02 (2)
..
09 (9)
0a (10)
0b (11)
0c (12)
0d (13)
0e (14)
0f (15)
10 (16)
11 (17)
12 (18)
..
1e (30)
1f (31)
20 (32)

Hexadecimal numbers can look exactly like decimal numbers when they are using only characters 0-9. This is why hex numbers are typically prefixed with a 0x since the prefix helps to differentiate it from decimal. In the sequence above it is deceptively easy to confuse hex 20 with decimal 20; hex 20 definitely does not equal decimal 20. So, to be explicit, we would typically write hex 20 as 0x20.

Lets look at an example of using hex. Consider the binary sequence 1100 1010. In order to convert this to hex we would simply convert each group of 4 to decimal and then convert that to hex. So, binary 1100 equals decimal 12. Decimal 12 is represented in hex as the character “c”. Binary 1010 equals decimal 10. Decimal 10 is represented in hex as the character “a”. The final conversion is: 1100 1010 -> 0xca.

So, how do we convert from hex to decimal. The most common way is via the indirect route of converting hex -> binary -> decimal. Using the previous example of 0xca, the conversion is as follows: 0xca -> 1100 1010 -> 202.

 

IP Addressing

An IP addres is simply a large integer, where IPv4 has 32 bits of capacity and IPv6 has 128 bits of capacity. Lets see an example of an IPv4 address as a binary number.

0111 1111 0000 0000 0000 0000 0000 0001

Imagine how difficult it would be to configure an IP address if we always represented them in binary. Instead, IP addresses are typically written using something other than binary. For IPv4, the most commonly used notation is that of a 4 groups of 8 bits, converted to decimal, and separated by periods. Using the example above:

0111 1111 0000 0000 0000 0000 0000 0001
0111 1111 . 0000 0000 . 0000 0000 . 0000 0001
127.0.0.1

Lets see an IPv6 example:

1111 1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001

Hopefully I didnt miss any bits in that huge string of ugly. Luckily IPv6 uses hex as shorthand so that the addressing is a bit more managable. Specifically, IPv6 uses groups of 16 bits broken up by colons. Using the above example:

1111 1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
1111 1111 1111 1111 : 0000 0000 0000 0000 : 0000 0000 0000 0000 : 0000 0000 0000 0000 : 0000 0000 0000 0000 : 0000 0000 0000 0000 : 0000 0000 0000 0000 : 0000 0000 0000 0001
ffff:0:0:0:0:0:0:1

IPv6 takes an additional step of compressing large sequences of zeros into a “::” (note that you can only use a single “::” in a given address). So the address above could be written as ffff::1.

 

IP Subnetting

In order to more effectively utilize the large 32 or 128 bit address space of IPv4 and IPv6, the designers of these protocols introduced the idea of subnetting. Subnetting may be though of as a means of breaking the entire collective of IP addresses into groups of smaller contiguous addresses (or networks). Subnetting allows us to break up the IP addressing space into smaller groups which may then be allocated to individual organizations. In turn, these organizations may utilize subnetting to further break up their allocated address space into yet smaller blocks for use within their local networks.

In order to represent a subnet there are 2 pieces of data required: an IP address and a subnet mask. The purpose of the mask is to deliniate the “network” portion of the address from the “host” portion of the address. Lets consider one of the most commonly size networks in use today; the /24. If I were to allocate the network 10.0.0.0/24 to you, then you would probably understand that you have been given the range of addresses 10.0.0.0 - 10.0.0.255. What you may not understand is the mechanism for determining this range of addresses.

Firstly, lets consider the subnet mask “/24” from the network 10.0.0.0/24. The subnet mask tells us that 24 bits are reserved for the “network” portion of the address while the remaining 8 bits (32 - 24 = 8) are reserved for the “host” portion. Knowing the range of IP addresses owned by this block is a matter of understanding that the “network” portion of the address is fixed (i.e. identical for all addresses on the network) and that only the “host” portion is variable per host on the network. Lets explore this further by representing both the subnet mask and the network address in binary:

   mask: 1111 1111 . 1111 1111 . 1111 1111 . 0000 0000
address: 0000 1010 . 0000 0000 . 0000 0000 . 0000 0000

In the above breakout we see that the first 24 bits of the total 32 bits are represented by 1’s. This matches the definition of a /24 subnet mask and tells us that all addresses in this network must share the same 24 bits. Lets iterate through some addresses of this network. In the below table I separate the network and host portions of each address. The #1 rule is that we cannot modify any bits from the network portion.

|network bits                      | host bits | address
|----------------------------------|-----------|----------
|0000 1010 . 0000 0000 . 0000 0000 | 0000 0000 | 10.0.0.0
|0000 1010 . 0000 0000 . 0000 0000 | 0000 0001 | 10.0.0.1
|0000 1010 . 0000 0000 . 0000 0000 | 0000 0010 | 10.0.0.2
|...                               | ...       | ...
|0000 1010 . 0000 0000 . 0000 0000 | 1111 1110 | 10.0.0.254
|0000 1010 . 0000 0000 . 0000 0000 | 1111 1111 | 10.0.0.255

As seen above, once all bits within the host portion have been set to “1” then we have reached the final address of the network. In the example above we see that the range of addresses for this network are 10.0.0.0 - 10.0.0.255, or 256 addresses (2 to the 8th power).

Lets look at an example using something other than a /24. Assume that we wanted to subnet 10.0.0.0/24 into a /30 instead. Here is the breakdown:

|network bits                                | host bits | address
|--------------------------------------------|-----------|----------
|0000 1010 . 0000 0000 . 0000 0000 . 0000 00 | 00        | 10.0.0.0
|0000 1010 . 0000 0000 . 0000 0000 . 0000 00 | 01        | 10.0.0.1
|0000 1010 . 0000 0000 . 0000 0000 . 0000 00 | 10        | 10.0.0.2
|0000 1010 . 0000 0000 . 0000 0000 . 0000 00 | 11        | 10.0.0.3

As we can see, we have “stolen” 6 bits from the host portion of the address in order to create our /30 network. This leaves us with only 2 bits in the host portion for addressing which means that the address range of 10.0.0.0/30 is 10.0.0.0 - 10.0.0.3, or 4 addresses (2 to the 2nd power).

What if we went the opposite direction and created a /23?

|network bits                     | host bits     | address
|---------------------------------|---------------|-----------
|0000 1010 . 0000 0000 . 0000 000 | 0 . 0000 0000 | 10.0.0.0
|0000 1010 . 0000 0000 . 0000 000 | 0 . 0000 0001 | 10.0.0.1
|0000 1010 . 0000 0000 . 0000 000 | 0 . 0000 0002 | 10.0.0.2
|...                              | ...           | ...
|0000 1010 . 0000 0000 . 0000 000 | 1 . 1111 1110 | 10.0.1.254
|0000 1010 . 0000 0000 . 0000 000 | 1 . 1111 1111 | 10.0.1.255

As we can see, we have “stolen” 1 bit from the network portion of the address in order to create our /23 network. This gives us 9 bits in the host portion for addressing which means that the address range of 10.0.0.0/23 is 10.0.0.0 - 10.0.1.255, or 512 addresses (2 to the 9th power).

Notice the pattern here:
  • for every bit we steal from the host portion, we halve the address space
  • for every bit we give to the host portion, we double the address space

This ties back to our exercises in binary counting. Everything operates on powers of 2.

Switching gears a bit, lets consider some simple sizing exercises.
  1. How many /25 networks can I create from a /24? In order to create /25 we would need to steal 1 bit from the host portion and give it to the network portion (25-24 = 1). 2 to the power of 1 is 2, so we would get 2 /25 networks from a /24.
  2. How many /30 networks can I create from a /24? Similar to the previous exercise, 30 - 24 = 6 so we need to steal 6 bits. 2 to the power of 6 is 64. So 64 /30 networks can be created from a single /24.
  3. How many addresses fit within a /30? We have 2 bits in the host portion of the address (32-30 = 2), so 2 to the 2nd power is 4.
  4. How many address fit within a /22? We have 10 bits in the host portion of the address (32-22 = 10), so 2 to the 10th power is 1024.
Lets consider a more complex problem.

If I have a /24 and I need to create 5 subnets from it, what would be the maximum size of these subnets? How many subnets would I get? What would be their network addresses?

Firstly, determine the number of bits we need to steal. Look at this incrementally:
  • 1 bit = 2 networks. Not enough.
  • 2 bits = 4 networks. Not enough.
  • 3 bits = 8 networks. Meets our requirement of 5 subnets.
So we need to steal 3 bits from the host portion, which gives us a /27. We can now determine the size of these subnets since we know that there are 5 bits remaining in the host portion of each subnet (32-27 = 5) and that 2 to the 5th power = 32. So we know that we will end up with 8 /27 subnets which can each hold 32 addresses.

How do we determine the network addresses for these subnets. Lets look at the binary breakout using 10.0.0.0/24 as our original network:

|network bits                      | stolen bits | host bits     | nework address
|----------------------------------|-------------|---------------|---------------
|0000 1010 . 0000 0000 . 0000 0000 | . 000       | 0 0000        | 10.0.0.0/27
|0000 1010 . 0000 0000 . 0000 0000 | . 001       | 0 0000        | 10.0.0.32/27
|0000 1010 . 0000 0000 . 0000 0000 | . 010       | 0 0000        | 10.0.0.64/27
|0000 1010 . 0000 0000 . 0000 0000 | . 011       | 0 0000        | 10.0.0.96/27
|0000 1010 . 0000 0000 . 0000 0000 | . 100       | 0 0000        | 10.0.0.128/27
|0000 1010 . 0000 0000 . 0000 0000 | . 101       | 0 0000        | 10.0.0.160/27
|0000 1010 . 0000 0000 . 0000 0000 | . 110       | 0 0000        | 10.0.0.192/27
|0000 1010 . 0000 0000 . 0000 0000 | . 111       | 0 0000        | 10.0.0.224/27

Notice that when determining the network addresses of our 8 /27 subnets, we have only incremented the bits which we stole from the host portion of our original /24. You may also notice that the result is that we are incrementing our network address by the size of each subnet, or by 32 in this case.

 

What About IPv6?

Quickly, how many /64 networks will fit into a single /63 IPv6 network? If you’re like most people, you’re thinking to yourself “… but I don’t know IPv6!”. However, if you pause for a moment then you will remember the IPv6 addresses are conceptually the same as IPv4 addresses, just longer. This means that the concept of IP subnetting is identical between the two address types. So, to find the answer to the previous question all you need to do is to solve the problem using the same technique as you would for IPv4 (the answer is 2, by the way).

Thursday, April 6, 2017

vCenter REST API and the Content Library

Untitled Document.md

Overview

In this post I will demonstrate the use of the REST API to place an ISO image within the content library of vCenter,
creating a new VM, and finally booting that VM from the ISO image.

I built these examples by following the documentation which is part of the REST SDK.

Getting Started

The examples assume the availability of a bash shell and curl, however, the tools themselves are not as
important as the actual process.

Set Environment Variables

Store the base URL of the vcenter host. Example:

VCENTER=https://vc1

Generate a unique client token for use later on.

CLIENT_TOKEN=$(uuidgen)

Authenticate

Post using http basic auth. Capture the session id which is returned.

curl -k -X POST -H 'Accept: application/json' \
--basic -u administrator@spinhirne.com:VMware1!  \
$VCENTER/rest/com/vmware/cis/session  | python -m json.tool

Store the session id within an environment variable. Example:

SESSION_ID=e37ebe2698fcf1b91af36cfbfcfd2acf

Creating a New Content Library

Get supported content library item types

Optional step, but informative.

vCenter has built in support for a couple of content types. Query the supported types. For this
guide we’ll make use of the “iso” type.

curl -k -X GET -H 'Accept: application/json' \
-H "vmware-api-session-id: $SESSION_ID" \
$VCENTER/rest/com/vmware/content/type  | python -m json.tool

Get a list of datastore IDs

Datastore IDs are needed in order to define a backend for local content libraries. Capture the
ID (the “datastore” attribute of the returned json) of each datastore you wish to use.

curl -k -X GET -H 'Accept: application/json' \
-H "vmware-api-session-id: $SESSION_ID" \
$VCENTER/rest/vcenter/datastore | python -m json.tool

Store the datastore IDs within an environment variable(s). Example:

DATASTORE0="datastore-12"

Create a new local library

Create a library and capture its UUID.

(curl -k -X POST \
-H 'Accept: application/json' -H "Content-Type: application/json" -H "vmware-api-session-id: $SESSION_ID" \
-d @- \
$VCENTER/rest/com/vmware/content/local-library \
<<- JSON
{
    "client_token": "$CLIENT_TOKEN",
    "create_spec": {
        "description": "test lib",
        "name": "testLib1",
        "storage_backings": [
            {
                "datastore_id": "$DATASTORE0",
                "type": "DATASTORE"
            }
        ],
        "type": "LOCAL"
    }
}
JSON
) | python -m json.tool

Store the UUID within an environment variable. Example:

LIBRARY=d38b7d74-70c5-4fa5-b4b6-5cd9001457ba

Add an ISO item to the library

Locate an iso file and capture the file name as an environment variable. Example:

ISO_FILE=/tmp/test.iso

Capture its size and md5sum as well:

ISO_MD5=$(md5sum $ISO_FILE | gawk '{print $1}')
ISO_SIZE=$(ls -l $ISO_FILE | gawk '{print $5}')

Create a library item for this iso.

(curl -k -X POST \
-H 'Accept: application/json' -H "Content-Type: application/json" -H "vmware-api-session-id: $SESSION_ID" \
-d @- \
$VCENTER/rest/com/vmware/content/library/item \
<<- JSON
{
        "client_token": "$CLIENT_TOKEN",
        "create_spec": {
                "description": "a custom iso",
                "library_id": "$LIBRARY",
                "name": "$(basename $ISO_FILE)",
                "type": "iso"
        }
}
JSON
) | python -m json.tool

Capture the library item within an environment variable. Example:

LIBRARY_ITEM=b568e170-4702-42d4-804a-93b35f678e30

Create an update session

An update session is needed in order to upload the actual iso file.

(curl -k -X POST \
-H 'Accept: application/json' -H "Content-Type: application/json" -H "vmware-api-session-id: $SESSION_ID" \
-d @- \
$VCENTER/rest/com/vmware/content/library/item/update-session \
<<- JSON
{
        "client_token": "$CLIENT_TOKEN",
        "create_spec": {
            "library_item_id": "$LIBRARY_ITEM"
        }
}
JSON
) | python -m json.tool

Capture the update session within an environment variable. Example:

UPDATE_SESSION=39bf1ccb-d7b9-4aff-b250-83b3c6c3620a:8bed2a31-ce25-4d9d-b0e8-dc1a861ebc1e

Request URI for file upload

We’re going to use a PUSH session to push the file to vCenter. For this, vCenter will give us an
endpoint for the upload.

(curl -k -X POST \
-H 'Accept: application/json' -H "Content-Type: application/json" -H "vmware-api-session-id: $SESSION_ID" \
-d @- \
$VCENTER/rest/com/vmware/content/library/item/updatesession/file/id:$UPDATE_SESSION?~action=add \
<<- JSON
{
    "file_spec": {
        "checksum_info": {
            "algorithm": "MD5",
            "checksum": "$ISO_MD5"
        },
        "name": "$(basename $ISO_FILE)",
        "size": $ISO_SIZE,
        "source_type": "PUSH"
    }
}
JSON
) | python -m json.tool

Capture the upload endpoint with an environment variable. Example:

UPLOAD_ENDPOINT=$VCENTER/cls/data/75be4d03-dcaa-451a-b946-1ba0aa38f947/dsl-4.4.10.iso

Upload the file

Upload the file using a PUT request with the file uploaded as multipart form data.

curl -v -k -X PUT \
-F "$(basename $ISO_FILE)=@$ISO_FILE" \
-H 'Accept: application/json' -H "vmware-api-session-id: $SESSION_ID" \
$UPLOAD_ENDPOINT

Validate the upload

Check for any upload errors.

curl -k -X POST \
-H 'Accept: application/json' -H "Content-Type: application/json" -H "vmware-api-session-id: $SESSION_ID" \
$VCENTER/rest/com/vmware/content/library/item/updatesession/file/id:$UPDATE_SESSION?~action=validate \
| python -m json.tool

Complete or fail the session

If there are any validation errors then fail the session:

curl -k -X POST \
-H 'Accept: application/json' -H "Content-Type: application/json" -H "vmware-api-session-id: $SESSION_ID" \
-d '{"client_error_message": "upload failed"}' \
-w "%{http_code}\n" \
$VCENTER/rest/com/vmware/content/library/item/update-session/id:$UPDATE_SESSION?~action=fail

Otherwise mark it as complete. Marking it complete causes the item to actually update with the uploaded content.

curl -k -X POST \
-H 'Accept: application/json' -H "Content-Type: application/json" -H "vmware-api-session-id: $SESSION_ID" \
-w "%{http_code}\n" \
$VCENTER/rest/com/vmware/content/library/item/update-session/id:$UPDATE_SESSION?~action=complete

Get the status of the update session

Optional step, but informative.

curl -k -X GET -H 'Accept: application/json' \
-H "vmware-api-session-id: $SESSION_ID" \
$VCENTER/rest/com/vmware/content/library/item/update-session/id:$UPDATE_SESSION \
| python -m json.tool

Delete the update session

Clean up.

curl -k -X DELETE -H 'Accept: application/json' \
-H "vmware-api-session-id: $SESSION_ID" \
-w "%{http_code}\n" \
$VCENTER/rest/com/vmware/content/library/item/update-session/id:$UPDATE_SESSION 

Creating a New VM

Get a list of folder IDs

We need to find a VM folder for our VM. Get a list of vm folders from the system and
capture the ID (the “folder” field) of one of them.

curl -k -X GET -H 'Accept: application/json' \
-H "vmware-api-session-id: $SESSION_ID" \
$VCENTER/rest/vcenter/folder?filter.type=VIRTUAL_MACHINE | python -m json.tool

Store the folder id within an environment variable. Example:

VMFOLDER="group-v3"

Get a list of hosts

Get a list of hosts from the system and capture the ID (the “host” field) of one of them.

curl -k -X GET -H 'Accept: application/json' \
-H "vmware-api-session-id: $SESSION_ID" \
$VCENTER/rest/vcenter/host | python -m json.tool

Store the host id within an environment variable. Example:

VCHOST="host-9"

Create a test VM

Now we’ll create a VM. We’ll deploy to the host/folder gathered previously and we’ll use the datastore
we used for our content library for its disk backing.

(curl -k -X POST \
-H 'Accept: application/json' -H "Content-Type: application/json" -H "vmware-api-session-id: $SESSION_ID" \
-d @- \
$VCENTER/rest/vcenter/vm \
<<- JSON
{
    "spec": {
        "name": "testVM",
        "guest_OS": "OTHER_LINUX_64",
        "placement": {
            "host": "$VCHOST",
            "datastore": "$DATASTORE0",
            "folder":"$VMFOLDER"
        }
    }
}
JSON
) | python -m json.tool

Store the VM id within an environment variable. Example:

VM="vm-59"

Mount the iso from the library on the VM

Mounting the iso from the content library will create a new cdrom device on the VM which is backed
by the library item. The call will return the id of the newly created cdrom.

(curl -k -X POST \
-H 'Accept: application/json' -H "Content-Type: application/json" -H "vmware-api-session-id: $SESSION_ID" \
-d @- \
$VCENTER/rest/com/vmware/vcenter/iso/image/id:$LIBRARY_ITEM?~action=mount \
<<- JSON
{
    "vm": "$VM"
}
JSON
) | python -m json.tool

Capture the id of the cdrom within an environment variable. Example:

VMCDROM=3000

Power on the VM

Power on the VM and then verify from the console that it has booted the iso image.

curl -k -X POST -H 'Accept: application/json' \
-H "vmware-api-session-id: $SESSION_ID" \
-w "%{http_code}\n" \
$VCENTER/rest/vcenter/vm/$VM/power/start

Power off the VM

Optionally power off the VM if you’re finished with it.

curl -k -X POST -H 'Accept: application/json' \
-H "vmware-api-session-id: $SESSION_ID" \
-w "%{http_code}\n" \
$VCENTER/rest/vcenter/vm/$VM/power/stop

Unmount the cdrom

curl -k -X POST \
-H 'Accept: application/json' -H "Content-Type: application/json" -H "vmware-api-session-id: $SESSION_ID" \
-w "%{http_code}\n" \
-d @- \
$VCENTER/rest/com/vmware/vcenter/iso/image/id:$VM?~action=unmount \
<<- JSON
{
    "cdrom": "$VMCDROM"
}
JSON

Final Clean Up

Delete the VM

curl -k -X DELETE -H 'Accept: application/json' \
-H "vmware-api-session-id: $SESSION_ID" \
-w "%{http_code}\n" \
$VCENTER/rest/vcenter/vm/$VM

Delete the library item

curl -k -X DELETE -H 'Accept: application/json' \
-H "vmware-api-session-id: $SESSION_ID" \
-w "%{http_code}\n" \
$VCENTER/rest/com/vmware/content/library/item/id:$LIBRARY_ITEM

Delete the library

curl -k -X DELETE -H 'Accept: application/json' \
-H "vmware-api-session-id: $SESSION_ID" \
-w "%{http_code}\n" \
$VCENTER/rest/com/vmware/content/local-library/id:$LIBRARY