Merge ~utlemming/cloud-init/+git/cloud-init:digitalocean_networking into cloud-init:master
| Status: | Merged |
|---|---|
| Merged at revision: | 9f83bb8e80806d3dd79ba426474dc3c696e19a41 |
| Proposed branch: | ~utlemming/cloud-init/+git/cloud-init:digitalocean_networking |
| Merge into: | cloud-init:master |
| Diff against target: |
625 lines (+485/-34) 3 files modified
cloudinit/sources/DataSourceDigitalOcean.py (+44/-26) cloudinit/sources/helpers/digitalocean.py (+187/-0) tests/unittests/test_datasource/test_digitalocean.py (+254/-8) |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Scott Moser | 2016-08-19 | Needs Information on 2016-09-28 | |
|
Review via email:
|
|||
Description of the Change
On DigitalOcean, the meta-data provides network configuration on a link-local address (ip4LL). This change makes uses Cloud-init's ability to setup netwokrking from data provided in the meta-data.
Rather than using avahi-autoipd (and thus adding an additional depedency on Cloud-init, a random link-local address is chosen. DigitalOcean's networking guarantees that the entire 169.254.0.0/16 net block is private to the fabric and the droplet. Therefore, any arbitrary address in 169.254.0.0/16 can be used (with the notable exception of 169.254.269.254). That said, the datasource will select a random address between 169.254.
Included in this merge proposal is an updated test case to use actual meta-data from a droplet.
| Scott Moser (smoser) wrote : | # |
fyi, you can push to
git+ssh://<email address hidden>
then its referencable via shorter url of
https:/
rather than
https:/
see Repository Urls at
https:/
for more information.
| Scott Moser (smoser) wrote : | # |
fixing previous comment
fyi, you can push to
git+ssh://<email address hidden>
then its referencable via shorter url of
https:/
compared to
https:/
The first is the "default" repository for cloud-init for you. You can have multiples under the +git/.
see Repository Urls at
https:/
for more information.
using the shorter form allows me to do something like:
for user in lpuser1 lpuser2 utlemming; do
git remote add $user git://git.
git fetch $user
done
- 739d113... by Ben Howard on 2016-08-30
| Ben Howard (utlemming) wrote : | # |
Thanks Scott for the review. I just got back from vacation. I've fixed the nit picks and will look at getting the Unit tests done shortly.
In the meantime, I've updated my branch.
- 4d78b9f... by Ben Howard on 2016-08-31
| Ben Howard (utlemming) wrote : | # |
Added test cases for the networking configuration.
| Ben Howard (utlemming) wrote : | # |
Also, I've push the branch to:
git+ssh://<email address hidden>
I figured I would leave this MP open rather than creating a new one so that the conversation wasn't lost.
| Scott Moser (smoser) wrote : | # |
Some changes i made at http://
I've pasted the comments http://
Over all, this is good, and thank you.
Comments below, where i put 'FIX' i'd like you to make sure that things are as you'd expect
and then a 'TEST' comment, please test that.
- DataSourceDigit
- explicitly populate the self.metadata from keys in the full
digital ocean metadata rather than assuming that values are there, and
not having a explicit list of what we expect. FIX: there may be other
values that were implied by copying the metadata to .metadata, please
check that.
this also allowed me to drop get_public_ssh_keys and availability
zone methods.
- changed
dns = self.metadata.
nameservers = dns.get(
to
nameservers = self.metadata_
that would fail badly if metadata might not have a 'dns' entry,
but really no worse as before, since if there was None in 'dns' you'd
get a TypeError on None.get(
- put the reading of the metadata into a function in do_helpers.
the value of doing this is that mocking the DataSource.get() is
much easier as you can just mock read_metadata() and have it return
what you expect.
FIX: change your tests to just use mock and mock that rather than
using httpretty.
- fixed a logexc usage, improved another one with more useful information.
- dropped use of 'util.which("ip")' in one case, rather just let
execution find that.
- renamed do_helper.
convert_
- TEST:
You can easily add tests of the network configuration conversion
that do not need the datasource infrastructure in place. Its good to
have the test of the datasource but you can test more network config
parsing directly by just calling it with digital ocean values and
making expectations on the response.
See my example TestNetworkConvert and how its easier than dealing with
the datasource.
- de8b146... by Ben Howard on 2016-09-13
- 528541a... by Ben Howard on 2016-09-13
- a9d8221... by Ben Howard on 2016-09-14
- f274cfc... by Ben Howard on 2016-09-14
| Ben Howard (utlemming) wrote : | # |
okay, I think that the issues are addressed. I added a test case to explicitly test the JSON that is returned from the metadata service; the logic being that using actual JSON gives a better, real world test. I can change it be explicit, but would rather be testing against real data, not mocked up data.
This code is all tested as well.
| Scott Moser (smoser) wrote : | # |
I'm going to merge this now. But for your reference, the changes I've made are below.
They generally include:
- move '_get_sysinfo' implementation into do_helper.
for easier patching
- some modifications to tests
- remove httppretty completely
- drop your test 'TestDataSource
what I was asking. I just wanted to actually use a response from
the metadata service.
- use of .copy() on DO_META so it didn't inadvertantly get updated.
- drop no longer used 'apply_filter' on get_data()
- drop 'launch_index' as that is inherited from DataSource.
- flake8 related cleanups.
- assertEqual rather than assertEquals
- add negative test (test_returns_
- log critical *and* raise exception if DigitalOcean is found as
system manufacture and droplet_id is not found.
- some LOG usage cleanups (use LOG.debug("msg %s", key) rather than
LOG.debug("msg {}".format(key)) as the ladder requires rendering/
formatting to string of 'key'. and is also harder to make work
with long messages.
diff --git a/cloudinit/
index 95a939f..c5770d5 100644
--- a/cloudinit/
+++ b/cloudinit/
@@ -54,38 +54,17 @@ class DataSourceDigit
def _get_sysinfo(self):
- # DigitalOcean embeds vendor ID and instance/droplet_id in the
- # SMBIOS information
+ return do_helper.
- LOG.debug("checking if instance is a DigitalOcean droplet")
-
- # Detect if we are on DigitalOcean and return the Droplet's ID
- vendor_name = util.read_
- if vendor_name != "DigitalOcean":
- return (False, None)
-
- LOG.info("running on DigitalOcean")
-
- droplet_id = util.read_
- if droplet_id:
- LOG.debug(("system identified via SMBIOS as DigitalOcean Droplet"
- "{}").format(
- else:
- LOG.critical(
- "Droplet, but did not provide an ID. Please file a "
- "support ticket at: "
- "https:/
- "new"))
-
- return (True, droplet_id)
-
- def get_data(self, apply_filter=
+ def get_data(self):
(is_do, droplet_id) = self._get_sysinfo()
# only proceed if we know we are on DigitalOcean
if not is_do:
return False
+ LOG.info("Running on digital ocean. droplet_id=%s" % droplet_id)
+
ipv4LL_nic = None
if self.use_ip4LL:
@@ -108,10 +87,6 @@ class DataSourceDigit
return True
- @property
- def launch_index(self):
- return None
-
def check_instance_
return sources.
| Scott Moser (smoser) wrote : | # |
Wow.
More work... further reading of this.
I have a branch i'm happy with, and proposed merging into this one...
https:/
the one question i have is in NIC_MAP.
the network data we get from digital ocean is like:
{
'public': [{'mac': '04:01...', ...}],
'private': ['mac': '05:01...', ...]],
}
the top level is a dictionary, but the second is a list.
Currently you assign the name by a map.
What if there are 2 entries in 'public'?
At this point they'd both get 'eth0' as their name.
| Ben Howard (utlemming) wrote : | # |
> the one question i have is in NIC_MAP.
> the network data we get from digital ocean is like:
> {
> 'public': [{'mac': '04:01...', ...}],
> 'private': ['mac': '05:01...', ...]],
> }
>
> the top level is a dictionary, but the second is a list.
> Currently you assign the name by a map.
> What if there are 2 entries in 'public'?
I can certify that there won't be, and if there are, then this is a _huge_ bug. We have notated that changes to the meta-data should be checked against Cloud-init first.
That said, I've double checked and I'm seeing (from actual droplets, i.e. 'curl http://
"interfaces": {
"public": [
{
"ipv4": {
},
},
"mac": "1e:9a:
"type": "public"
}
]
},
And
"interfaces": {
"private": [
{
"ipv4": {
},
"mac": "96:b5:
"type": "private"
}
],
"public": [
{
"ipv4": {
},
"ipv6": {
"cidr": 64,
},
},
"mac": "5a:b1:
"type": "public"
}
]
},
| Scott Moser (smoser) wrote : | # |
Yeah. Just odd that it is a list if there can not be more than one entry.


Over all, this is really nice. Its mostly exactly what i would like to have network datasources doing.
General flow in the 'get' for your datasource then looks like:
a.) quickly check (ie, via dmi info) if you are on the cloud. if not rreturn
b.) you are in all likely hood on your cloud, so you this datasource is in charge
c.) configure local networking transiently as you did
d.) read network metadata service
e.) un-do your network changes
there are some nits in line below, and please make sure that in the local datasource 'get', you're un-configuring your ipv4 link local addresses.
Also, need some unit tests. the unit tests can focus on parse_network_ configuration as that is the brunt of the work.