Merge ~sankaraditya/cloud-init:topic-stanguturi-vmware-curtin-changes into cloud-init:master
- Git
- lp:~sankaraditya/cloud-init
- topic-stanguturi-vmware-curtin-changes
- Merge into master
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Scott Moser | ||||
Approved revision: | f0b2b7cba92cd940ad3931a487087abb4126b019 | ||||
Merged at revision: | a1dfdda2a2ae20fe026881980ddf7d16110f06e2 | ||||
Proposed branch: | ~sankaraditya/cloud-init:topic-stanguturi-vmware-curtin-changes | ||||
Merge into: | cloud-init:master | ||||
Diff against target: |
808 lines (+418/-103) 4 files modified
cloudinit/sources/DataSourceOVF.py (+64/-27) cloudinit/sources/helpers/vmware/imc/config_nic.py (+130/-71) cloudinit/sources/helpers/vmware/imc/guestcust_util.py (+7/-5) tests/unittests/test_vmware_config_file.py (+217/-0) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Scott Moser | Approve | ||
Server Team CI bot | continuous-integration | Approve | |
Chad Smith | Approve | ||
Review via email:
|
Commit message
Description of the change
vmware customization: return network config format
For customizing the machines hosted on 'VMWare' hypervisor, the datasource
should return the 'network config' data in 'curtin' format.
Modify the code to read the customization configuration and return the converted data.
Added few tests.
LP: #1675063
- cab90ca... by Sankar Tanguturi
-
Fix the positioning of 'route' element.
As per the documentation available at
http://curtin. readthedocs. io/en/latest/ topics/ networking. html,
'route' is not a child element of 'subnets'. It is a peer element of
'nameserver' / 'physical' elements.Modified the code to fix the 'route' elements.
Updated the tests. - 6f4a2e0... by Sankar Tanguturi
-
Populate the 'destination' key to the 'route' elements.
Added the code to populate 'destination' key in the route elements.
Added the new tests for the new function.Ignoring the routes for static IPV6 for now.
Fixed the unit tests accordingly. - 66391c3... by Sankar Tanguturi
-
Merge branch 'master' into topic-stangutur
i-vmware- curtin

Scott Moser (smoser) wrote : | # |
- 394a132... by Sankar Tanguturi
-
Addressed review comments from Scott Moser.
- Added few private variables to use in the future when
the NICS have to be enabled when the customization gets completed.
- Re-factored the code. Implemented few wrapper functions to
make the code more readable and make testing easier.
- Modified the unit test code accordingly.
- Remove the code to clear dhcp. Now that the datasource is just
returning the network configuration, cloud-init / curtin should
take care of all dhcp settings.

Sankar Tanguturi (sankaraditya) wrote : | # |
> a.) I think I'd like to move the conversion of the network config out of the
> 'get'.
> The LOG.debug "Applying the network customization" is actually incorrect in
> that
> no changes are done there. Lets move that to happen in the
> 'network_config' function.
>
> if self._network_
> return self._network_
>
> ## convert the network config from the data in 'self' here to to the
> desired
> ## 'network_config' format.
> nc = NicConcifugrato
> cfg = get_vmware_
> self._network_
> return self.network_config
Thanks for the comments. I tried modifying the code as per your suggestion and found an issue. When cloud-init runs in local mode initially, the datasourceOVF returned the data and then cloud-init saved the instance some where. During this stage, _network_config is saved as False. And then network-config property is called. The local mode finished. When the cloud-init in 'network' mode started, it loaded the saved instance. Since the _network_config is not saved, whenever this property is retrieved, the same code is executed again. This is not quite right for our use case.
Any suggestion to fix this?

Scott Moser (smoser) wrote : | # |
Why was the network information not available in 'local' mode?
You'll have to show the code that you tried with, because nothing in your current tree here will set _network_config to false.
You're right that we have to get the network config in local mode otherwise it wont be applied early enough.

Scott Moser (smoser) wrote : | # |
I'd also recommend storing the contents of vmwareImcConfig
self._vmware_
and then when you make a 'ConfigFile' object pass those contents in (you'll have ot modify it to take a string contents instead of having ConfigFile take a filename.

Sankar Tanguturi (sankaraditya) wrote : | # |
> Why was the network information not available in 'local' mode?
>
> You'll have to show the code that you tried with, because nothing in your
> current tree here will set _network_config to false.
>
> You're right that we have to get the network config in local mode otherwise it
> wont be applied early enough.
Sorry if my previous comment was not clear. (_network_config was set to None and not False). Let me explain:
1. When the cloud-init initially runs in 'local' mode, first getdata is called and the necessary settings are retrieved. Now that we modified the code to retrieve the network code to 'network_config', getdata will not return the network data. At that point, cloud-init saves the datasource instance and it will have self._network_
2. After few seconds, cloud-init runs in 'network' mode. This time, the saved instance is retrieved and since the self._network_
So, to avoid this, I think, self._network_
Thanks
Sankar.
- 4bb42b4... by Sankar Tanguturi
-
Added the code to wait for nics.txt file.
NICS.txt is another file that is populated by 'VMware Tools'
daemon which contains the information related to NICS. Added the
code to wait for nics.txt file.

Sankar Tanguturi (sankaraditya) wrote : | # |
Addressed few review comments. Updated the code and also the tests.
In the getData() function itself, we want to parse the network configuration and return an error if parsing fails. For that reason, I didn't move the 'network config' part from getData() to network_config @property method.
Thanks

Scott Moser (smoser) wrote : | # |
Can you open a bug for this? Just so I have something to track.
https:/
I think my request for get_network_config does not make any sense any more.
The way you've done it now, there is basically no value in that function. No one calls it directly, so just make it part of 'get_network_
Some other comments in line, nothing huge.
Good work Sankar,
Scott

Sankar Tanguturi (sankaraditya) wrote : | # |
> Can you open a bug for this? Just so I have something to track.
> https:/
Scott,
Thanks for the valuable comments. For what part, you are asking me to file a bug?

Sankar Tanguturi (sankaraditya) wrote : | # |
Will fix as per the review comments and update the diff. I have only one question about the 'activate' method. I mentioned below.
Thanks
Sankar.
- 2f2d810... by Sankar Tanguturi
-
Fixed some nits suggested by Scott Moser.

Scott Moser (smoser) wrote : | # |
Sankar, I went ahead and opened a bug. I just wanted something to track this larger set of changes.
One other request in this Merge. We should now be able to drop NicConfigurator and any tests that exist for it as this will all be getting rendered with cloud-init's generic renderers.

Sankar Tanguturi (sankaraditya) wrote : | # |
> One other request in this Merge. We should now be able to drop
> NicConfigurator and any tests that exist for it as this will all be getting
> rendered with cloud-init's generic renderers.
I don't think we can drop the NicConfigurator. It the the class that generates the necessary network config in curtin format. It doesn't not actually configure the nics but just reads the mac addresses of the system and returns proper network data.

Sankar Tanguturi (sankaraditya) wrote : | # |
Working on some small nits. Will update the diff.

Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:4bb42b4f22c
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
- 8e3bb47... by Sankar Tanguturi
-
Merge branch 'master' into topic-stangutur
i-vmware- curtin

Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:8e3bb474186
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/

Scott Moser (smoser) wrote : | # |
one small comment. this was as far as I got just yet.

Chad Smith (chad.smith) wrote : | # |
Looks really good. Long term I would like to see us hoist some of the ip-command parsing utilities up into cloudinit.
Thanks again for this work. let's wrap up these branches early next week if we can. Sorry for the delay.

Sankar Tanguturi (sankaraditya) wrote : | # |
Thanks Chad and Scott for your valuable inputs.
Will address all review comments and will update the diff.
- 4301c29... by Sankar Tanguturi
-
Addressed few review comments from Chad Smith and Scott Moser.
- Added a docstring to each test function.
- Removed few unnecessary log messages.
- Removed 'enable_nics' in a specific code path where it is really not
required.
- Fixed a typo in a comment.
- Modified wait_for_imc_cfg_ file to take dirpath as optional argument
and look into /var/run/vmware-imc by default.

Sankar Tanguturi (sankaraditya) wrote : | # |
Updating the diff after addressing review comments from Chat and SCott. Thanks.

Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:4301c299aaf
https:/
Executed test runs:
SUCCESS: Checkout
FAILED: Unit & Style Tests
Click here to trigger a rebuild:
https:/
- 13e4e08... by Sankar Tanguturi
-
Merge branch 'master' into current topic branch.
Resolved Conflicts:
cloudinit/sources/ DataSourceOVF. py
tests/unittests/ test_vmware_ config_ file.py - 39d7aae... by Sankar Tanguturi
-
Fixing few issues after the recent merge with master.
- Use mask_to_net_prefix instead of mask2cidr
- Fixing few issues reported by flake / tox
- Modified the code to enable NICS only in successful case.
In the case of failure, no request will be sent to
the underlying hypervisor to enable the NICS.
- Minor code refactoring.

Chad Smith (chad.smith) wrote : | # |
Thanks for the comment updates and questions answered Sankar!
Looks like this branch only needs a git rebase to fixup the following conflicts:
Conflict in cloudinit/
Conflict in tests/unittests
# from your branch: you probably already know this, it was kindof new to me
git pull origin/master
git rebase origin/master
# iterate through any conflicts, edit conflicts git add <conflict_files>; git rebase --continue

Sankar Tanguturi (sankaraditya) wrote : | # |
Resolved conflicts and updated the diff. Thanks.

Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:39d7aaed101
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
SUCCESS: MAAS Compatability Testing
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
- a3d130d... by Sankar Tanguturi
-
Add 'source interfaces.d/*.cfg' line to /etc/network/
interfaces Modified the code to configure the /etc/network/
interfaces file
for debian guests. The 'configure' operation takes a backup of
the file and writes 'source /etc/network/interfaces. d/*.cfg' line so that
the cloud-init rendered config in /etc/network/interfaces. d/...cfg
file will be used.Fixed the wait_for_
imc_cfg_ file function. Used os.path.isfile()

Sankar Tanguturi (sankaraditya) wrote : | # |
As discussed with Chad Smith today during 'cloud-init' meeting, updated the diff to add 'source /etc/network/
Thanks Chad.

Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:a3d130dc31e
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
SUCCESS: MAAS Compatability Testing
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
- 891e2d3... by Sankar Tanguturi
-
Generate a unique instance id for the ds so that re-customization
will happen in cloud-init.

Sankar Tanguturi (sankaraditya) wrote : | # |
In VMware guest customization workflow, we should be able to do re-customization. After discussing with Scott and Chad, modified the code to generate a unique id when there is a customization. This will force the cloud-init to do the re-customization.
Thanks Scott and Chad.

Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:891e2d38d3e
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
SUCCESS: MAAS Compatability Testing
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/

Chad Smith (chad.smith) : | # |

Chad Smith (chad.smith) wrote : | # |
Good update on random instance-id Sankar, if you can instrument or add to an existing unit test that exposes the instance-id update +1 on this branch.
- e0a6eb6... by Sankar Tanguturi
-
Adding a new unit test case for the instance-id for DataSourceOVF
Fixed the typo in config_nic.py
Added a new unit test case for DataSourceOVF instance-id
Fixed the docstring for the functions comments in config_nic.py

Sankar Tanguturi (sankaraditya) wrote : | # |
Addressed all review comments from Chad and updated the diff.

Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:e0a6eb60c18
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
SUCCESS: MAAS Compatability Testing
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/

Chad Smith (chad.smith) wrote : | # |
Thanks Sankar for the fixes.
Since, as you mentioned, the python configuration engine overwrites /etc/network/
# DO NOT EDIT THIS FILE BY HAND -- AUTOMATICALLY GENERATED BY cloud-init.
Thanks for the additional unit test on randomized instance-id. etc.
Just some minor nits on the unit tests.
Wherever possible please don't use assertTrue( some conditional) because unit test errors in those cases are not helpful, they only say "AssertionError: False is not true". But if you use assertEqual() or assertIn you get slightly more informative test errors:
AssertionError: 'iid-vmware-' not found in 'some-random-
I approve after your unittest fixups are there
- f0b2b7c... by Sankar Tanguturi
-
Fix test_vmware_
config_ file test script. Replaced self.assertTrue with self.assertIn and self.assertEqual
at few places in the code.

Sankar Tanguturi (sankaraditya) wrote : | # |
Fixed the new unit test case as Chad suggested. Thanks.

Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:f0b2b7cba92
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
FAILED: MAAS Compatability Testing
Click here to trigger a rebuild:
https:/

Chad Smith (chad.smith) wrote : | # |
LGTM; +1 and thanks.

Sankar Tanguturi (sankaraditya) wrote : | # |
> LGTM; +1 and thanks.
Thanks Chad.

Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:f0b2b7cba92
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
FAILED: Ubuntu LTS: Build
Click here to trigger a rebuild:
https:/

Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:f0b2b7cba92
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
SUCCESS: MAAS Compatability Testing
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/

Scott Moser (smoser) wrote : | # |
approving based on Chad's review and talking with Sankar.
Preview Diff
1 | diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py |
2 | index 73d3877..aa5f798 100644 |
3 | --- a/cloudinit/sources/DataSourceOVF.py |
4 | +++ b/cloudinit/sources/DataSourceOVF.py |
5 | @@ -51,6 +51,10 @@ class DataSourceOVF(sources.DataSource): |
6 | self.cfg = {} |
7 | self.supported_seed_starts = ("/", "file://") |
8 | self.vmware_customization_supported = True |
9 | + self._network_config = None |
10 | + self._vmware_nics_to_enable = None |
11 | + self._vmware_cust_conf = None |
12 | + self._vmware_cust_found = False |
13 | |
14 | def __str__(self): |
15 | root = sources.DataSource.__str__(self) |
16 | @@ -60,8 +64,8 @@ class DataSourceOVF(sources.DataSource): |
17 | found = [] |
18 | md = {} |
19 | ud = "" |
20 | - vmwarePlatformFound = False |
21 | - vmwareImcConfigFilePath = '' |
22 | + vmwareImcConfigFilePath = None |
23 | + nicspath = None |
24 | |
25 | defaults = { |
26 | "instance-id": "iid-dsovf", |
27 | @@ -101,25 +105,26 @@ class DataSourceOVF(sources.DataSource): |
28 | logfunc=LOG.debug, |
29 | msg="waiting for configuration file", |
30 | func=wait_for_imc_cfg_file, |
31 | - args=("/var/run/vmware-imc", "cust.cfg", max_wait)) |
32 | + args=("cust.cfg", max_wait)) |
33 | |
34 | if vmwareImcConfigFilePath: |
35 | LOG.debug("Found VMware Customization Config File at %s", |
36 | vmwareImcConfigFilePath) |
37 | + nicspath = wait_for_imc_cfg_file( |
38 | + filename="nics.txt", maxwait=10, naplen=5) |
39 | else: |
40 | LOG.debug("Did not find VMware Customization Config File") |
41 | else: |
42 | LOG.debug("Customization for VMware platform is disabled.") |
43 | |
44 | if vmwareImcConfigFilePath: |
45 | - nics = "" |
46 | + self._vmware_nics_to_enable = "" |
47 | try: |
48 | cf = ConfigFile(vmwareImcConfigFilePath) |
49 | - conf = Config(cf) |
50 | - (md, ud, cfg) = read_vmware_imc(conf) |
51 | - dirpath = os.path.dirname(vmwareImcConfigFilePath) |
52 | - nics = get_nics_to_enable(dirpath) |
53 | - markerid = conf.marker_id |
54 | + self._vmware_cust_conf = Config(cf) |
55 | + (md, ud, cfg) = read_vmware_imc(self._vmware_cust_conf) |
56 | + self._vmware_nics_to_enable = get_nics_to_enable(nicspath) |
57 | + markerid = self._vmware_cust_conf.marker_id |
58 | markerexists = check_marker_exists(markerid) |
59 | except Exception as e: |
60 | LOG.debug("Error parsing the customization Config File") |
61 | @@ -127,28 +132,29 @@ class DataSourceOVF(sources.DataSource): |
62 | set_customization_status( |
63 | GuestCustStateEnum.GUESTCUST_STATE_RUNNING, |
64 | GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED) |
65 | - enable_nics(nics) |
66 | - return False |
67 | + raise e |
68 | finally: |
69 | util.del_dir(os.path.dirname(vmwareImcConfigFilePath)) |
70 | try: |
71 | - LOG.debug("Applying the Network customization") |
72 | - nicConfigurator = NicConfigurator(conf.nics) |
73 | - nicConfigurator.configure() |
74 | + LOG.debug("Preparing the Network configuration") |
75 | + self._network_config = get_network_config_from_conf( |
76 | + self._vmware_cust_conf, |
77 | + True, |
78 | + True, |
79 | + self.distro.osfamily) |
80 | except Exception as e: |
81 | - LOG.debug("Error applying the Network Configuration") |
82 | LOG.exception(e) |
83 | set_customization_status( |
84 | GuestCustStateEnum.GUESTCUST_STATE_RUNNING, |
85 | GuestCustEventEnum.GUESTCUST_EVENT_NETWORK_SETUP_FAILED) |
86 | - enable_nics(nics) |
87 | - return False |
88 | + raise e |
89 | + |
90 | if markerid and not markerexists: |
91 | LOG.debug("Applying password customization") |
92 | pwdConfigurator = PasswordConfigurator() |
93 | - adminpwd = conf.admin_password |
94 | + adminpwd = self._vmware_cust_conf.admin_password |
95 | try: |
96 | - resetpwd = conf.reset_password |
97 | + resetpwd = self._vmware_cust_conf.reset_password |
98 | if adminpwd or resetpwd: |
99 | pwdConfigurator.configure(adminpwd, resetpwd, |
100 | self.distro) |
101 | @@ -159,7 +165,6 @@ class DataSourceOVF(sources.DataSource): |
102 | set_customization_status( |
103 | GuestCustStateEnum.GUESTCUST_STATE_RUNNING, |
104 | GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED) |
105 | - enable_nics(nics) |
106 | return False |
107 | if markerid: |
108 | LOG.debug("Handle marker creation") |
109 | @@ -170,14 +175,18 @@ class DataSourceOVF(sources.DataSource): |
110 | set_customization_status( |
111 | GuestCustStateEnum.GUESTCUST_STATE_RUNNING, |
112 | GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED) |
113 | - enable_nics(nics) |
114 | return False |
115 | |
116 | - vmwarePlatformFound = True |
117 | + self._vmware_cust_found = True |
118 | + found.append('vmware-tools') |
119 | + |
120 | + # TODO: Need to set the status to DONE only when the |
121 | + # customization is done successfully. |
122 | set_customization_status( |
123 | GuestCustStateEnum.GUESTCUST_STATE_DONE, |
124 | GuestCustErrorEnum.GUESTCUST_ERROR_SUCCESS) |
125 | - enable_nics(nics) |
126 | + enable_nics(self._vmware_nics_to_enable) |
127 | + |
128 | else: |
129 | np = {'iso': transport_iso9660, |
130 | 'vmware-guestd': transport_vmware_guestd, } |
131 | @@ -192,7 +201,7 @@ class DataSourceOVF(sources.DataSource): |
132 | found.append(name) |
133 | |
134 | # There was no OVF transports found |
135 | - if len(found) == 0 and not vmwarePlatformFound: |
136 | + if len(found) == 0: |
137 | return False |
138 | |
139 | if 'seedfrom' in md and md['seedfrom']: |
140 | @@ -237,6 +246,10 @@ class DataSourceOVF(sources.DataSource): |
141 | def get_config_obj(self): |
142 | return self.cfg |
143 | |
144 | + @property |
145 | + def network_config(self): |
146 | + return self._network_config |
147 | + |
148 | |
149 | class DataSourceOVFNet(DataSourceOVF): |
150 | def __init__(self, sys_cfg, distro, paths): |
151 | @@ -268,12 +281,13 @@ def get_max_wait_from_cfg(cfg): |
152 | return max_wait |
153 | |
154 | |
155 | -def wait_for_imc_cfg_file(dirpath, filename, maxwait=180, naplen=5): |
156 | +def wait_for_imc_cfg_file(filename, maxwait=180, naplen=5, |
157 | + dirpath="/var/run/vmware-imc"): |
158 | waited = 0 |
159 | |
160 | while waited < maxwait: |
161 | - fileFullPath = search_file(dirpath, filename) |
162 | - if fileFullPath: |
163 | + fileFullPath = os.path.join(dirpath, filename) |
164 | + if os.path.isfile(fileFullPath): |
165 | return fileFullPath |
166 | LOG.debug("Waiting for VMware Customization Config File") |
167 | time.sleep(naplen) |
168 | @@ -281,6 +295,26 @@ def wait_for_imc_cfg_file(dirpath, filename, maxwait=180, naplen=5): |
169 | return None |
170 | |
171 | |
172 | +def get_network_config_from_conf(config, use_system_devices=True, |
173 | + configure=False, osfamily=None): |
174 | + nicConfigurator = NicConfigurator(config.nics, use_system_devices) |
175 | + nics_cfg_list = nicConfigurator.generate(configure, osfamily) |
176 | + |
177 | + return get_network_config(nics_cfg_list, |
178 | + config.name_servers, |
179 | + config.dns_suffixes) |
180 | + |
181 | + |
182 | +def get_network_config(nics=None, nameservers=None, search=None): |
183 | + config_list = nics |
184 | + |
185 | + if nameservers or search: |
186 | + config_list.append({'type': 'nameserver', 'address': nameservers, |
187 | + 'search': search}) |
188 | + |
189 | + return {'version': 1, 'config': config_list} |
190 | + |
191 | + |
192 | # This will return a dict with some content |
193 | # meta-data, user-data, some config |
194 | def read_vmware_imc(config): |
195 | @@ -296,6 +330,9 @@ def read_vmware_imc(config): |
196 | if config.timezone: |
197 | cfg['timezone'] = config.timezone |
198 | |
199 | + # Generate a unique instance-id so that re-customization will |
200 | + # happen in cloud-init |
201 | + md['instance-id'] = "iid-vmware-" + util.rand_str(strlen=8) |
202 | return (md, ud, cfg) |
203 | |
204 | |
205 | diff --git a/cloudinit/sources/helpers/vmware/imc/config_nic.py b/cloudinit/sources/helpers/vmware/imc/config_nic.py |
206 | index 67ac21d..2fb07c5 100644 |
207 | --- a/cloudinit/sources/helpers/vmware/imc/config_nic.py |
208 | +++ b/cloudinit/sources/helpers/vmware/imc/config_nic.py |
209 | @@ -9,22 +9,48 @@ import logging |
210 | import os |
211 | import re |
212 | |
213 | +from cloudinit.net.network_state import mask_to_net_prefix |
214 | from cloudinit import util |
215 | |
216 | logger = logging.getLogger(__name__) |
217 | |
218 | |
219 | +def gen_subnet(ip, netmask): |
220 | + """ |
221 | + Return the subnet for a given ip address and a netmask |
222 | + @return (str): the subnet |
223 | + @param ip: ip address |
224 | + @param netmask: netmask |
225 | + """ |
226 | + ip_array = ip.split(".") |
227 | + mask_array = netmask.split(".") |
228 | + result = [] |
229 | + for index in list(range(4)): |
230 | + result.append(int(ip_array[index]) & int(mask_array[index])) |
231 | + |
232 | + return ".".join([str(x) for x in result]) |
233 | + |
234 | + |
235 | class NicConfigurator(object): |
236 | - def __init__(self, nics): |
237 | + def __init__(self, nics, use_system_devices=True): |
238 | """ |
239 | Initialize the Nic Configurator |
240 | @param nics (list) an array of nics to configure |
241 | + @param use_system_devices (Bool) Get the MAC names from the system |
242 | + if this is True. If False, then mac names will be retrieved from |
243 | + the specified nics. |
244 | """ |
245 | self.nics = nics |
246 | self.mac2Name = {} |
247 | self.ipv4PrimaryGateway = None |
248 | self.ipv6PrimaryGateway = None |
249 | - self.find_devices() |
250 | + |
251 | + if use_system_devices: |
252 | + self.find_devices() |
253 | + else: |
254 | + for nic in self.nics: |
255 | + self.mac2Name[nic.mac.lower()] = nic.name |
256 | + |
257 | self._primaryNic = self.get_primary_nic() |
258 | |
259 | def get_primary_nic(self): |
260 | @@ -61,138 +87,163 @@ class NicConfigurator(object): |
261 | |
262 | def gen_one_nic(self, nic): |
263 | """ |
264 | - Return the lines needed to configure a nic |
265 | - @return (str list): the string list to configure the nic |
266 | + Return the config list needed to configure a nic |
267 | + @return (list): the subnets and routes list to configure the nic |
268 | @param nic (NicBase): the nic to configure |
269 | """ |
270 | - lines = [] |
271 | - name = self.mac2Name.get(nic.mac.lower()) |
272 | + mac = nic.mac.lower() |
273 | + name = self.mac2Name.get(mac) |
274 | if not name: |
275 | raise ValueError('No known device has MACADDR: %s' % nic.mac) |
276 | |
277 | - if nic.onboot: |
278 | - lines.append('auto %s' % name) |
279 | + nics_cfg_list = [] |
280 | + |
281 | + cfg = {'type': 'physical', 'name': name, 'mac_address': mac} |
282 | + |
283 | + subnet_list = [] |
284 | + route_list = [] |
285 | |
286 | # Customize IPv4 |
287 | - lines.extend(self.gen_ipv4(name, nic)) |
288 | + (subnets, routes) = self.gen_ipv4(name, nic) |
289 | + subnet_list.extend(subnets) |
290 | + route_list.extend(routes) |
291 | |
292 | # Customize IPv6 |
293 | - lines.extend(self.gen_ipv6(name, nic)) |
294 | + (subnets, routes) = self.gen_ipv6(name, nic) |
295 | + subnet_list.extend(subnets) |
296 | + route_list.extend(routes) |
297 | + |
298 | + cfg.update({'subnets': subnet_list}) |
299 | |
300 | - lines.append('') |
301 | + nics_cfg_list.append(cfg) |
302 | + if route_list: |
303 | + nics_cfg_list.extend(route_list) |
304 | |
305 | - return lines |
306 | + return nics_cfg_list |
307 | |
308 | def gen_ipv4(self, name, nic): |
309 | """ |
310 | - Return the lines needed to configure the IPv4 setting of a nic |
311 | - @return (str list): the string list to configure the gateways |
312 | - @param name (str): name of the nic |
313 | + Return the set of subnets and routes needed to configure the |
314 | + IPv4 settings of a nic |
315 | + @return (set): the set of subnet and routes to configure the gateways |
316 | + @param name (str): subnet and route list for the nic |
317 | @param nic (NicBase): the nic to configure |
318 | """ |
319 | - lines = [] |
320 | + |
321 | + subnet = {} |
322 | + route_list = [] |
323 | + |
324 | + if nic.onboot: |
325 | + subnet.update({'control': 'auto'}) |
326 | |
327 | bootproto = nic.bootProto.lower() |
328 | if nic.ipv4_mode.lower() == 'disabled': |
329 | bootproto = 'manual' |
330 | - lines.append('iface %s inet %s' % (name, bootproto)) |
331 | |
332 | if bootproto != 'static': |
333 | - return lines |
334 | + subnet.update({'type': 'dhcp'}) |
335 | + return ([subnet], route_list) |
336 | + else: |
337 | + subnet.update({'type': 'static'}) |
338 | |
339 | # Static Ipv4 |
340 | addrs = nic.staticIpv4 |
341 | if not addrs: |
342 | - return lines |
343 | + return ([subnet], route_list) |
344 | |
345 | v4 = addrs[0] |
346 | if v4.ip: |
347 | - lines.append(' address %s' % v4.ip) |
348 | + subnet.update({'address': v4.ip}) |
349 | if v4.netmask: |
350 | - lines.append(' netmask %s' % v4.netmask) |
351 | + subnet.update({'netmask': v4.netmask}) |
352 | |
353 | # Add the primary gateway |
354 | if nic.primary and v4.gateways: |
355 | self.ipv4PrimaryGateway = v4.gateways[0] |
356 | - lines.append(' gateway %s metric 0' % self.ipv4PrimaryGateway) |
357 | - return lines |
358 | + subnet.update({'gateway': self.ipv4PrimaryGateway}) |
359 | + return [subnet] |
360 | |
361 | # Add routes if there is no primary nic |
362 | if not self._primaryNic: |
363 | - lines.extend(self.gen_ipv4_route(nic, v4.gateways)) |
364 | + route_list.extend(self.gen_ipv4_route(nic, |
365 | + v4.gateways, |
366 | + v4.netmask)) |
367 | |
368 | - return lines |
369 | + return ([subnet], route_list) |
370 | |
371 | - def gen_ipv4_route(self, nic, gateways): |
372 | + def gen_ipv4_route(self, nic, gateways, netmask): |
373 | """ |
374 | - Return the lines needed to configure additional Ipv4 route |
375 | - @return (str list): the string list to configure the gateways |
376 | + Return the routes list needed to configure additional Ipv4 route |
377 | + @return (list): the route list to configure the gateways |
378 | @param nic (NicBase): the nic to configure |
379 | @param gateways (str list): the list of gateways |
380 | """ |
381 | - lines = [] |
382 | + route_list = [] |
383 | + |
384 | + cidr = mask_to_net_prefix(netmask) |
385 | |
386 | for gateway in gateways: |
387 | - lines.append(' up route add default gw %s metric 10000' % |
388 | - gateway) |
389 | + destination = "%s/%d" % (gen_subnet(gateway, netmask), cidr) |
390 | + route_list.append({'destination': destination, |
391 | + 'type': 'route', |
392 | + 'gateway': gateway, |
393 | + 'metric': 10000}) |
394 | |
395 | - return lines |
396 | + return route_list |
397 | |
398 | def gen_ipv6(self, name, nic): |
399 | """ |
400 | - Return the lines needed to configure the gateways for a nic |
401 | - @return (str list): the string list to configure the gateways |
402 | + Return the set of subnets and routes needed to configure the |
403 | + gateways for a nic |
404 | + @return (set): the set of subnets and routes to configure the gateways |
405 | @param name (str): name of the nic |
406 | @param nic (NicBase): the nic to configure |
407 | """ |
408 | - lines = [] |
409 | |
410 | if not nic.staticIpv6: |
411 | - return lines |
412 | + return ([], []) |
413 | |
414 | + subnet_list = [] |
415 | # Static Ipv6 |
416 | addrs = nic.staticIpv6 |
417 | - lines.append('iface %s inet6 static' % name) |
418 | - lines.append(' address %s' % addrs[0].ip) |
419 | - lines.append(' netmask %s' % addrs[0].netmask) |
420 | |
421 | - for addr in addrs[1:]: |
422 | - lines.append(' up ifconfig %s inet6 add %s/%s' % (name, addr.ip, |
423 | - addr.netmask)) |
424 | - # Add the primary gateway |
425 | - if nic.primary: |
426 | - for addr in addrs: |
427 | - if addr.gateway: |
428 | - self.ipv6PrimaryGateway = addr.gateway |
429 | - lines.append(' gateway %s' % self.ipv6PrimaryGateway) |
430 | - return lines |
431 | + for addr in addrs: |
432 | + subnet = {'type': 'static6', |
433 | + 'address': addr.ip, |
434 | + 'netmask': addr.netmask} |
435 | + subnet_list.append(subnet) |
436 | |
437 | - # Add routes if there is no primary nic |
438 | - if not self._primaryNic: |
439 | - lines.extend(self._genIpv6Route(name, nic, addrs)) |
440 | + # TODO: Add the primary gateway |
441 | + |
442 | + route_list = [] |
443 | + # TODO: Add routes if there is no primary nic |
444 | + # if not self._primaryNic: |
445 | + # route_list.extend(self._genIpv6Route(name, nic, addrs)) |
446 | |
447 | - return lines |
448 | + return (subnet_list, route_list) |
449 | |
450 | def _genIpv6Route(self, name, nic, addrs): |
451 | - lines = [] |
452 | + route_list = [] |
453 | |
454 | for addr in addrs: |
455 | - lines.append(' up route -A inet6 add default gw ' |
456 | - '%s metric 10000' % addr.gateway) |
457 | + route_list.append({'type': 'route', |
458 | + 'gateway': addr.gateway, |
459 | + 'metric': 10000}) |
460 | + |
461 | + return route_list |
462 | |
463 | - return lines |
464 | + def generate(self, configure=False, osfamily=None): |
465 | + """Return the config elements that are needed to configure the nics""" |
466 | + if configure: |
467 | + logger.info("Configuring the interfaces file") |
468 | + self.configure(osfamily) |
469 | |
470 | - def generate(self): |
471 | - """Return the lines that is needed to configure the nics""" |
472 | - lines = [] |
473 | - lines.append('iface lo inet loopback') |
474 | - lines.append('auto lo') |
475 | - lines.append('') |
476 | + nics_cfg_list = [] |
477 | |
478 | for nic in self.nics: |
479 | - lines.extend(self.gen_one_nic(nic)) |
480 | + nics_cfg_list.extend(self.gen_one_nic(nic)) |
481 | |
482 | - return lines |
483 | + return nics_cfg_list |
484 | |
485 | def clear_dhcp(self): |
486 | logger.info('Clearing DHCP leases') |
487 | @@ -201,11 +252,16 @@ class NicConfigurator(object): |
488 | util.subp(["pkill", "dhclient"], rcs=[0, 1]) |
489 | util.subp(["rm", "-f", "/var/lib/dhcp/*"]) |
490 | |
491 | - def configure(self): |
492 | + def configure(self, osfamily=None): |
493 | """ |
494 | - Configure the /etc/network/intefaces |
495 | + Configure the /etc/network/interfaces |
496 | Make a back up of the original |
497 | """ |
498 | + |
499 | + if not osfamily or osfamily != "debian": |
500 | + logger.info("Debian OS not detected. Skipping the configure step") |
501 | + return |
502 | + |
503 | containingDir = '/etc/network' |
504 | |
505 | interfaceFile = os.path.join(containingDir, 'interfaces') |
506 | @@ -215,10 +271,13 @@ class NicConfigurator(object): |
507 | if not os.path.exists(originalFile) and os.path.exists(interfaceFile): |
508 | os.rename(interfaceFile, originalFile) |
509 | |
510 | - lines = self.generate() |
511 | - with open(interfaceFile, 'w') as fp: |
512 | - for line in lines: |
513 | - fp.write('%s\n' % line) |
514 | + lines = [ |
515 | + "# DO NOT EDIT THIS FILE BY HAND --" |
516 | + " AUTOMATICALLY GENERATED BY cloud-init", |
517 | + "source /etc/network/interfaces.d/*.cfg", |
518 | + ] |
519 | + |
520 | + util.write_file(interfaceFile, content='\n'.join(lines)) |
521 | |
522 | self.clear_dhcp() |
523 | |
524 | diff --git a/cloudinit/sources/helpers/vmware/imc/guestcust_util.py b/cloudinit/sources/helpers/vmware/imc/guestcust_util.py |
525 | index 1ab6bd4..4407525 100644 |
526 | --- a/cloudinit/sources/helpers/vmware/imc/guestcust_util.py |
527 | +++ b/cloudinit/sources/helpers/vmware/imc/guestcust_util.py |
528 | @@ -59,14 +59,16 @@ def set_customization_status(custstate, custerror, errormessage=None): |
529 | return (out, err) |
530 | |
531 | |
532 | -# This will read the file nics.txt in the specified directory |
533 | -# and return the content |
534 | -def get_nics_to_enable(dirpath): |
535 | - if not dirpath: |
536 | +def get_nics_to_enable(nicsfilepath): |
537 | + """Reads the NICS from the specified file path and returns the content |
538 | + |
539 | + @param nicsfilepath: Absolute file path to the NICS.txt file. |
540 | + """ |
541 | + |
542 | + if not nicsfilepath: |
543 | return None |
544 | |
545 | NICS_SIZE = 1024 |
546 | - nicsfilepath = os.path.join(dirpath, "nics.txt") |
547 | if not os.path.exists(nicsfilepath): |
548 | return None |
549 | |
550 | diff --git a/tests/unittests/test_vmware_config_file.py b/tests/unittests/test_vmware_config_file.py |
551 | index 03b36d3..2572ee8 100644 |
552 | --- a/tests/unittests/test_vmware_config_file.py |
553 | +++ b/tests/unittests/test_vmware_config_file.py |
554 | @@ -9,9 +9,13 @@ import logging |
555 | import sys |
556 | |
557 | from .helpers import CiTestCase |
558 | +from cloudinit.sources.DataSourceOVF import get_network_config_from_conf |
559 | +from cloudinit.sources.DataSourceOVF import read_vmware_imc |
560 | from cloudinit.sources.helpers.vmware.imc.boot_proto import BootProtoEnum |
561 | from cloudinit.sources.helpers.vmware.imc.config import Config |
562 | from cloudinit.sources.helpers.vmware.imc.config_file import ConfigFile |
563 | +from cloudinit.sources.helpers.vmware.imc.config_nic import gen_subnet |
564 | +from cloudinit.sources.helpers.vmware.imc.config_nic import NicConfigurator |
565 | |
566 | logging.basicConfig(level=logging.DEBUG, stream=sys.stdout) |
567 | logger = logging.getLogger(__name__) |
568 | @@ -20,6 +24,7 @@ logger = logging.getLogger(__name__) |
569 | class TestVmwareConfigFile(CiTestCase): |
570 | |
571 | def test_utility_methods(self): |
572 | + """Tests basic utility methods of ConfigFile class""" |
573 | cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg") |
574 | |
575 | cf.clear() |
576 | @@ -43,7 +48,26 @@ class TestVmwareConfigFile(CiTestCase): |
577 | self.assertFalse(cf.should_keep_current_value("BAR"), "keepBar") |
578 | self.assertTrue(cf.should_remove_current_value("BAR"), "removeBar") |
579 | |
580 | + def test_datasource_instance_id(self): |
581 | + """Tests instance id for the DatasourceOVF""" |
582 | + cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg") |
583 | + |
584 | + instance_id_prefix = 'iid-vmware-' |
585 | + |
586 | + conf = Config(cf) |
587 | + |
588 | + (md1, _, _) = read_vmware_imc(conf) |
589 | + self.assertIn(instance_id_prefix, md1["instance-id"]) |
590 | + self.assertEqual(len(md1["instance-id"]), len(instance_id_prefix) + 8) |
591 | + |
592 | + (md2, _, _) = read_vmware_imc(conf) |
593 | + self.assertIn(instance_id_prefix, md2["instance-id"]) |
594 | + self.assertEqual(len(md2["instance-id"]), len(instance_id_prefix) + 8) |
595 | + |
596 | + self.assertNotEqual(md1["instance-id"], md2["instance-id"]) |
597 | + |
598 | def test_configfile_static_2nics(self): |
599 | + """Tests Config class for a configuration with two static NICs.""" |
600 | cf = ConfigFile("tests/data/vmware/cust-static-2nic.cfg") |
601 | |
602 | conf = Config(cf) |
603 | @@ -81,6 +105,7 @@ class TestVmwareConfigFile(CiTestCase): |
604 | self.assertTrue(not nics[1].staticIpv6, "ipv61 dhcp") |
605 | |
606 | def test_config_file_dhcp_2nics(self): |
607 | + """Tests Config class for a configuration with two DHCP NICs.""" |
608 | cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg") |
609 | |
610 | conf = Config(cf) |
611 | @@ -117,5 +142,197 @@ class TestVmwareConfigFile(CiTestCase): |
612 | conf = Config(cf) |
613 | self.assertTrue(conf.reset_password, "reset password") |
614 | |
615 | + def test_get_config_nameservers(self): |
616 | + """Tests DNS and nameserver settings in a configuration.""" |
617 | + cf = ConfigFile("tests/data/vmware/cust-static-2nic.cfg") |
618 | + |
619 | + config = Config(cf) |
620 | + |
621 | + network_config = get_network_config_from_conf(config, False) |
622 | + |
623 | + self.assertEqual(1, network_config.get('version')) |
624 | + |
625 | + config_types = network_config.get('config') |
626 | + name_servers = None |
627 | + dns_suffixes = None |
628 | + |
629 | + for type in config_types: |
630 | + if type.get('type') == 'nameserver': |
631 | + name_servers = type.get('address') |
632 | + dns_suffixes = type.get('search') |
633 | + break |
634 | + |
635 | + self.assertEqual(['10.20.145.1', '10.20.145.2'], |
636 | + name_servers, |
637 | + "dns") |
638 | + self.assertEqual(['eng.vmware.com', 'proxy.vmware.com'], |
639 | + dns_suffixes, |
640 | + "suffixes") |
641 | + |
642 | + def test_gen_subnet(self): |
643 | + """Tests if gen_subnet properly calculates network subnet from |
644 | + IPv4 address and netmask""" |
645 | + ip_subnet_list = [['10.20.87.253', '255.255.252.0', '10.20.84.0'], |
646 | + ['10.20.92.105', '255.255.252.0', '10.20.92.0'], |
647 | + ['192.168.0.10', '255.255.0.0', '192.168.0.0']] |
648 | + for entry in ip_subnet_list: |
649 | + self.assertEqual(entry[2], gen_subnet(entry[0], entry[1]), |
650 | + "Subnet for a specified ip and netmask") |
651 | + |
652 | + def test_get_config_dns_suffixes(self): |
653 | + """Tests if get_network_config_from_conf properly |
654 | + generates nameservers and dns settings from a |
655 | + specified configuration""" |
656 | + cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg") |
657 | + |
658 | + config = Config(cf) |
659 | + |
660 | + network_config = get_network_config_from_conf(config, False) |
661 | + |
662 | + self.assertEqual(1, network_config.get('version')) |
663 | + |
664 | + config_types = network_config.get('config') |
665 | + name_servers = None |
666 | + dns_suffixes = None |
667 | + |
668 | + for type in config_types: |
669 | + if type.get('type') == 'nameserver': |
670 | + name_servers = type.get('address') |
671 | + dns_suffixes = type.get('search') |
672 | + break |
673 | + |
674 | + self.assertEqual([], |
675 | + name_servers, |
676 | + "dns") |
677 | + self.assertEqual(['eng.vmware.com'], |
678 | + dns_suffixes, |
679 | + "suffixes") |
680 | + |
681 | + def test_get_nics_list_dhcp(self): |
682 | + """Tests if NicConfigurator properly calculates network subnets |
683 | + for a configuration with a list of DHCP NICs""" |
684 | + cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg") |
685 | + |
686 | + config = Config(cf) |
687 | + |
688 | + nicConfigurator = NicConfigurator(config.nics, False) |
689 | + nics_cfg_list = nicConfigurator.generate() |
690 | + |
691 | + self.assertEqual(2, len(nics_cfg_list), "number of config elements") |
692 | + |
693 | + nic1 = {'name': 'NIC1'} |
694 | + nic2 = {'name': 'NIC2'} |
695 | + for cfg in nics_cfg_list: |
696 | + if cfg.get('name') == nic1.get('name'): |
697 | + nic1.update(cfg) |
698 | + elif cfg.get('name') == nic2.get('name'): |
699 | + nic2.update(cfg) |
700 | + |
701 | + self.assertEqual('physical', nic1.get('type'), 'type of NIC1') |
702 | + self.assertEqual('NIC1', nic1.get('name'), 'name of NIC1') |
703 | + self.assertEqual('00:50:56:a6:8c:08', nic1.get('mac_address'), |
704 | + 'mac address of NIC1') |
705 | + subnets = nic1.get('subnets') |
706 | + self.assertEqual(1, len(subnets), 'number of subnets for NIC1') |
707 | + subnet = subnets[0] |
708 | + self.assertEqual('dhcp', subnet.get('type'), 'DHCP type for NIC1') |
709 | + self.assertEqual('auto', subnet.get('control'), 'NIC1 Control type') |
710 | + |
711 | + self.assertEqual('physical', nic2.get('type'), 'type of NIC2') |
712 | + self.assertEqual('NIC2', nic2.get('name'), 'name of NIC2') |
713 | + self.assertEqual('00:50:56:a6:5a:de', nic2.get('mac_address'), |
714 | + 'mac address of NIC2') |
715 | + subnets = nic2.get('subnets') |
716 | + self.assertEqual(1, len(subnets), 'number of subnets for NIC2') |
717 | + subnet = subnets[0] |
718 | + self.assertEqual('dhcp', subnet.get('type'), 'DHCP type for NIC2') |
719 | + self.assertEqual('auto', subnet.get('control'), 'NIC2 Control type') |
720 | + |
721 | + def test_get_nics_list_static(self): |
722 | + """Tests if NicConfigurator properly calculates network subnets |
723 | + for a configuration with 2 static NICs""" |
724 | + cf = ConfigFile("tests/data/vmware/cust-static-2nic.cfg") |
725 | + |
726 | + config = Config(cf) |
727 | + |
728 | + nicConfigurator = NicConfigurator(config.nics, False) |
729 | + nics_cfg_list = nicConfigurator.generate() |
730 | + |
731 | + self.assertEqual(5, len(nics_cfg_list), "number of elements") |
732 | + |
733 | + nic1 = {'name': 'NIC1'} |
734 | + nic2 = {'name': 'NIC2'} |
735 | + route_list = [] |
736 | + for cfg in nics_cfg_list: |
737 | + cfg_type = cfg.get('type') |
738 | + if cfg_type == 'physical': |
739 | + if cfg.get('name') == nic1.get('name'): |
740 | + nic1.update(cfg) |
741 | + elif cfg.get('name') == nic2.get('name'): |
742 | + nic2.update(cfg) |
743 | + elif cfg_type == 'route': |
744 | + route_list.append(cfg) |
745 | + |
746 | + self.assertEqual('physical', nic1.get('type'), 'type of NIC1') |
747 | + self.assertEqual('NIC1', nic1.get('name'), 'name of NIC1') |
748 | + self.assertEqual('00:50:56:a6:8c:08', nic1.get('mac_address'), |
749 | + 'mac address of NIC1') |
750 | + |
751 | + subnets = nic1.get('subnets') |
752 | + self.assertEqual(2, len(subnets), 'Number of subnets') |
753 | + |
754 | + static_subnet = [] |
755 | + static6_subnet = [] |
756 | + |
757 | + for subnet in subnets: |
758 | + subnet_type = subnet.get('type') |
759 | + if subnet_type == 'static': |
760 | + static_subnet.append(subnet) |
761 | + elif subnet_type == 'static6': |
762 | + static6_subnet.append(subnet) |
763 | + else: |
764 | + self.assertEqual(True, False, 'Unknown type') |
765 | + |
766 | + self.assertEqual(1, len(static_subnet), 'Number of static subnet') |
767 | + self.assertEqual(1, len(static6_subnet), 'Number of static6 subnet') |
768 | + |
769 | + subnet = static_subnet[0] |
770 | + self.assertEqual('10.20.87.154', subnet.get('address'), |
771 | + 'IPv4 address of static subnet') |
772 | + self.assertEqual('255.255.252.0', subnet.get('netmask'), |
773 | + 'NetMask of static subnet') |
774 | + self.assertEqual('auto', subnet.get('control'), |
775 | + 'control for static subnet') |
776 | + |
777 | + subnet = static6_subnet[0] |
778 | + self.assertEqual('fc00:10:20:87::154', subnet.get('address'), |
779 | + 'IPv6 address of static subnet') |
780 | + self.assertEqual('64', subnet.get('netmask'), |
781 | + 'NetMask of static6 subnet') |
782 | + |
783 | + route_set = set(['10.20.87.253', '10.20.87.105', '192.168.0.10']) |
784 | + for route in route_list: |
785 | + self.assertEqual(10000, route.get('metric'), 'metric of route') |
786 | + gateway = route.get('gateway') |
787 | + if gateway in route_set: |
788 | + route_set.discard(gateway) |
789 | + else: |
790 | + self.assertEqual(True, False, 'invalid gateway %s' % (gateway)) |
791 | + |
792 | + self.assertEqual('physical', nic2.get('type'), 'type of NIC2') |
793 | + self.assertEqual('NIC2', nic2.get('name'), 'name of NIC2') |
794 | + self.assertEqual('00:50:56:a6:ef:7d', nic2.get('mac_address'), |
795 | + 'mac address of NIC2') |
796 | + |
797 | + subnets = nic2.get('subnets') |
798 | + self.assertEqual(1, len(subnets), 'Number of subnets for NIC2') |
799 | + |
800 | + subnet = subnets[0] |
801 | + self.assertEqual('static', subnet.get('type'), 'Subnet type') |
802 | + self.assertEqual('192.168.6.102', subnet.get('address'), |
803 | + 'Subnet address') |
804 | + self.assertEqual('255.255.0.0', subnet.get('netmask'), |
805 | + 'Subnet netmask') |
806 | + |
807 | |
808 | # vi: ts=4 expandtab |
Sankar,
Hey, this looks really good. I do have some comments, but thanks for the good work.
a.) I think I'd like to move the conversion of the network config out of the 'get'.
The LOG.debug "Applying the network customization" is actually incorrect in that
no changes are done there. Lets move that to happen in the 'network_config' function.
if self._network_ config is not None: config
return self._network_
## convert the network config from the data in 'self' here to to the desired r(self. conf.nics) network_ config( ...) _network_ config = cfg
## 'network_config' format.
nc = NicConcifugrato
cfg = get_vmware_
self.
return self.network_config
b.) lets change get_vmware_ network_ config to either take no 'Config' input or only Config input.
as it is right now it takes a Config and a 'nics_cfg_list'.
but the nics_cfg_list is completely derived from the Config, so why not just pass the Config?
its probably helpful to just have a:
get_network_ config_ from_conf( )
get_network_ config( nameservers= None, search=None, nics=None)
that calls:
c.) It'll be helpful if there is an easy way to go from a vmwareImcConfig FilePath contents to a network config. Then your tests will just do: config_ from_vmware_ cfg("""
nc = network_
[NETWORK]
NETWORKING = yes
BOOTPROTO = dhcp
HOSTNAME = myhost1
DOMAINNAME = eng.vmware.com
[NIC-CONFIG]
NICS = NIC1,NIC2
[NIC1] assertEqual( expected, found)
...
""")
self.