Merge lp:~cisco-openstack/neutron/l2network-plugin into lp:neutron/diablo
- l2network-plugin
- Merge into diablo
Status: | Merged |
---|---|
Merged at revision: | 41 |
Proposed branch: | lp:~cisco-openstack/neutron/l2network-plugin |
Merge into: | lp:neutron/diablo |
Diff against target: |
4999 lines (+4806/-0) 37 files modified
quantum/plugins/cisco/README (+236/-0) quantum/plugins/cisco/__init__.py (+18/-0) quantum/plugins/cisco/common/__init__.py (+18/-0) quantum/plugins/cisco/common/cisco_configparser.py (+40/-0) quantum/plugins/cisco/common/cisco_constants.py (+103/-0) quantum/plugins/cisco/common/cisco_credentials.py (+60/-0) quantum/plugins/cisco/common/cisco_exceptions.py (+57/-0) quantum/plugins/cisco/common/cisco_nova_configuration.py (+35/-0) quantum/plugins/cisco/common/cisco_utils.py (+59/-0) quantum/plugins/cisco/conf/credentials.ini (+15/-0) quantum/plugins/cisco/conf/l2network_plugin.ini (+16/-0) quantum/plugins/cisco/conf/nexus.ini (+8/-0) quantum/plugins/cisco/conf/nova.ini (+8/-0) quantum/plugins/cisco/conf/plugins.ini (+3/-0) quantum/plugins/cisco/conf/ucs.ini (+10/-0) quantum/plugins/cisco/db/__init__.py (+18/-0) quantum/plugins/cisco/l2device_plugin_base.py (+152/-0) quantum/plugins/cisco/l2network_model.py (+100/-0) quantum/plugins/cisco/l2network_model_base.py (+150/-0) quantum/plugins/cisco/l2network_plugin.py (+394/-0) quantum/plugins/cisco/l2network_plugin_configuration.py (+50/-0) quantum/plugins/cisco/nexus/__init__.py (+18/-0) quantum/plugins/cisco/nexus/cisco_nexus_configuration.py (+35/-0) quantum/plugins/cisco/nexus/cisco_nexus_network_driver.py (+226/-0) quantum/plugins/cisco/nexus/cisco_nexus_plugin.py (+168/-0) quantum/plugins/cisco/run_tests.py (+301/-0) quantum/plugins/cisco/tests/__init__.py (+18/-0) quantum/plugins/cisco/tests/unit/__init__.py (+32/-0) quantum/plugins/cisco/tests/unit/test_l2networkApi.py (+892/-0) quantum/plugins/cisco/tests/unit/test_nexus_plugin.py (+282/-0) quantum/plugins/cisco/tests/unit/test_ucs_driver.py (+165/-0) quantum/plugins/cisco/tests/unit/test_ucs_plugin.py (+480/-0) quantum/plugins/cisco/ucs/__init__.py (+18/-0) quantum/plugins/cisco/ucs/cisco_getvif.py (+51/-0) quantum/plugins/cisco/ucs/cisco_ucs_configuration.py (+37/-0) quantum/plugins/cisco/ucs/cisco_ucs_network_driver.py (+235/-0) quantum/plugins/cisco/ucs/cisco_ucs_plugin.py (+298/-0) |
To merge this branch: | bzr merge lp:~cisco-openstack/neutron/l2network-plugin |
Related bugs: | |
Related blueprints: |
Auto-associate floating IP.
(High)
L3 agent support dual-NAT mode
(Undefined)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Salvatore Orlando | Approve | ||
Somik Behera | netstack-core | Approve | |
dan wendlandt | Approve | ||
Review via email: mp+70804@code.launchpad.net |
Commit message
Description of the change
This plugin implementation provides the following capabilities:
* A reference implementation of plugin framework for L2 network
* Supports multiple switches in the network
* Supports multiple models of switches concurrently
* Supports Cisco UCS blade servers with M81KR Virtual Interface Cards (aka "Palo adapters") via 802.1Qbh.
* Supports the Cisco Nexus family of switches.
All modules checked in are new (see quantum/
Unit tests are provided under quantum/
For details, please see: quantum/
The following is output from unit tests run on the core API (all tests pass successfully):
CoreAPITestFunc
test_
2011-08-08 16:48:54,093 DEBUG Creating new tenant record with tenant id test_tenant
2011-08-08 16:48:54,093 DEBUG test_associate_
OK
test_
2011-08-08 16:48:54,093 DEBUG test_associate_
OK
test_
2011-08-08 16:48:54,094 DEBUG create_network() called
2011-08-08 16:48:54,107 DEBUG delete_network() called
2011-08-08 16:48:54,109 DEBUG test_create_network - END
OK
test_
2011-08-08 16:48:54,109 DEBUG create_network() called
2011-08-08 16:48:54,112 DEBUG Creating new tenant record with tenant id test_network
2011-08-08 16:48:54,112 DEBUG create_port() called
2011-08-08 16:48:54,115 DEBUG delete_port() called
2011-08-08 16:48:54,117 DEBUG delete_network() called
2011-08-08 16:48:54,120 DEBUG test_create_port - END
OK
test_
2011-08-08 16:48:54,120 DEBUG create_port() called
2011-08-08 16:48:54,120 DEBUG test_create_
OK
test_
2011-08-08 16:48:54,121 DEBUG test_create_
OK
test_
2011-08-08 16:48:54,121 DEBUG create_network() called
2011-08-08 16:48:54,123 DEBUG delete_network() called
2011-08-08 16:48:54,126 DEBUG test_delete_network - END
OK
test_
2011-08-08 16:48:54,126 DEBUG delete_network() called
2011-08-08 16:48:54,126 DEBUG test_delete_
OK
test_
2011-08-08 16:48:54,126 DEBUG create_network() called
2011-08-08 16:48:54,128 DEBUG create_port() called
2011-08-08 16:48:54,131 DEBUG plug_interface() called
2011-08-08 16:48:54,133 DEBUG delete_network() called
2011-08-08 16:48:54,133 DEBUG unplug_interface() called
2011-08-08 16:48:54,136 DEBUG delete_port() called
2011-08-08 16:48:54,138 DEBUG delete_network() called
2011-08-08 16:48:54,141 DEBUG test_delete_
OK
test_
2011-08-08 16:48:54,141 DEBUG create_network() called
2011-08-08 16:48:54,144 DEBUG create_port() called
2011-08-08 16:48:54,146 DEBUG delete_port() called
2011-08-08 16:48:54,148 DEBUG delete_network() called
2011-08-08 16:48:54,151 DEBUG test_delete_port - END
OK
test_
2011-08-08 16:48:54,151 DEBUG create_network() called
2011-08-08 16:48:54,154 DEBUG delete_port() called
2011-08-08 16:48:54,154 DEBUG delete_network() called
2011-08-08 16:48:54,156 DEBUG test_delete_portDNE - END
OK
test_
2011-08-08 16:48:54,156 DEBUG create_network() called
2011-08-08 16:48:54,159 DEBUG create_port() called
2011-08-08 16:48:54,161 DEBUG plug_interface() called
2011-08-08 16:48:54,164 DEBUG delete_port() called
2011-08-08 16:48:54,164 DEBUG unplug_interface() called
2011-08-08 16:48:54,166 DEBUG delete_port() called
2011-08-08 16:48:54,169 DEBUG delete_network() called
2011-08-08 16:48:54,171 DEBUG test_delete_
OK
test_
2011-08-08 16:48:54,172 DEBUG delete_port() called
2011-08-08 16:48:54,172 DEBUG test_delete_
OK
test_
2011-08-08 16:48:54,172 DEBUG test_delete_
OK
test_
2011-08-08 16:48:54,172 DEBUG test_delete_
OK
test_
2011-08-08 16:48:54,172 DEBUG test_delete_
OK
test_
2011-08-08 16:48:54,173 DEBUG test_disassocia
OK
test_
2011-08-08 16:48:54,173 DEBUG test_disassocia
OK
test_get_tenant 2011-08-08 16:48:54,173 DEBUG test_get_tenant - START
2011-08-08 16:48:54,173 DEBUG test_get_tenant - END
OK
test_
2011-08-08 16:48:54,173 DEBUG test_get_vlan_name - END
OK
test_
2011-08-08 16:48:54,173 DEBUG test_validate_
OK
test_
2011-08-08 16:48:54,174 DEBUG create_network() called
2011-08-08 16:48:54,176 DEBUG create_network() called
2011-08-08 16:48:54,178 DEBUG get_all_networks() called
2011-08-08 16:48:54,180 DEBUG delete_network() called
2011-08-08 16:48:54,182 DEBUG delete_network() called
2011-08-08 16:48:54,185 DEBUG test_list_networks - END
OK
test_
2011-08-08 16:48:54,185 DEBUG test_create_
OK
test_list_ports 2011-08-08 16:48:54,185 DEBUG test_list_ports - START
2011-08-08 16:48:54,185 DEBUG create_network() called
2011-08-08 16:48:54,187 DEBUG create_port() called
2011-08-08 16:48:54,190 DEBUG create_port() called
2011-08-08 16:48:54,192 DEBUG get_all_ports() called
2011-08-08 16:48:54,193 DEBUG delete_port() called
2011-08-08 16:48:54,196 DEBUG delete_port() called
2011-08-08 16:48:54,198 DEBUG delete_network() called
2011-08-08 16:48:54,201 DEBUG test_list_ports - END
OK
test_
2011-08-08 16:48:54,201 DEBUG create_network() called
2011-08-08 16:48:54,204 DEBUG create_port() called
2011-08-08 16:48:54,206 DEBUG plug_interface() called
2011-08-08 16:48:54,208 DEBUG unplug_interface() called
2011-08-08 16:48:54,211 DEBUG delete_port() called
2011-08-08 16:48:54,214 DEBUG delete_network() called
2011-08-08 16:48:54,217 DEBUG test_plug_interface - END
OK
test_
2011-08-08 16:48:54,217 DEBUG plug_interface() called
2011-08-08 16:48:54,217 DEBUG test_plug_
OK
test_
2011-08-08 16:48:54,217 DEBUG create_network() called
2011-08-08 16:48:54,219 DEBUG plug_interface() called
2011-08-08 16:48:54,220 DEBUG delete_network() called
2011-08-08 16:48:54,222 DEBUG test_plug_
OK
test_
2011-08-08 16:48:54,222 DEBUG create_network() called
2011-08-08 16:48:54,225 DEBUG create_port() called
2011-08-08 16:48:54,227 DEBUG plug_interface() called
2011-08-08 16:48:54,229 DEBUG plug_interface() called
2011-08-08 16:48:54,230 DEBUG unplug_interface() called
2011-08-08 16:48:54,232 DEBUG delete_port() called
2011-08-08 16:48:54,235 DEBUG delete_network() called
2011-08-08 16:48:54,237 DEBUG test_plug_
OK
test_
2011-08-08 16:48:54,237 DEBUG create_network() called
2011-08-08 16:48:54,240 DEBUG rename_network() called
2011-08-08 16:48:54,242 DEBUG delete_network() called
2011-08-08 16:48:54,245 DEBUG test_rename_network - END
OK
test_
2011-08-08 16:48:54,245 DEBUG rename_network() called
2011-08-08 16:48:54,247 DEBUG test_rename_
OK
test_
2011-08-08 16:48:54,248 DEBUG test_show_
OK
test_
2011-08-08 16:48:54,248 DEBUG test_rename_
OK
test_
2011-08-08 16:48:54,248 DEBUG create_network() called
2011-08-08 16:48:54,250 DEBUG get_network_
2011-08-08 16:48:54,252 DEBUG delete_network() called
2011-08-08 16:48:54,254 DEBUG test_show_network - END
OK
test_
2011-08-08 16:48:54,254 DEBUG get_network_
2011-08-08 16:48:54,256 DEBUG test_show_
OK
test_show_port 2011-08-08 16:48:54,256 DEBUG test_show_port - START
2011-08-08 16:48:54,256 DEBUG create_network() called
2011-08-08 16:48:54,258 DEBUG create_port() called
2011-08-08 16:48:54,261 DEBUG get_port_details() called
2011-08-08 16:48:54,262 DEBUG delete_port() called
2011-08-08 16:48:54,264 DEBUG delete_network() called
2011-08-08 16:48:54,267 DEBUG test_show_port - END
OK
test_
2011-08-08 16:48:54,267 DEBUG create_network() called
2011-08-08 16:48:54,269 DEBUG get_port_details() called
2011-08-08 16:48:54,271 DEBUG delete_network() called
2011-08-08 16:48:54,273 DEBUG test_show_portDNE - END
OK
test_
2011-08-08 16:48:54,273 DEBUG get_port_details() called
2011-08-08 16:48:54,275 DEBUG test_show_
OK
test_
2011-08-08 16:48:54,275 DEBUG test_show_
OK
test_
2011-08-08 16:48:54,275 DEBUG test_show_
OK
test_
2011-08-08 16:48:54,275 DEBUG create_network() called
2011-08-08 16:48:54,278 DEBUG create_port() called
2011-08-08 16:48:54,280 DEBUG plug_interface() called
2011-08-08 16:48:54,283 DEBUG unplug_interface() called
2011-08-08 16:48:54,285 DEBUG delete_port() called
2011-08-08 16:48:54,287 DEBUG delete_network() called
2011-08-08 16:48:54,290 DEBUG test_unplug_
OK
test_
2011-08-08 16:48:54,290 DEBUG unplug_interface() called
2011-08-08 16:48:54,290 DEBUG test_unplug_
OK
test_
2011-08-08 16:48:54,291 DEBUG create_network() called
2011-08-08 16:48:54,293 DEBUG unplug_interface() called
2011-08-08 16:48:54,293 DEBUG delete_network() called
2011-08-08 16:48:54,296 DEBUG test_unplug_
OK
test_
2011-08-08 16:48:54,296 DEBUG create_network() called
2011-08-08 16:48:54,298 DEBUG create_port() called
2011-08-08 16:48:54,301 DEBUG update_port() called
2011-08-08 16:48:54,302 DEBUG delete_port() called
2011-08-08 16:48:54,304 DEBUG delete_network() called
2011-08-08 16:48:54,307 DEBUG test_update_port - END
OK
test_
2011-08-08 16:48:54,307 DEBUG create_network() called
2011-08-08 16:48:54,309 DEBUG update_port() called
2011-08-08 16:48:54,311 DEBUG delete_network() called
2011-08-08 16:48:54,313 DEBUG test_update_portDNE - END
OK
test_
2011-08-08 16:48:54,313 DEBUG update_port() called
2011-08-08 16:48:54,315 DEBUG test_update_
OK
test_
2011-08-08 16:48:54,315 DEBUG create_network() called
2011-08-08 16:48:54,318 DEBUG create_port() called
2011-08-08 16:48:54,320 DEBUG plug_interface() called
2011-08-08 16:48:54,323 DEBUG unplug_interface() called
2011-08-08 16:48:54,325 DEBUG delete_port() called
2011-08-08 16:48:54,328 DEBUG delete_network() called
2011-08-08 16:48:54,330 DEBUG test_validate_
OK
test_
2011-08-08 16:48:54,330 DEBUG test_validate_
OK
-------
Ran 47 tests in 0.238s
OK
- 58. By Sumit Naiksatam
-
Changed to default plugin class name.
- 59. By Sumit Naiksatam
-
Tiny change to the README file, instructions on how to get ncclient.
Salvatore Orlando (salvatore-orlando) wrote : | # |
Sumit Naiksatam (snaiksat) wrote : | # |
Thanks Salvatore for your in-depth review. I will be happy to provide the information you need. My responses are embedded inline.
> Hi Sumit,
>
> This is a massive piece of work, well done!
> The code looks great. It is well written and easily understandable.
>
> - README states that this plugin provides "A reference implementation of
> plugin framework for L2 network". My understanding of a "reference
> implementation" is that it should be a relatively basic implementation of a
> standardized interface, which could be executed by a large number of users.
> Probably the cisco plugin should not be considered a "reference"
> implementation for Quantum, as it has pretty much strict sw & hw requirements.
>
<Sumit> It seems the presence of the "Pre-requisites" section at the top of the README misleads the reader into thinking that the L2Network-plugin framework presented here can be only run with those specific requirements. We wanted to convey that the requirements mentioned in the pre-requisites were necessary when using the UCS and/or Nexus hardware. However, in the absence of the specific hardware and software, the plugin framework presented here can still be deployed and used.
For instance we have run this on an Ubuntu box by disabling the UCS and Nexus device-plugins (disabling is a matter of configuring the quantum/
l2network_
embody a framework which is not specific to any hardware or software, and can be easily reused and adapted to any other combination of devices and networks.
This notion of the framework is also elaborated in the design document for the blueprint:
http://
To summarize, it should be noted that there are two distinct broad contributions in this branch (1) a device/
Coming back to your comment, would it be ok if we qualify the "Pre-requisites" section with the additional information that those are needed only in the case of UCS and/or Nexus hardware/software? (I have modified the README file to reflect this.)</Sumit>
> - It would be great if you can improve a bit pylint score for your branch.
> Your branch scores 6.53/10, it would be great if could bring that result to at
> leasr 7.5/10. We are already doing some work for improving pylint in trunk
> code.
>
<Sumit> Would it be acceptable if we achieve this in an upcoming merge (which we are waiting to pro...
dan wendlandt (danwent) wrote : | # |
Very cool to see this code is ready for merge. Having a Cisco plugin is an important step for the project as a whole, so I'd like to see this code get in ASAP.
I have a good number of comments below, but for the most part they are just suggestions for how you could improve the readability of the code for developers (like me!) that are looking at it for the first time. I don't see any of them as blockers and since this code is entirely self-contained, I'm voting to approve for merge right away. Getting the pylint score up is a good goal, but as long as you take care of that with the next merge I'm cool with it.
Great work :)
Dan
'quantum/
* The notion of a "reference implementation" vs. the Palo/Nexus specific functionality makes sense, since you've explained it to me before. Perhaps separate out the audiences would make it more clear though. For quantum administrators, this code is useful because they can us it with UCS/Nexus based networks. For quantum developers this code is useful because they may want to base their own plugin on it.
* If you are using a special branch of nova, it may be cleaner + easier to just add a sqlalchemy migrate script to automatically create this table with the user syncs the DB.
200
+1. Create a table in the "nova" database for ports. This can be
201
+ accomplished with the following SQL statement:
quantum/
* Currently, the rest of the Quantum codebase uses "ACTIVE" instead of "UP". While I'm personally in favor of "UP", its probably best to be consistent, then we can switch all of them at the same time if we decide that's the right thing to do.
+PORT_UP = "UP"
* it strikes me that some of these constants are not plugin specific, so ideally we'd actually have those in quantum/common, so they can be leveraged by all plugins, clients, tests, etc. This isn't required, but would be a nice thing to do at some point.
quantum/
* I'm guessing your OK with this, but it struck me as a bit odd that you had methods for modifying the store, but those modifications where not persisted. Just wanted to make sure that is what you intended.
quantum/
It took me a bit of glancing to figure out how this file differed from the standard quantum/
quantum/
* Its not obvious to a reader why certain methods are implemented here while others aren't. A comment could help clarify why methods like update_port(), get_all_ports(), etc are not implemented (e.g., must they be implemented by a child class? If so, raising a NotImplemented exception might be better than silently passing).
quantum/
* use of the term "model" threw me at first, as in nova and the main quantum code-base "model" tends to refer to an sqlalchemy database model, whereas this code (I don't think) is rel...
- 60. By Sumit Naiksatam
-
Removed main from modules as per review comments.
Sumit Naiksatam (snaiksat) wrote : | # |
Thanks Dan for the thorough review, and the words of appreciation. My responses inline, pretty much in agreement with most of your suggestions (and a lot of the changes have already gone into the ongoing work). Some of the points which you have raised are better clarified in the document posted here:
http://
> Very cool to see this code is ready for merge. Having a Cisco plugin is an
> important step for the project as a whole, so I'd like to see this code get in
> ASAP.
>
> I have a good number of comments below, but for the most part they are just
> suggestions for how you could improve the readability of the code for
> developers (like me!) that are looking at it for the first time. I don't see
> any of them as blockers and since this code is entirely self-contained, I'm
> voting to approve for merge right away. Getting the pylint score up is a good
> goal, but as long as you take care of that with the next merge I'm cool with
> it.
>
> Great work :)
>
> Dan
>
> 'quantum/
>
> * The notion of a "reference implementation" vs. the Palo/Nexus specific
> functionality makes sense, since you've explained it to me before. Perhaps
> separate out the audiences would make it more clear though. For quantum
> administrators, this code is useful because they can us it with UCS/Nexus
> based networks. For quantum developers this code is useful because they may
> want to base their own plugin on it.
>
<Sumit> Agreed. We will try to do a better job of highlighting the L2 Network Plugin Framework (to support multiple devices/switches) versus the specific support for UCS/Nexus, both of which have gone into this branch. The former is intended to be useful for anyone using the Quantum service, and faced with the issue of handling multiple types of devices.</Sumit>
> * If you are using a special branch of nova, it may be cleaner + easier to
> just add a sqlalchemy migrate script to automatically create this table with
> the user syncs the DB.
> 200
> +1. Create a table in the "nova" database for ports. This can be
> 201
> + accomplished with the following SQL statement:
>
<Sumit> Agree. This is actually a temporary requirement, and should go away altogether. </Sumit>
> quantum/
>
> * Currently, the rest of the Quantum codebase uses "ACTIVE" instead of "UP".
> While I'm personally in favor of "UP", its probably best to be consistent,
> then we can switch all of them at the same time if we decide that's the right
> thing to do.
>
> +PORT_UP = "UP"
<Sumit> Agree. This has already been changed in a new branch. </Sumit>
>
> * it strikes me that some of these constants are not plugin specific, so
> ideally we'd actually have those in quantum/common, so they can be leveraged
> by all plugins, clients, tests, etc. This isn't required, but would be a nice
> thing to do at some point.
>
<Sumit> Ok, will look into it. </Sumit>
> quantum/
>
> * I'm guessing your OK with this, but it struck me as a bit odd that you had
> methods for modifying the store...
dan wendlandt (danwent) wrote : | # |
Ok, sounds like we're in good shape with this branch. I know Salvatore said
he was at least partially out-of-office this week, but it doesn't sound like
there were any blockers from his end, so hopefully we can get this merged
early this week.
Dan
p.s. the diagram in the document does indeed make things more clear. Its
funny, as I had almost added a comment to my review saying that a diagram
would be helpful :)
On Mon, Aug 15, 2011 at 2:31 AM, Sumit Naiksatam <email address hidden> wrote:
> Thanks Dan for the thorough review, and the words of appreciation. My
> responses inline, pretty much in agreement with most of your suggestions
> (and a lot of the changes have already gone into the ongoing work). Some of
> the points which you have raised are better clarified in the document posted
> here:
> http://
> this available sooner).
>
> > Very cool to see this code is ready for merge. Having a Cisco plugin is
> an
> > important step for the project as a whole, so I'd like to see this code
> get in
> > ASAP.
> >
> > I have a good number of comments below, but for the most part they are
> just
> > suggestions for how you could improve the readability of the code for
> > developers (like me!) that are looking at it for the first time. I don't
> see
> > any of them as blockers and since this code is entirely self-contained,
> I'm
> > voting to approve for merge right away. Getting the pylint score up is a
> good
> > goal, but as long as you take care of that with the next merge I'm cool
> with
> > it.
> >
> > Great work :)
> >
> > Dan
> >
> > 'quantum/
> >
> > * The notion of a "reference implementation" vs. the Palo/Nexus specific
> > functionality makes sense, since you've explained it to me before.
> Perhaps
> > separate out the audiences would make it more clear though. For quantum
> > administrators, this code is useful because they can us it with UCS/Nexus
> > based networks. For quantum developers this code is useful because they
> may
> > want to base their own plugin on it.
> >
>
> <Sumit> Agreed. We will try to do a better job of highlighting the L2
> Network Plugin Framework (to support multiple devices/switches) versus the
> specific support for UCS/Nexus, both of which have gone into this branch.
> The former is intended to be useful for anyone using the Quantum service,
> and faced with the issue of handling multiple types of devices.</Sumit>
>
> > * If you are using a special branch of nova, it may be cleaner + easier
> to
> > just add a sqlalchemy migrate script to automatically create this table
> with
> > the user syncs the DB.
> > 200
> > +1. Create a table in the "nova" database for ports. This can be
> > 201
> > + accomplished with the following SQL statement:
> >
>
> <Sumit> Agree. This is actually a temporary requirement, and should go away
> altogether. </Sumit>
>
> > quantum/
> >
> > * Currently, the rest of the Quantum codebase uses "ACTIVE" instead of
> "UP".
> > While I'm personally in favor of "UP", its probably best to be
> consistent,
> > then we can switch all of them at the sam...
- 61. By Edgar Magana
-
Removing extra testing function on Nexus Driver
Somik Behera (somikbehera) wrote : | # |
Hi Sumit,
First of all, Congratulations! to you, Edgar, Shweta and all others that have worked on creating this hunk of code. I concur the feelings of Salvatore and Dan and overall it looks well done!
I believe, Salvatore and Dan have done a extensive review and I believe everything looks pretty good. I did have one small comment regarding this merge-prop and "Define Quantum Plugin Interface for Python" blueprint.
At this point, the above referenced blueprint, which is a non vendor implementation specific, generic Quantum plugin, is already implemented and complete.
I was never under the impression the work contained in this merge-prop was specified under "Define Quantum Plugin Interface for Python" blueprint.
I do like the idea of "multi-switch plugin framework" that you guys have implemented as a generic framework for others to leverage, but I would unlink this branch from "Define Quantum Plugin Interface for Python" blueprint.
Reading the README, I believe we should call this a reference implementation of "Multi-switch plugin framework for a Quantum plugin using Cisco platform components" not "A reference implementation of plugin framework for L2 network."
An "reference implementation" is something that can be used by the end-user without requiring any specific HW/SW (something that cannot be obtained in the public domain).
This ambiguity threw me off as to what we are actually providing is not a plugin framework for Quantum, but a framework for a Quantum Plugin to use multiple devices/switches, which is great! but we just have better elucidate the fact.
Thanks,
Somik
- 62. By Sumit Naiksatam
-
Changes in the README file to incorporate Somik's comments.
Sumit Naiksatam (snaiksat) wrote : | # |
Thanks Somik for your review and words of encouragement. I have incorporate both your comments (1) removed the link to one of the blueprints, (2) changed the wording in the README as per your suggestion. Kindly check, and if found satisfactory, please approve.
> Hi Sumit,
>
> First of all, Congratulations! to you, Edgar, Shweta and all others that have
> worked on creating this hunk of code. I concur the feelings of Salvatore and
> Dan and overall it looks well done!
>
> I believe, Salvatore and Dan have done a extensive review and I believe
> everything looks pretty good. I did have one small comment regarding this
> merge-prop and "Define Quantum Plugin Interface for Python" blueprint.
>
> At this point, the above referenced blueprint, which is a non vendor
> implementation specific, generic Quantum plugin, is already implemented and
> complete.
>
> I was never under the impression the work contained in this merge-prop was
> specified under "Define Quantum Plugin Interface for Python" blueprint.
>
> I do like the idea of "multi-switch plugin framework" that you guys have
> implemented as a generic framework for others to leverage, but I would unlink
> this branch from "Define Quantum Plugin Interface for Python" blueprint.
>
> Reading the README, I believe we should call this a reference implementation
> of "Multi-switch plugin framework for a Quantum plugin using Cisco platform
> components" not "A reference implementation of plugin framework for L2
> network."
>
> An "reference implementation" is something that can be used by the end-user
> without requiring any specific HW/SW (something that cannot be obtained in the
> public domain).
>
>
> This ambiguity threw me off as to what we are actually providing is not a
> plugin framework for Quantum, but a framework for a Quantum Plugin to use
> multiple devices/switches, which is great! but we just have better elucidate
> the fact.
>
> Thanks,
> Somik
Sumit Naiksatam (snaiksat) wrote : | # |
Typo: incorporate > incorporated :-)
Thanks,
~Sumit.
> Thanks Somik for your review and words of encouragement. I have incorporate
> both your comments (1) removed the link to one of the blueprints, (2) changed
> the wording in the README as per your suggestion. Kindly check, and if found
> satisfactory, please approve.
>
> > Hi Sumit,
> >
> > First of all, Congratulations! to you, Edgar, Shweta and all others that
> have
> > worked on creating this hunk of code. I concur the feelings of Salvatore and
> > Dan and overall it looks well done!
> >
> > I believe, Salvatore and Dan have done a extensive review and I believe
> > everything looks pretty good. I did have one small comment regarding this
> > merge-prop and "Define Quantum Plugin Interface for Python" blueprint.
> >
> > At this point, the above referenced blueprint, which is a non vendor
> > implementation specific, generic Quantum plugin, is already implemented and
> > complete.
> >
> > I was never under the impression the work contained in this merge-prop was
> > specified under "Define Quantum Plugin Interface for Python" blueprint.
> >
> > I do like the idea of "multi-switch plugin framework" that you guys have
> > implemented as a generic framework for others to leverage, but I would
> unlink
> > this branch from "Define Quantum Plugin Interface for Python" blueprint.
> >
> > Reading the README, I believe we should call this a reference implementation
> > of "Multi-switch plugin framework for a Quantum plugin using Cisco platform
> > components" not "A reference implementation of plugin framework for L2
> > network."
> >
> > An "reference implementation" is something that can be used by the end-user
> > without requiring any specific HW/SW (something that cannot be obtained in
> the
> > public domain).
> >
> >
> > This ambiguity threw me off as to what we are actually providing is not a
> > plugin framework for Quantum, but a framework for a Quantum Plugin to use
> > multiple devices/switches, which is great! but we just have better elucidate
> > the fact.
> >
> > Thanks,
> > Somik
Somik Behera (somikbehera) wrote : | # |
Looks good!
Salvatore Orlando (salvatore-orlando) wrote : | # |
Comments addressed.
I'm ok to improve pylint score in subsequent merge.
Preview Diff
1 | === modified file 'etc/quantum.conf' |
2 | === added directory 'quantum/plugins/cisco' |
3 | === added file 'quantum/plugins/cisco/README' |
4 | --- quantum/plugins/cisco/README 1970-01-01 00:00:00 +0000 |
5 | +++ quantum/plugins/cisco/README 2011-08-15 17:33:23 +0000 |
6 | @@ -0,0 +1,236 @@ |
7 | +===================================================================== |
8 | +README: A Framework for a Quantum Plugin Supporting Multiple Switches |
9 | +===================================================================== |
10 | + |
11 | +:Author: Sumit Naiksatam, Ram Durairaj, Mark Voelker, Edgar Magana, Shweta Padubidri, Rohit Agarwalla, Ying Liu |
12 | +:Contact: netstack@lists.launchpad.net |
13 | +:Web site: https://launchpad.net/~cisco-openstack |
14 | +:Copyright: 2011 Cisco Systems, Inc. |
15 | + |
16 | +.. contents:: |
17 | + |
18 | +Introduction |
19 | +------------ |
20 | + |
21 | +This plugin implementation provides the following capabilities |
22 | +to help you take your Layer 2 network for a Quantum leap: |
23 | + |
24 | +* A reference implementation a framework for a Quantum Plugin |
25 | +to use multiple devices/switches in a L2 network |
26 | +* Supports multiple models of switches concurrently |
27 | +* Supports Cisco UCS blade servers with M81KR Virtual Interface Cards |
28 | + (aka "Palo adapters") via 802.1Qbh. |
29 | +* Supports the Cisco Nexus family of switches. |
30 | + |
31 | +It does not provide: |
32 | + |
33 | +* A hologram of Al that only you can see. |
34 | +* A map to help you find your way through time. |
35 | +* A cure for amnesia or your swiss-cheesed brain. |
36 | + |
37 | +Let's leap in! |
38 | + |
39 | +Pre-requisites |
40 | +-------------- |
41 | +(The following are necessary only when using the UCS and/or Nexus devices in your system. |
42 | +If you plan to just leverage the plugin framework, you do not need these.) |
43 | +* One or more UCS B200 series blade servers with M81KR VIC (aka |
44 | + Palo adapters) installed. |
45 | +* UCSM 2.0 (Capitola) Build 230 or above. |
46 | +* OpenStack Cactus release installation (additional patch is required, |
47 | + details follow in this document) |
48 | +* RHEL 6.1 (as of this writing, UCS only officially supports RHEL, but |
49 | + it should be noted that Ubuntu support is planned in coming releases as well) |
50 | + ** Package: python-configobj-4.6.0-3.el6.noarch (or newer) |
51 | + ** Package: python-routes-1.12.3-2.el6.noarch (or newer) |
52 | + |
53 | +If you are using a Nexus switch in your topology, you'll need the following |
54 | +NX-OS version and packages to enable Nexus support: |
55 | +* NX-OS 5.2.1 (Delhi) Build 69 or above. |
56 | +* paramiko library - SSHv2 protocol library for python |
57 | + ** To install on RHEL 6.1, run: yum install python-paramiko |
58 | +* ncclient v0.3.1 - Python library for NETCONF clients |
59 | + ** RedHat does not provide a package for ncclient in RHEL 6.1. Here is how |
60 | + to get it, from your shell prompt do: |
61 | + |
62 | + git clone git@github.com:ddutta/ncclient.git |
63 | + sudo python ./setup.py install |
64 | + |
65 | + ** For more information of ncclient, see: |
66 | + http://schmizz.net/ncclient/ |
67 | + |
68 | +To verify the version of any package you have installed on your system, |
69 | +run "rpm -qav | grep <package name>", where <package name> is the |
70 | +package you want to query (for example: python-routes). |
71 | + |
72 | +Note that you can get access to recent versions of the packages above |
73 | +and other OpenStack software packages by adding a new repository to |
74 | +your yum configuration. To do so, edit or create |
75 | +/etc/yum.repos.d/openstack.repo and add the following: |
76 | + |
77 | +[openstack-deps] |
78 | +name=OpenStack Nova Compute Dependencies |
79 | +baseurl=http://yum.griddynamics.net/yum/cactus/deps |
80 | +enabled=1 |
81 | +gpgcheck=1 |
82 | +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-OPENSTACK |
83 | + |
84 | +Then run "yum install python-routes". |
85 | + |
86 | + |
87 | +Module Structure: |
88 | +----------------- |
89 | +* quantum/plugins/cisco/ - Contains the L2-Network Plugin Framework |
90 | + /common - Modules common to the entire plugin |
91 | + /conf - All configuration files |
92 | + /db - Persistence framework |
93 | + /nexus - Nexus-specific modules |
94 | + /tests - Tests specific to this plugin |
95 | + /ucs - UCS-specific modules |
96 | + |
97 | + |
98 | +Plugin Installation Instructions |
99 | +---------------------------------- |
100 | +1. Make a backup copy of quantum/quantum/plugins.ini. |
101 | + |
102 | +2. Edit quantum/quantum/plugins.ini and edit the "provider" entry to point |
103 | + to the L2Network-plugin: |
104 | + |
105 | +provider = quantum.plugins.cisco.l2network_plugin.L2Network |
106 | + |
107 | +3. If you are not running Quantum on the same host as the OpenStack Cloud |
108 | + Controller, you will need to change the db_ip_address configuration |
109 | + in nova.ini. |
110 | + |
111 | +4. If you want to turn on support for Cisco Nexus switches: |
112 | + 4a. Uncomment the nexus_plugin property in |
113 | + quantum/plugins/cisco/conf/plugins.ini to read: |
114 | + |
115 | +nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin.NexusPlugin |
116 | + |
117 | + 4b. Enter the relevant configuration in the |
118 | + quantum/plugins/cisco/conf/nexus.ini file. Example: |
119 | + |
120 | +[SWITCH] |
121 | +# Change the following to reflect the IP address of the Nexus switch. |
122 | +# This will be the address at which Quantum sends and receives configuration |
123 | +# information via SSHv2. |
124 | +nexus_ip_address=10.0.0.1 |
125 | +# Port number on the Nexus switch to which the UCSM 6120 is connected |
126 | +# Use shortened interface syntax, e.g. "3/23" not "Ethernet3/23". |
127 | +nexus_port=3/23 |
128 | + |
129 | +[DRIVER] |
130 | +name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver |
131 | + |
132 | + 4c. Make sure that SSH host key of the Nexus switch is known to the |
133 | + host on which you are running the Quantum service. You can do |
134 | + this simply by logging in to your Quantum host as the user that |
135 | + Quantum runs as and SSHing to the switch at least once. If the |
136 | + host key changes (e.g. due to replacement of the supervisor or |
137 | + clearing of the SSH config on the switch), you may need to repeat |
138 | + this step and remove the old hostkey from ~/.ssh/known_hosts. |
139 | + |
140 | +5. Verify that you have the correct credentials for each IP address listed |
141 | + in quantum/plugins/cisco/conf/credentials.ini. Example: |
142 | + |
143 | +# Provide the UCSM credentials |
144 | +# UCSM IP address, username and password. |
145 | +[10.0.0.2] |
146 | +username=admin |
147 | +password=mySecretPasswordForUCSM |
148 | + |
149 | +# Provide the Nova DB credentials. |
150 | +# The IP address should be the same as in nova.ini. |
151 | +[10.0.0.3] |
152 | +username=nova |
153 | +password=mySecretPasswordForNova |
154 | + |
155 | +# Provide the Nexus credentials, if you are using Nexus switches. |
156 | +# If not this will be ignored. |
157 | +[10.0.0.1] |
158 | +username=admin |
159 | +password=mySecretPasswordForNexus |
160 | + |
161 | +6. Start the Quantum service. If something doesn't work, verify that |
162 | + your configuration of each of the above files hasn't gone a little kaka. |
163 | + Once you've put right what once went wrong, leap on. |
164 | + |
165 | + |
166 | +How to test the installation |
167 | +---------------------------- |
168 | +The unit tests are located at quantum/plugins/cisco/tests/unit. They can be |
169 | +executed from quantum/plugins/cisco/ using the run_tests.py script. |
170 | + |
171 | +1. Testing the core API (without UCS/Nexus/RHEL hardware, and can be run on |
172 | + Ubuntu): |
173 | + First disable all device-specific plugins by commenting out the entries in: |
174 | + quantum/plugins/cisco/conf/plugins.ini |
175 | + Then run the test script: |
176 | + |
177 | +python run_tests.py unit.test_l2networkApi |
178 | + |
179 | +2. Specific Plugin unit test (needs environment setup as indicated in the |
180 | + pre-requisites): |
181 | + python run_tests.py unit.<name_of_the file> |
182 | + E.g.: |
183 | + |
184 | +python run_tests.py unit.test_ucs_plugin.py |
185 | + |
186 | +3. All unit tests (needs environment setup as indicated in the pre-requisites): |
187 | + |
188 | +python run_tests.py unit |
189 | + |
190 | + |
191 | +Additional installation required on Nova Compute |
192 | +------------------------------------------------ |
193 | +1. Create a table in the "nova" database for ports. This can be |
194 | + accomplished with the following SQL statement: |
195 | + |
196 | +CREATE TABLE ports ( |
197 | + port_id VARCHAR(255) PRIMARY KEY, |
198 | + profile_name VARCHAR(255), |
199 | + dynamic_vnic VARCHAR(255), |
200 | + host VARCHAR(255), |
201 | + instance_name VARCHAR(255), |
202 | + instance_nic_name VARCHAR(255), |
203 | + used TINYINT(1) |
204 | +); |
205 | + |
206 | + Assuming you're using MySQL, you can run the following command from a |
207 | + shell prompt on the Cloud Controller node to create the table: |
208 | + |
209 | +mysql -uroot -p nova -e 'create table ports (port_id VARCHAR(255) primary key, profile_name VARCHAR(255), dynamic_vnic VARCHAR(255), host VARCHAR(255), instance_name VARCHAR(255), instance_nic_name VARCHAR(255), used tinyint(1));' |
210 | + |
211 | +You'll be prompted for a password. |
212 | + |
213 | +2. A patch is available for the Cactus release in this branch: |
214 | + https://code.launchpad.net/~snaiksat/quantum/cactus-ucs-support |
215 | + replace the following file in your installation: |
216 | + /usr/lib/python2.6/site-packages/nova/virt/libvirt_conn.py |
217 | + with the file from the branch: |
218 | + nova/virt/libvirt_conn.py |
219 | + |
220 | +3. Add the following file from the Cisco Nova branch: |
221 | + nova/virt/cisco_ucs.py |
222 | + to: |
223 | + /usr/lib/python2.6/site-packages/nova/virt/cisco_ucs.py |
224 | + |
225 | +4. Add the 802.1Qbh specific libvirt template file, from: |
226 | + nova/virt/libvirt-qbh.xml.template |
227 | + to: |
228 | + /usr/share/nova/libvirt-qbh.xml.template |
229 | + |
230 | +5. Edit /etc/nova.conf to set the libvirt XML template to the above template: |
231 | + --libvirt_xml_template=/usr/share/nova/libvirt-qbh.xml.template |
232 | + |
233 | +6. Restart the nova-compute service. |
234 | + |
235 | + (Note that the requirement for the above patch is temporary and will go away |
236 | + with the integration with OpenStack Diablo. A 802.1Qbh-specific VIF driver |
237 | + will be made available as per the specification here: |
238 | + http://wiki.openstack.org/network-refactoring#VIF_driver) |
239 | + |
240 | +Bingo bango bongo! That's it! Thanks for taking the leap into Quantum. |
241 | + |
242 | +...Oh, boy! |
243 | |
244 | === added file 'quantum/plugins/cisco/__init__.py' |
245 | --- quantum/plugins/cisco/__init__.py 1970-01-01 00:00:00 +0000 |
246 | +++ quantum/plugins/cisco/__init__.py 2011-08-15 17:33:23 +0000 |
247 | @@ -0,0 +1,18 @@ |
248 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
249 | +# |
250 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
251 | +# |
252 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
253 | +# not use this file except in compliance with the License. You may obtain |
254 | +# a copy of the License at |
255 | +# |
256 | +# http://www.apache.org/licenses/LICENSE-2.0 |
257 | +# |
258 | +# Unless required by applicable law or agreed to in writing, software |
259 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
260 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
261 | +# License for the specific language governing permissions and limitations |
262 | +# under the License. |
263 | +# |
264 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
265 | +# |
266 | |
267 | === added directory 'quantum/plugins/cisco/common' |
268 | === added file 'quantum/plugins/cisco/common/__init__.py' |
269 | --- quantum/plugins/cisco/common/__init__.py 1970-01-01 00:00:00 +0000 |
270 | +++ quantum/plugins/cisco/common/__init__.py 2011-08-15 17:33:23 +0000 |
271 | @@ -0,0 +1,18 @@ |
272 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
273 | +# |
274 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
275 | +# |
276 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
277 | +# not use this file except in compliance with the License. You may obtain |
278 | +# a copy of the License at |
279 | +# |
280 | +# http://www.apache.org/licenses/LICENSE-2.0 |
281 | +# |
282 | +# Unless required by applicable law or agreed to in writing, software |
283 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
284 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
285 | +# License for the specific language governing permissions and limitations |
286 | +# under the License. |
287 | +# |
288 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
289 | +# |
290 | |
291 | === added file 'quantum/plugins/cisco/common/cisco_configparser.py' |
292 | --- quantum/plugins/cisco/common/cisco_configparser.py 1970-01-01 00:00:00 +0000 |
293 | +++ quantum/plugins/cisco/common/cisco_configparser.py 2011-08-15 17:33:23 +0000 |
294 | @@ -0,0 +1,40 @@ |
295 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
296 | +# |
297 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
298 | +# |
299 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
300 | +# not use this file except in compliance with the License. You may obtain |
301 | +# a copy of the License at |
302 | +# |
303 | +# http://www.apache.org/licenses/LICENSE-2.0 |
304 | +# |
305 | +# Unless required by applicable law or agreed to in writing, software |
306 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
307 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
308 | +# License for the specific language governing permissions and limitations |
309 | +# under the License. |
310 | +# |
311 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
312 | +# |
313 | + |
314 | +import logging as LOG |
315 | +import os |
316 | + |
317 | +from configobj import ConfigObj |
318 | +from validate import Validator |
319 | + |
320 | +from quantum.plugins.cisco.common import cisco_constants as const |
321 | +from quantum.plugins.cisco.common import cisco_exceptions as cexc |
322 | + |
323 | +LOG.basicConfig(level=LOG.WARN) |
324 | +LOG.getLogger(const.LOGGER_COMPONENT_NAME) |
325 | + |
326 | + |
327 | +class CiscoConfigParser(ConfigObj): |
328 | + |
329 | + def __init__(self, filename): |
330 | + super(CiscoConfigParser, self).__init__(filename, raise_errors=True, |
331 | + file_error=True) |
332 | + |
333 | + def dummy(self, section, key): |
334 | + return section[key] |
335 | |
336 | === added file 'quantum/plugins/cisco/common/cisco_constants.py' |
337 | --- quantum/plugins/cisco/common/cisco_constants.py 1970-01-01 00:00:00 +0000 |
338 | +++ quantum/plugins/cisco/common/cisco_constants.py 2011-08-15 17:33:23 +0000 |
339 | @@ -0,0 +1,103 @@ |
340 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
341 | +# |
342 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
343 | +# |
344 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
345 | +# not use this file except in compliance with the License. You may obtain |
346 | +# a copy of the License at |
347 | +# |
348 | +# http://www.apache.org/licenses/LICENSE-2.0 |
349 | +# |
350 | +# Unless required by applicable law or agreed to in writing, software |
351 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
352 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
353 | +# License for the specific language governing permissions and limitations |
354 | +# under the License. |
355 | +# |
356 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
357 | +# |
358 | + |
359 | +PLUGINS = 'PLUGINS' |
360 | + |
361 | +PORT_STATE = 'port-state' |
362 | +PORT_UP = "UP" |
363 | +PORT_DOWN = "DOWN" |
364 | + |
365 | +ATTACHMENT = 'attachment' |
366 | +PORT_ID = 'port-id' |
367 | + |
368 | +NET_ID = 'net-id' |
369 | +NET_NAME = 'net-name' |
370 | +NET_PORTS = 'net-ports' |
371 | +NET_VLAN_NAME = 'net-vlan-name' |
372 | +NET_VLAN_ID = 'net-vlan-id' |
373 | +NET_TENANTS = 'net-tenants' |
374 | + |
375 | +TENANT_ID = 'tenant-id' |
376 | +TENANT_NETWORKS = 'tenant-networks' |
377 | +TENANT_NAME = 'tenant-name' |
378 | +TENANT_PORTPROFILES = 'tenant-portprofiles' |
379 | +TENANT_QOS_LEVELS = 'tenant-qos-levels' |
380 | +TENANT_CREDENTIALS = 'tenant-credentials' |
381 | + |
382 | +PORT_PROFILE = 'port-profile' |
383 | +PROFILE_ID = 'profile-id' |
384 | +PROFILE_NAME = 'profile-name' |
385 | +PROFILE_VLAN_NAME = 'profile-vlan-name' |
386 | +PROFILE_VLAN_ID = 'vlan-id' |
387 | +PROFILE_QOS = 'profile-qos' |
388 | +PROFILE_ASSOCIATIONS = 'assignment' |
389 | + |
390 | +QOS_LEVEL_ID = 'qos-id' |
391 | +QOS_LEVEL_NAME = 'qos-name' |
392 | +QOS_LEVEL_ASSOCIATIONS = 'qos-level-associations' |
393 | +QOS_LEVEL_DESCRIPTION = 'qos-desc' |
394 | + |
395 | +CREDENTIAL_ID = 'credential-id' |
396 | +CREDENTIAL_NAME = 'credential-name' |
397 | +CREDENTIAL_USERNAME = 'credential-username' |
398 | +CREDENTIAL_PASSWORD = 'credential-password' |
399 | +MASKED_PASSWORD = '********' |
400 | + |
401 | +USERNAME = 'username' |
402 | +PASSWORD = 'password' |
403 | + |
404 | +LOGGER_COMPONENT_NAME = "cisco_plugin" |
405 | + |
406 | +BLADE_INTF_DN = "blade-intf-distinguished-name" |
407 | +BLADE_INTF_ORDER = "blade-intf-order" |
408 | +BLADE_INTF_LINK_STATE = "blade-intf-link-state" |
409 | +BLADE_INTF_OPER_STATE = "blade-intf-operational-state" |
410 | +BLADE_INTF_INST_TYPE = "blade-intf-inst-type" |
411 | +BLADE_INTF_RHEL_DEVICE_NAME = "blade-intf-rhel-device-name" |
412 | +BLADE_INTF_DYNAMIC = "dynamic" |
413 | +BLADE_INTF_STATE_UNKNOWN = "unknown" |
414 | +BLADE_INTF_STATE_UNALLOCATED = "unallocated" |
415 | +BLADE_INTF_RESERVED = "blade-intf-reserved" |
416 | +BLADE_INTF_UNRESERVED = "blade-intf-unreserved" |
417 | +BLADE_INTF_RESERVATION = "blade-intf-reservation-status" |
418 | +BLADE_UNRESERVED_INTF_COUNT = "blade-unreserved-interfaces-count" |
419 | +BLADE_INTF_DATA = "blade-intf-data" |
420 | + |
421 | +LEAST_RSVD_BLADE_UCSM = "least-reserved-blade-ucsm" |
422 | +LEAST_RSVD_BLADE_CHASSIS = "least-reserved-blade-chassis" |
423 | +LEAST_RSVD_BLADE_ID = "least-reserved-blade-id" |
424 | +LEAST_RSVD_BLADE_DATA = "least-reserved-blade-data" |
425 | + |
426 | +RESERVED_NIC_HOSTNAME = "reserved-dynamic-nic-hostname" |
427 | +RESERVED_NIC_NAME = "reserved-dynamic-nic-device-name" |
428 | + |
429 | +RESERVED_INTERFACE_UCSM = "reserved-interface-ucsm-ip" |
430 | +RESERVED_INTERFACE_CHASSIS = "reserved-interface-chassis" |
431 | +RESERVED_INTERFACE_BLADE = "reserved-interface-blade" |
432 | +RESERVED_INTERFACE_DN = "reserved-interface-dn" |
433 | + |
434 | +RHEL_DEVICE_NAME_REPFIX = "eth" |
435 | + |
436 | +UCS_PLUGIN = 'ucs_plugin' |
437 | +NEXUS_PLUGIN = 'nexus_plugin' |
438 | + |
439 | +PLUGIN_OBJ_REF = 'plugin-obj-ref' |
440 | +PARAM_LIST = 'param-list' |
441 | + |
442 | +DEVICE_IP = 'device-ip' |
443 | |
444 | === added file 'quantum/plugins/cisco/common/cisco_credentials.py' |
445 | --- quantum/plugins/cisco/common/cisco_credentials.py 1970-01-01 00:00:00 +0000 |
446 | +++ quantum/plugins/cisco/common/cisco_credentials.py 2011-08-15 17:33:23 +0000 |
447 | @@ -0,0 +1,60 @@ |
448 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
449 | +# |
450 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
451 | +# |
452 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
453 | +# not use this file except in compliance with the License. You may obtain |
454 | +# a copy of the License at |
455 | +# |
456 | +# http://www.apache.org/licenses/LICENSE-2.0 |
457 | +# |
458 | +# Unless required by applicable law or agreed to in writing, software |
459 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
460 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
461 | +# License for the specific language governing permissions and limitations |
462 | +# under the License. |
463 | +# |
464 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
465 | +# |
466 | + |
467 | +import logging as LOG |
468 | +import os |
469 | + |
470 | +from quantum.plugins.cisco.common import cisco_constants as const |
471 | +from quantum.plugins.cisco.common import cisco_configparser as confp |
472 | + |
473 | +LOG.basicConfig(level=LOG.WARN) |
474 | +LOG.getLogger(const.LOGGER_COMPONENT_NAME) |
475 | + |
476 | +CREDENTIALS_FILE = "../conf/credentials.ini" |
477 | + |
478 | +cp = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \ |
479 | + + "/" + CREDENTIALS_FILE) |
480 | +_creds_dictionary = cp.walk(cp.dummy) |
481 | + |
482 | + |
483 | +class Store(object): |
484 | + @staticmethod |
485 | + def putCredential(id, username, password): |
486 | + _creds_dictionary[id] = {const.USERNAME: username, |
487 | + const.PASSWORD: password} |
488 | + |
489 | + @staticmethod |
490 | + def getUsername(id): |
491 | + return _creds_dictionary[id][const.USERNAME] |
492 | + |
493 | + @staticmethod |
494 | + def getPassword(id): |
495 | + return _creds_dictionary[id][const.PASSWORD] |
496 | + |
497 | + @staticmethod |
498 | + def getCredential(id): |
499 | + return _creds_dictionary[id] |
500 | + |
501 | + @staticmethod |
502 | + def getCredentials(): |
503 | + return _creds_dictionary |
504 | + |
505 | + @staticmethod |
506 | + def deleteCredential(id): |
507 | + return _creds_dictionary.pop(id) |
508 | |
509 | === added file 'quantum/plugins/cisco/common/cisco_exceptions.py' |
510 | --- quantum/plugins/cisco/common/cisco_exceptions.py 1970-01-01 00:00:00 +0000 |
511 | +++ quantum/plugins/cisco/common/cisco_exceptions.py 2011-08-15 17:33:23 +0000 |
512 | @@ -0,0 +1,57 @@ |
513 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
514 | +# |
515 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
516 | +# |
517 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
518 | +# not use this file except in compliance with the License. You may obtain |
519 | +# a copy of the License at |
520 | +# |
521 | +# http://www.apache.org/licenses/LICENSE-2.0 |
522 | +# |
523 | +# Unless required by applicable law or agreed to in writing, software |
524 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
525 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
526 | +# License for the specific language governing permissions and limitations |
527 | +# under the License. |
528 | +# |
529 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
530 | +# |
531 | + |
532 | +""" |
533 | +Exceptions used by the Cisco plugin |
534 | +""" |
535 | + |
536 | +from quantum.common import exceptions |
537 | + |
538 | + |
539 | +class NoMoreNics(exceptions.QuantumException): |
540 | + message = _("Unable to complete operation on port %(port_id)s " \ |
541 | + "for network %(net_id)s. No more dynamic nics are available" \ |
542 | + "in the system.") |
543 | + |
544 | + |
545 | +class PortProfileLimit(exceptions.QuantumException): |
546 | + message = _("Unable to complete operation on port %(port_id)s " \ |
547 | + "for network %(net_id)s. The system has reached the maximum" \ |
548 | + "limit of allowed port profiles.") |
549 | + |
550 | + |
551 | +class UCSMPortProfileLimit(exceptions.QuantumException): |
552 | + message = _("Unable to complete operation on port %(port_id)s " \ |
553 | + "for network %(net_id)s. The system has reached the maximum" \ |
554 | + "limit of allowed UCSM port profiles.") |
555 | + |
556 | + |
557 | +class NetworksLimit(exceptions.QuantumException): |
558 | + message = _("Unable to create new network. Number of networks" \ |
559 | + "for the system has exceeded the limit") |
560 | + |
561 | + |
562 | +class PortProfileNotFound(exceptions.QuantumException): |
563 | + message = _("Port profile %(portprofile_id)s could not be found " \ |
564 | + "for tenant %(tenant_id)s") |
565 | + |
566 | + |
567 | +class PortProfileInvalidDelete(exceptions.QuantumException): |
568 | + message = _("Port profile %(profile_id)s could not be deleted " \ |
569 | + "for tenant %(tenant_id)s since port associations exist") |
570 | |
571 | === added file 'quantum/plugins/cisco/common/cisco_nova_configuration.py' |
572 | --- quantum/plugins/cisco/common/cisco_nova_configuration.py 1970-01-01 00:00:00 +0000 |
573 | +++ quantum/plugins/cisco/common/cisco_nova_configuration.py 2011-08-15 17:33:23 +0000 |
574 | @@ -0,0 +1,35 @@ |
575 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
576 | +# |
577 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
578 | +# |
579 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
580 | +# not use this file except in compliance with the License. You may obtain |
581 | +# a copy of the License at |
582 | +# |
583 | +# http://www.apache.org/licenses/LICENSE-2.0 |
584 | +# |
585 | +# Unless required by applicable law or agreed to in writing, software |
586 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
587 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
588 | +# License for the specific language governing permissions and limitations |
589 | +# under the License. |
590 | +# |
591 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
592 | +# |
593 | + |
594 | +import os |
595 | + |
596 | +from quantum.plugins.cisco.common import cisco_configparser as confp |
597 | + |
598 | +CONF_FILE = "../conf/nova.ini" |
599 | + |
600 | +cp = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \ |
601 | + + "/" + CONF_FILE) |
602 | + |
603 | +section = cp['NOVA'] |
604 | +DB_SERVER_IP = section['db_server_ip'] |
605 | +DB_NAME = section['db_name'] |
606 | +DB_USERNAME = section['db_username'] |
607 | +DB_PASSWORD = section['db_password'] |
608 | +NOVA_HOST_NAME = section['nova_host_name'] |
609 | +NOVA_PROJ_NAME = section['nova_proj_name'] |
610 | |
611 | === added file 'quantum/plugins/cisco/common/cisco_utils.py' |
612 | --- quantum/plugins/cisco/common/cisco_utils.py 1970-01-01 00:00:00 +0000 |
613 | +++ quantum/plugins/cisco/common/cisco_utils.py 2011-08-15 17:33:23 +0000 |
614 | @@ -0,0 +1,59 @@ |
615 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
616 | +# |
617 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
618 | +# |
619 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
620 | +# not use this file except in compliance with the License. You may obtain |
621 | +# a copy of the License at |
622 | +# |
623 | +# http://www.apache.org/licenses/LICENSE-2.0 |
624 | +# |
625 | +# Unless required by applicable law or agreed to in writing, software |
626 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
627 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
628 | +# License for the specific language governing permissions and limitations |
629 | +# under the License. |
630 | +# |
631 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
632 | +# |
633 | + |
634 | +import MySQLdb |
635 | +import logging as LOG |
636 | +import sys |
637 | +import traceback |
638 | + |
639 | +from quantum.common import exceptions as exc |
640 | +from quantum.plugins.cisco.common import cisco_constants as const |
641 | +from quantum.plugins.cisco.common import cisco_credentials as cred |
642 | +from quantum.plugins.cisco.common import cisco_nova_configuration as conf |
643 | + |
644 | +LOG.basicConfig(level=LOG.WARN) |
645 | +LOG.getLogger(const.LOGGER_COMPONENT_NAME) |
646 | + |
647 | + |
648 | +class DBUtils(object): |
649 | + |
650 | + def __init__(self): |
651 | + pass |
652 | + |
653 | + def _get_db_connection(self): |
654 | + db_ip = conf.DB_SERVER_IP |
655 | + db_username = conf.DB_USERNAME |
656 | + db_password = conf.DB_PASSWORD |
657 | + self.db = MySQLdb.connect(db_ip, db_username, db_password, |
658 | + conf.DB_NAME) |
659 | + return self.db |
660 | + |
661 | + def execute_db_query(self, sql_query): |
662 | + db = self._get_db_connection() |
663 | + cursor = db.cursor() |
664 | + try: |
665 | + cursor.execute(sql_query) |
666 | + results = cursor.fetchall() |
667 | + db.commit() |
668 | + LOG.debug("DB query execution succeeded: %s" % sql_query) |
669 | + except: |
670 | + db.rollback() |
671 | + LOG.debug("DB query execution failed: %s" % sql_query) |
672 | + traceback.print_exc() |
673 | + db.close() |
674 | |
675 | === added directory 'quantum/plugins/cisco/conf' |
676 | === added file 'quantum/plugins/cisco/conf/credentials.ini' |
677 | --- quantum/plugins/cisco/conf/credentials.ini 1970-01-01 00:00:00 +0000 |
678 | +++ quantum/plugins/cisco/conf/credentials.ini 2011-08-15 17:33:23 +0000 |
679 | @@ -0,0 +1,15 @@ |
680 | +#Provide the UCSM credentials |
681 | +[<put_ucsm_ip_address_here>] |
682 | +username=<put_user_name_here> |
683 | +password=<put_password_here> |
684 | + |
685 | +#Provide the Nova DB credentials, the IP address should be the same as in nova.ini |
686 | +[<put_nova_db_ip_here>] |
687 | +username=<put_user_name_here> |
688 | +password=<put_password_here> |
689 | + |
690 | +#Provide the Nexus credentials, if you are using Nexus |
691 | +[<put_nexus_ip_address_here>] |
692 | +username=<put_user_name_here> |
693 | +password=<put_password_here> |
694 | + |
695 | |
696 | === added file 'quantum/plugins/cisco/conf/l2network_plugin.ini' |
697 | --- quantum/plugins/cisco/conf/l2network_plugin.ini 1970-01-01 00:00:00 +0000 |
698 | +++ quantum/plugins/cisco/conf/l2network_plugin.ini 2011-08-15 17:33:23 +0000 |
699 | @@ -0,0 +1,16 @@ |
700 | +[VLANS] |
701 | +vlan_start=<put_vlan_id_range_start_here> |
702 | +vlan_end=<put_vlan_id_range_end_here> |
703 | +vlan_name_prefix=q- |
704 | + |
705 | +[PORTS] |
706 | +max_ports=100 |
707 | + |
708 | +[PORTPROFILES] |
709 | +max_port_profiles=65568 |
710 | + |
711 | +[NETWORKS] |
712 | +max_networks=65568 |
713 | + |
714 | +[MODEL] |
715 | +model_class=quantum.plugins.cisco.l2network_model.L2NetworkModel |
716 | |
717 | === added file 'quantum/plugins/cisco/conf/nexus.ini' |
718 | --- quantum/plugins/cisco/conf/nexus.ini 1970-01-01 00:00:00 +0000 |
719 | +++ quantum/plugins/cisco/conf/nexus.ini 2011-08-15 17:33:23 +0000 |
720 | @@ -0,0 +1,8 @@ |
721 | +[SWITCH] |
722 | +# Change the following to reflect the Nexus switch details |
723 | +nexus_ip_address=<put_nexus_switch_ip_address_here> |
724 | +#Port number of the Interface connected from the Nexus 7K Switch to UCSM 6120, e.g.: 3/23 |
725 | +nexus_port=<put_interface_name_here> |
726 | + |
727 | +[DRIVER] |
728 | +name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver |
729 | |
730 | === added file 'quantum/plugins/cisco/conf/nova.ini' |
731 | --- quantum/plugins/cisco/conf/nova.ini 1970-01-01 00:00:00 +0000 |
732 | +++ quantum/plugins/cisco/conf/nova.ini 2011-08-15 17:33:23 +0000 |
733 | @@ -0,0 +1,8 @@ |
734 | +[NOVA] |
735 | +#Change the following details to reflect your OpenStack Nova configuration. If you are running this service on the same machine as the Nova DB, you do not have to change the IP address. |
736 | +db_server_ip=127.0.0.1 |
737 | +db_name=nova |
738 | +db_username=<put_db_user_name_here> |
739 | +db_password=<put_db_password_here> |
740 | +nova_host_name=<put_openstack_cloud_controller_hostname_here> |
741 | +nova_proj_name=<put_openstack_project_name_here> |
742 | |
743 | === added file 'quantum/plugins/cisco/conf/plugins.ini' |
744 | --- quantum/plugins/cisco/conf/plugins.ini 1970-01-01 00:00:00 +0000 |
745 | +++ quantum/plugins/cisco/conf/plugins.ini 2011-08-15 17:33:23 +0000 |
746 | @@ -0,0 +1,3 @@ |
747 | +[PLUGINS] |
748 | +ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_plugin.UCSVICPlugin |
749 | +#nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin.NexusPlugin |
750 | |
751 | === added file 'quantum/plugins/cisco/conf/ucs.ini' |
752 | --- quantum/plugins/cisco/conf/ucs.ini 1970-01-01 00:00:00 +0000 |
753 | +++ quantum/plugins/cisco/conf/ucs.ini 2011-08-15 17:33:23 +0000 |
754 | @@ -0,0 +1,10 @@ |
755 | +[UCSM] |
756 | +#change the following to the appropriate UCSM IP address |
757 | +ip_address=<put_ucsm_ip_address_here> |
758 | +default_vlan_name=default |
759 | +default_vlan_id=1 |
760 | +max_ucsm_port_profiles=1024 |
761 | +profile_name_prefix=q- |
762 | + |
763 | +[DRIVER] |
764 | +name=quantum.plugins.cisco.ucs.cisco_ucs_network_driver.CiscoUCSMDriver |
765 | |
766 | === added directory 'quantum/plugins/cisco/db' |
767 | === added file 'quantum/plugins/cisco/db/__init__.py' |
768 | --- quantum/plugins/cisco/db/__init__.py 1970-01-01 00:00:00 +0000 |
769 | +++ quantum/plugins/cisco/db/__init__.py 2011-08-15 17:33:23 +0000 |
770 | @@ -0,0 +1,18 @@ |
771 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
772 | +# |
773 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
774 | +# |
775 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
776 | +# not use this file except in compliance with the License. You may obtain |
777 | +# a copy of the License at |
778 | +# |
779 | +# http://www.apache.org/licenses/LICENSE-2.0 |
780 | +# |
781 | +# Unless required by applicable law or agreed to in writing, software |
782 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
783 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
784 | +# License for the specific language governing permissions and limitations |
785 | +# under the License. |
786 | +# |
787 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
788 | +# |
789 | |
790 | === added file 'quantum/plugins/cisco/l2device_plugin_base.py' |
791 | --- quantum/plugins/cisco/l2device_plugin_base.py 1970-01-01 00:00:00 +0000 |
792 | +++ quantum/plugins/cisco/l2device_plugin_base.py 2011-08-15 17:33:23 +0000 |
793 | @@ -0,0 +1,152 @@ |
794 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
795 | +# |
796 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
797 | +# |
798 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
799 | +# not use this file except in compliance with the License. You may obtain |
800 | +# a copy of the License at |
801 | +# |
802 | +# http://www.apache.org/licenses/LICENSE-2.0 |
803 | +# |
804 | +# Unless required by applicable law or agreed to in writing, software |
805 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
806 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
807 | +# License for the specific language governing permissions and limitations |
808 | +# under the License. |
809 | +# |
810 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
811 | +# |
812 | + |
813 | +import inspect |
814 | +from abc import ABCMeta, abstractmethod |
815 | + |
816 | + |
817 | +class L2DevicePluginBase(object): |
818 | + |
819 | + __metaclass__ = ABCMeta |
820 | + |
821 | + @abstractmethod |
822 | + def get_all_networks(self, tenant_id, **kwargs): |
823 | + """ |
824 | + :returns: |
825 | + :raises: |
826 | + """ |
827 | + pass |
828 | + |
829 | + @abstractmethod |
830 | + def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id, |
831 | + **kwargs): |
832 | + """ |
833 | + :returns: |
834 | + :raises: |
835 | + """ |
836 | + pass |
837 | + |
838 | + @abstractmethod |
839 | + def delete_network(self, tenant_id, net_id, **kwargs): |
840 | + """ |
841 | + :returns: |
842 | + :raises: |
843 | + """ |
844 | + pass |
845 | + |
846 | + @abstractmethod |
847 | + def get_network_details(self, tenant_id, net_id, **kwargs): |
848 | + """ |
849 | + :returns: |
850 | + :raises: |
851 | + """ |
852 | + pass |
853 | + |
854 | + @abstractmethod |
855 | + def rename_network(self, tenant_id, net_id, new_name, **kwargs): |
856 | + """ |
857 | + :returns: |
858 | + :raises: |
859 | + """ |
860 | + pass |
861 | + |
862 | + @abstractmethod |
863 | + def get_all_ports(self, tenant_id, net_id, **kwargs): |
864 | + """ |
865 | + :returns: |
866 | + :raises: |
867 | + """ |
868 | + pass |
869 | + |
870 | + @abstractmethod |
871 | + def create_port(self, tenant_id, net_id, port_state, port_id, **kwargs): |
872 | + """ |
873 | + :returns: |
874 | + :raises: |
875 | + """ |
876 | + pass |
877 | + |
878 | + @abstractmethod |
879 | + def delete_port(self, tenant_id, net_id, port_id, **kwargs): |
880 | + """ |
881 | + :returns: |
882 | + :raises: |
883 | + """ |
884 | + pass |
885 | + |
886 | + @abstractmethod |
887 | + def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs): |
888 | + """ |
889 | + :returns: |
890 | + :raises: |
891 | + """ |
892 | + pass |
893 | + |
894 | + @abstractmethod |
895 | + def get_port_details(self, tenant_id, net_id, port_id, **kwargs): |
896 | + """ |
897 | + :returns: |
898 | + :raises: |
899 | + """ |
900 | + pass |
901 | + |
902 | + @abstractmethod |
903 | + def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id, |
904 | + **kwargs): |
905 | + """ |
906 | + :returns: |
907 | + :raises: |
908 | + """ |
909 | + pass |
910 | + |
911 | + @abstractmethod |
912 | + def unplug_interface(self, tenant_id, net_id, port_id, **kwargs): |
913 | + """ |
914 | + :returns: |
915 | + :raises: |
916 | + """ |
917 | + pass |
918 | + |
919 | + @classmethod |
920 | + def __subclasshook__(cls, klass): |
921 | + """ |
922 | + The __subclasshook__ method is a class method |
923 | + that will be called everytime a class is tested |
924 | + using issubclass(klass, Plugin). |
925 | + In that case, it will check that every method |
926 | + marked with the abstractmethod decorator is |
927 | + provided by the plugin class. |
928 | + """ |
929 | + if cls is QuantumPluginBase: |
930 | + for method in cls.__abstractmethods__: |
931 | + method_ok = False |
932 | + for base in klass.__mro__: |
933 | + if method in base.__dict__: |
934 | + fn_obj = base.__dict__[method] |
935 | + if inspect.isfunction(fn_obj): |
936 | + abstract_fn_obj = cls.__dict__[method] |
937 | + arg_count = fn_obj.func_code.co_argcount |
938 | + expected_arg_count = \ |
939 | + abstract_fn_obj.func_code.co_argcount |
940 | + method_ok = arg_count == expected_arg_count |
941 | + if method_ok: |
942 | + continue |
943 | + return NotImplemented |
944 | + return True |
945 | + return NotImplemented |
946 | |
947 | === added file 'quantum/plugins/cisco/l2network_model.py' |
948 | --- quantum/plugins/cisco/l2network_model.py 1970-01-01 00:00:00 +0000 |
949 | +++ quantum/plugins/cisco/l2network_model.py 2011-08-15 17:33:23 +0000 |
950 | @@ -0,0 +1,100 @@ |
951 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
952 | +# |
953 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
954 | +# |
955 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
956 | +# not use this file except in compliance with the License. You may obtain |
957 | +# a copy of the License at |
958 | +# |
959 | +# http://www.apache.org/licenses/LICENSE-2.0 |
960 | +# |
961 | +# Unless required by applicable law or agreed to in writing, software |
962 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
963 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
964 | +# License for the specific language governing permissions and limitations |
965 | +# under the License. |
966 | +# |
967 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
968 | +# |
969 | + |
970 | +import inspect |
971 | +import logging as LOG |
972 | + |
973 | +from quantum.common import utils |
974 | +from quantum.plugins.cisco.l2network_model_base import L2NetworkModelBase |
975 | +from quantum.plugins.cisco import l2network_plugin_configuration as conf |
976 | +from quantum.plugins.cisco.common import cisco_constants as const |
977 | + |
978 | +LOG.basicConfig(level=LOG.WARN) |
979 | +LOG.getLogger(const.LOGGER_COMPONENT_NAME) |
980 | + |
981 | + |
982 | +class L2NetworkModel(L2NetworkModelBase): |
983 | + _plugins = {} |
984 | + |
985 | + def __init__(self): |
986 | + for key in conf.plugins[const.PLUGINS].keys(): |
987 | + self._plugins[key] = utils.import_object( |
988 | + conf.plugins[const.PLUGINS][key]) |
989 | + LOG.debug("Loaded device plugin %s\n" % \ |
990 | + conf.plugins[const.PLUGINS][key]) |
991 | + |
992 | + def _funcName(self, offset=0): |
993 | + return inspect.stack()[1 + offset][3] |
994 | + |
995 | + def _invokeAllDevicePlugins(self, function_name, args, kwargs): |
996 | + for pluginObjRef in self._plugins.values(): |
997 | + getattr(pluginObjRef, function_name)(*args, **kwargs) |
998 | + |
999 | + def _invokeUCSPlugin(self, function_name, args, kwargs): |
1000 | + if const.UCS_PLUGIN in self._plugins.keys(): |
1001 | + getattr(self._plugins[const.UCS_PLUGIN], |
1002 | + function_name)(*args, **kwargs) |
1003 | + |
1004 | + def _invokeNexusPlugin(self, function_name, args, kwargs): |
1005 | + if const.NEXUS_PLUGIN in self._plugins.keys(): |
1006 | + getattr(self._plugins[const.NEXUS_PLUGIN], |
1007 | + function_name)(*args, **kwargs) |
1008 | + |
1009 | + def get_all_networks(self, args): |
1010 | + pass |
1011 | + |
1012 | + def create_network(self, args): |
1013 | + deviceParams = {const.DEVICE_IP: ""} |
1014 | + self._invokeAllDevicePlugins(self._funcName(), args, deviceParams) |
1015 | + |
1016 | + def delete_network(self, args): |
1017 | + deviceParams = {const.DEVICE_IP: ""} |
1018 | + self._invokeAllDevicePlugins(self._funcName(), args, deviceParams) |
1019 | + |
1020 | + def get_network_details(self, args): |
1021 | + pass |
1022 | + |
1023 | + def rename_network(self, args): |
1024 | + deviceParams = {const.DEVICE_IP: ""} |
1025 | + self._invokeAllDevicePlugins(self._funcName(), args, deviceParams) |
1026 | + |
1027 | + def get_all_ports(self, args): |
1028 | + pass |
1029 | + |
1030 | + def create_port(self, args): |
1031 | + deviceParams = {const.DEVICE_IP: ""} |
1032 | + self._invokeUCSPlugin(self._funcName(), args, deviceParams) |
1033 | + |
1034 | + def delete_port(self, args): |
1035 | + deviceParams = {const.DEVICE_IP: ""} |
1036 | + self._invokeUCSPlugin(self._funcName(), args, deviceParams) |
1037 | + |
1038 | + def update_port(self, args): |
1039 | + pass |
1040 | + |
1041 | + def get_port_details(self, args): |
1042 | + pass |
1043 | + |
1044 | + def plug_interface(self, args): |
1045 | + deviceParams = {const.DEVICE_IP: ""} |
1046 | + self._invokeUCSPlugin(self._funcName(), args, deviceParams) |
1047 | + |
1048 | + def unplug_interface(self, args): |
1049 | + deviceParams = {const.DEVICE_IP: ""} |
1050 | + self._invokeUCSPlugin(self._funcName(), args, deviceParams) |
1051 | |
1052 | === added file 'quantum/plugins/cisco/l2network_model_base.py' |
1053 | --- quantum/plugins/cisco/l2network_model_base.py 1970-01-01 00:00:00 +0000 |
1054 | +++ quantum/plugins/cisco/l2network_model_base.py 2011-08-15 17:33:23 +0000 |
1055 | @@ -0,0 +1,150 @@ |
1056 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
1057 | +# |
1058 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
1059 | +# |
1060 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
1061 | +# not use this file except in compliance with the License. You may obtain |
1062 | +# a copy of the License at |
1063 | +# |
1064 | +# http://www.apache.org/licenses/LICENSE-2.0 |
1065 | +# |
1066 | +# Unless required by applicable law or agreed to in writing, software |
1067 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
1068 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
1069 | +# License for the specific language governing permissions and limitations |
1070 | +# under the License. |
1071 | +# |
1072 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
1073 | +# |
1074 | + |
1075 | +import inspect |
1076 | +from abc import ABCMeta, abstractmethod |
1077 | + |
1078 | + |
1079 | +class L2NetworkModelBase(object): |
1080 | + |
1081 | + __metaclass__ = ABCMeta |
1082 | + |
1083 | + @abstractmethod |
1084 | + def get_all_networks(self, args): |
1085 | + """ |
1086 | + :returns: |
1087 | + :raises: |
1088 | + """ |
1089 | + pass |
1090 | + |
1091 | + @abstractmethod |
1092 | + def create_network(self, args): |
1093 | + """ |
1094 | + :returns: |
1095 | + :raises: |
1096 | + """ |
1097 | + pass |
1098 | + |
1099 | + @abstractmethod |
1100 | + def delete_network(self, args): |
1101 | + """ |
1102 | + :returns: |
1103 | + :raises: |
1104 | + """ |
1105 | + pass |
1106 | + |
1107 | + @abstractmethod |
1108 | + def get_network_details(self, args): |
1109 | + """ |
1110 | + :returns: |
1111 | + :raises: |
1112 | + """ |
1113 | + pass |
1114 | + |
1115 | + @abstractmethod |
1116 | + def rename_network(self, args): |
1117 | + """ |
1118 | + :returns: |
1119 | + :raises: |
1120 | + """ |
1121 | + pass |
1122 | + |
1123 | + @abstractmethod |
1124 | + def get_all_ports(self, args): |
1125 | + """ |
1126 | + :returns: |
1127 | + :raises: |
1128 | + """ |
1129 | + pass |
1130 | + |
1131 | + @abstractmethod |
1132 | + def create_port(self, args): |
1133 | + """ |
1134 | + :returns: |
1135 | + :raises: |
1136 | + """ |
1137 | + pass |
1138 | + |
1139 | + @abstractmethod |
1140 | + def delete_port(self, args): |
1141 | + """ |
1142 | + :returns: |
1143 | + :raises: |
1144 | + """ |
1145 | + pass |
1146 | + |
1147 | + @abstractmethod |
1148 | + def update_port(self, args): |
1149 | + """ |
1150 | + :returns: |
1151 | + :raises: |
1152 | + """ |
1153 | + pass |
1154 | + |
1155 | + @abstractmethod |
1156 | + def get_port_details(self, args): |
1157 | + """ |
1158 | + :returns: |
1159 | + :raises: |
1160 | + """ |
1161 | + pass |
1162 | + |
1163 | + @abstractmethod |
1164 | + def plug_interface(self, args): |
1165 | + """ |
1166 | + :returns: |
1167 | + :raises: |
1168 | + """ |
1169 | + pass |
1170 | + |
1171 | + @abstractmethod |
1172 | + def unplug_interface(self, args): |
1173 | + """ |
1174 | + :returns: |
1175 | + :raises: |
1176 | + """ |
1177 | + pass |
1178 | + |
1179 | + @classmethod |
1180 | + def __subclasshook__(cls, klass): |
1181 | + """ |
1182 | + The __subclasshook__ method is a class method |
1183 | + that will be called everytime a class is tested |
1184 | + using issubclass(klass, Plugin). |
1185 | + In that case, it will check that every method |
1186 | + marked with the abstractmethod decorator is |
1187 | + provided by the plugin class. |
1188 | + """ |
1189 | + if cls is QuantumPluginBase: |
1190 | + for method in cls.__abstractmethods__: |
1191 | + method_ok = False |
1192 | + for base in klass.__mro__: |
1193 | + if method in base.__dict__: |
1194 | + fn_obj = base.__dict__[method] |
1195 | + if inspect.isfunction(fn_obj): |
1196 | + abstract_fn_obj = cls.__dict__[method] |
1197 | + arg_count = fn_obj.func_code.co_argcount |
1198 | + expected_arg_count = \ |
1199 | + abstract_fn_obj.func_code.co_argcount |
1200 | + method_ok = arg_count == expected_arg_count |
1201 | + if method_ok: |
1202 | + continue |
1203 | + return NotImplemented |
1204 | + return True |
1205 | + return NotImplemented |
1206 | |
1207 | === added file 'quantum/plugins/cisco/l2network_plugin.py' |
1208 | --- quantum/plugins/cisco/l2network_plugin.py 1970-01-01 00:00:00 +0000 |
1209 | +++ quantum/plugins/cisco/l2network_plugin.py 2011-08-15 17:33:23 +0000 |
1210 | @@ -0,0 +1,394 @@ |
1211 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
1212 | +# |
1213 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
1214 | +# |
1215 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
1216 | +# not use this file except in compliance with the License. You may obtain |
1217 | +# a copy of the License at |
1218 | +# |
1219 | +# http://www.apache.org/licenses/LICENSE-2.0 |
1220 | +# |
1221 | +# Unless required by applicable law or agreed to in writing, software |
1222 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
1223 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
1224 | +# License for the specific language governing permissions and limitations |
1225 | +# under the License. |
1226 | +# |
1227 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
1228 | +# |
1229 | + |
1230 | +import inspect |
1231 | +import logging as LOG |
1232 | + |
1233 | +from quantum.common import exceptions as exc |
1234 | +from quantum.common import utils |
1235 | +from quantum.quantum_plugin_base import QuantumPluginBase |
1236 | +from quantum.plugins.cisco import l2network_plugin_configuration as conf |
1237 | +from quantum.plugins.cisco.common import cisco_constants as const |
1238 | +from quantum.plugins.cisco.common import cisco_exceptions as cexc |
1239 | + |
1240 | +LOG.basicConfig(level=LOG.WARN) |
1241 | +LOG.getLogger(const.LOGGER_COMPONENT_NAME) |
1242 | + |
1243 | + |
1244 | +class L2Network(QuantumPluginBase): |
1245 | + _networks = {} |
1246 | + _tenants = {} |
1247 | + _portprofiles = {} |
1248 | + |
1249 | + def __init__(self): |
1250 | + self._net_counter = 0 |
1251 | + self._portprofile_counter = 0 |
1252 | + self._port_counter = 0 |
1253 | + self._vlan_counter = int(conf.VLAN_START) - 1 |
1254 | + self._model = utils.import_object(conf.MODEL_CLASS) |
1255 | + |
1256 | + """ |
1257 | + Core API implementation |
1258 | + """ |
1259 | + def get_all_networks(self, tenant_id): |
1260 | + """ |
1261 | + Returns a dictionary containing all |
1262 | + <network_uuid, network_name> for |
1263 | + the specified tenant. |
1264 | + """ |
1265 | + LOG.debug("get_all_networks() called\n") |
1266 | + self._invokeDevicePlugins(self._funcName(), [tenant_id]) |
1267 | + return self._networks.values() |
1268 | + |
1269 | + def create_network(self, tenant_id, net_name): |
1270 | + """ |
1271 | + Creates a new Virtual Network, and assigns it |
1272 | + a symbolic name. |
1273 | + """ |
1274 | + LOG.debug("create_network() called\n") |
1275 | + new_net_id = self._get_unique_net_id(tenant_id) |
1276 | + vlan_id = self._get_vlan_for_tenant(tenant_id, net_name) |
1277 | + vlan_name = self._get_vlan_name(new_net_id, str(vlan_id)) |
1278 | + self._invokeDevicePlugins(self._funcName(), [tenant_id, net_name, |
1279 | + new_net_id, vlan_name, |
1280 | + vlan_id]) |
1281 | + new_net_dict = {const.NET_ID: new_net_id, |
1282 | + const.NET_NAME: net_name, |
1283 | + const.NET_PORTS: {}, |
1284 | + const.NET_VLAN_NAME: vlan_name, |
1285 | + const.NET_VLAN_ID: vlan_id, |
1286 | + const.NET_TENANTS: [tenant_id]} |
1287 | + self._networks[new_net_id] = new_net_dict |
1288 | + tenant = self._get_tenant(tenant_id) |
1289 | + tenant_networks = tenant[const.TENANT_NETWORKS] |
1290 | + tenant_networks[new_net_id] = new_net_dict |
1291 | + return new_net_dict |
1292 | + |
1293 | + def delete_network(self, tenant_id, net_id): |
1294 | + """ |
1295 | + Deletes the network with the specified network identifier |
1296 | + belonging to the specified tenant. |
1297 | + """ |
1298 | + LOG.debug("delete_network() called\n") |
1299 | + net = self._networks.get(net_id) |
1300 | + if net: |
1301 | + if len(net[const.NET_PORTS].values()) > 0: |
1302 | + ports_on_net = net[const.NET_PORTS].values() |
1303 | + for port in ports_on_net: |
1304 | + if port[const.ATTACHMENT]: |
1305 | + raise exc.NetworkInUse(net_id=net_id) |
1306 | + for port in ports_on_net: |
1307 | + self.delete_port(tenant_id, net_id, port[const.PORT_ID]) |
1308 | + |
1309 | + self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id]) |
1310 | + self._networks.pop(net_id) |
1311 | + tenant = self._get_tenant(tenant_id) |
1312 | + tenant_networks = tenant[const.TENANT_NETWORKS] |
1313 | + tenant_networks.pop(net_id) |
1314 | + return net |
1315 | + # Network not found |
1316 | + raise exc.NetworkNotFound(net_id=net_id) |
1317 | + |
1318 | + def get_network_details(self, tenant_id, net_id): |
1319 | + """ |
1320 | + Gets the details of a particular network |
1321 | + """ |
1322 | + LOG.debug("get_network_details() called\n") |
1323 | + self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id]) |
1324 | + network = self._get_network(tenant_id, net_id) |
1325 | + ports_on_net = network[const.NET_PORTS].values() |
1326 | + return {const.NET_ID: network[const.NET_ID], |
1327 | + const.NET_NAME: network[const.NET_NAME], |
1328 | + const.NET_PORTS: ports_on_net} |
1329 | + |
1330 | + def rename_network(self, tenant_id, net_id, new_name): |
1331 | + """ |
1332 | + Updates the symbolic name belonging to a particular |
1333 | + Virtual Network. |
1334 | + """ |
1335 | + LOG.debug("rename_network() called\n") |
1336 | + self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id, |
1337 | + new_name]) |
1338 | + network = self._get_network(tenant_id, net_id) |
1339 | + network[const.NET_NAME] = new_name |
1340 | + return network |
1341 | + |
1342 | + def get_all_ports(self, tenant_id, net_id): |
1343 | + """ |
1344 | + Retrieves all port identifiers belonging to the |
1345 | + specified Virtual Network. |
1346 | + """ |
1347 | + LOG.debug("get_all_ports() called\n") |
1348 | + self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id]) |
1349 | + network = self._get_network(tenant_id, net_id) |
1350 | + ports_on_net = network[const.NET_PORTS].values() |
1351 | + return ports_on_net |
1352 | + |
1353 | + def create_port(self, tenant_id, net_id, port_state=None): |
1354 | + """ |
1355 | + Creates a port on the specified Virtual Network. |
1356 | + """ |
1357 | + LOG.debug("create_port() called\n") |
1358 | + net = self._get_network(tenant_id, net_id) |
1359 | + ports = net[const.NET_PORTS] |
1360 | + unique_port_id_string = self._get_unique_port_id(tenant_id, net_id) |
1361 | + self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id, |
1362 | + port_state, |
1363 | + unique_port_id_string]) |
1364 | + new_port_dict = {const.PORT_ID: unique_port_id_string, |
1365 | + const.PORT_STATE: const.PORT_UP, |
1366 | + const.ATTACHMENT: None} |
1367 | + ports[unique_port_id_string] = new_port_dict |
1368 | + return new_port_dict |
1369 | + |
1370 | + def delete_port(self, tenant_id, net_id, port_id): |
1371 | + """ |
1372 | + Deletes a port on a specified Virtual Network, |
1373 | + if the port contains a remote interface attachment, |
1374 | + the remote interface should first be un-plugged and |
1375 | + then the port can be deleted. |
1376 | + """ |
1377 | + LOG.debug("delete_port() called\n") |
1378 | + port = self._get_port(tenant_id, net_id, port_id) |
1379 | + if port[const.ATTACHMENT]: |
1380 | + raise exc.PortInUse(net_id=net_id, port_id=port_id, |
1381 | + att_id=port[const.ATTACHMENT]) |
1382 | + try: |
1383 | + #TODO (Sumit): Before deleting port profile make sure that there |
1384 | + # is no VM using this port profile |
1385 | + self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id, |
1386 | + port_id]) |
1387 | + net = self._get_network(tenant_id, net_id) |
1388 | + net[const.NET_PORTS].pop(port_id) |
1389 | + except KeyError: |
1390 | + raise exc.PortNotFound(net_id=net_id, port_id=port_id) |
1391 | + |
1392 | + def update_port(self, tenant_id, net_id, port_id, port_state): |
1393 | + """ |
1394 | + Updates the state of a port on the specified Virtual Network. |
1395 | + """ |
1396 | + LOG.debug("update_port() called\n") |
1397 | + self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id, |
1398 | + port_id, port_state]) |
1399 | + port = self._get_port(tenant_id, net_id, port_id) |
1400 | + self._validate_port_state(port_state) |
1401 | + port[const.PORT_STATE] = port_state |
1402 | + return port |
1403 | + |
1404 | + def get_port_details(self, tenant_id, net_id, port_id): |
1405 | + """ |
1406 | + This method allows the user to retrieve a remote interface |
1407 | + that is attached to this particular port. |
1408 | + """ |
1409 | + LOG.debug("get_port_details() called\n") |
1410 | + self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id, |
1411 | + port_id]) |
1412 | + return self._get_port(tenant_id, net_id, port_id) |
1413 | + |
1414 | + def plug_interface(self, tenant_id, net_id, port_id, |
1415 | + remote_interface_id): |
1416 | + """ |
1417 | + Attaches a remote interface to the specified port on the |
1418 | + specified Virtual Network. |
1419 | + """ |
1420 | + LOG.debug("plug_interface() called\n") |
1421 | + self._validate_attachment(tenant_id, net_id, port_id, |
1422 | + remote_interface_id) |
1423 | + port = self._get_port(tenant_id, net_id, port_id) |
1424 | + if port[const.ATTACHMENT]: |
1425 | + raise exc.PortInUse(net_id=net_id, port_id=port_id, |
1426 | + att_id=port[const.ATTACHMENT]) |
1427 | + self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id, |
1428 | + port_id, |
1429 | + remote_interface_id]) |
1430 | + port[const.ATTACHMENT] = remote_interface_id |
1431 | + |
1432 | + def unplug_interface(self, tenant_id, net_id, port_id): |
1433 | + """ |
1434 | + Detaches a remote interface from the specified port on the |
1435 | + specified Virtual Network. |
1436 | + """ |
1437 | + LOG.debug("unplug_interface() called\n") |
1438 | + port = self._get_port(tenant_id, net_id, port_id) |
1439 | + self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id, |
1440 | + port_id]) |
1441 | + port[const.ATTACHMENT] = None |
1442 | + |
1443 | + """ |
1444 | + Extension API implementation |
1445 | + """ |
1446 | + def get_all_portprofiles(self, tenant_id): |
1447 | + return self._portprofiles.values() |
1448 | + |
1449 | + def get_portprofile_details(self, tenant_id, profile_id): |
1450 | + return self._get_portprofile(tenant_id, profile_id) |
1451 | + |
1452 | + def create_portprofile(self, tenant_id, profile_name, vlan_id): |
1453 | + profile_id = self._get_unique_profile_id(tenant_id) |
1454 | + new_port_profile_dict = {const.PROFILE_ID: profile_id, |
1455 | + const.PROFILE_NAME: profile_name, |
1456 | + const.PROFILE_ASSOCIATIONS: [], |
1457 | + const.PROFILE_VLAN_ID: vlan_id, |
1458 | + const.PROFILE_QOS: None} |
1459 | + self._portprofiles[profile_id] = new_port_profile_dict |
1460 | + tenant = self._get_tenant(tenant_id) |
1461 | + portprofiles = tenant[const.TENANT_PORTPROFILES] |
1462 | + portprofiles[profile_id] = new_port_profile_dict |
1463 | + return new_port_profile_dict |
1464 | + |
1465 | + def delete_portprofile(self, tenant_id, profile_id): |
1466 | + portprofile = self._get_portprofile(tenant_id, profile_id) |
1467 | + associations = portprofile[const.PROFILE_ASSOCIATIONS] |
1468 | + if len(associations) > 0: |
1469 | + raise cexc.PortProfileInvalidDelete(tenant_id=tenant_id, |
1470 | + profile_id=profile_id) |
1471 | + else: |
1472 | + self._portprofiles.pop(profile_id) |
1473 | + tenant = self._get_tenant(tenant_id) |
1474 | + tenant[const.TENANT_PORTPROFILES].pop(profile_id) |
1475 | + |
1476 | + def rename_portprofile(self, tenant_id, profile_id, new_name): |
1477 | + portprofile = self._get_portprofile(tenant_id, profile_id) |
1478 | + portprofile[const.PROFILE_NAME] = new_name |
1479 | + return portprofile |
1480 | + |
1481 | + def associate_portprofile(self, tenant_id, net_id, |
1482 | + port_id, portprofile_id): |
1483 | + portprofile = self._get_portprofile(tenant_id, portprofile_id) |
1484 | + associations = portprofile[const.PROFILE_ASSOCIATIONS] |
1485 | + associations.append(port_id) |
1486 | + |
1487 | + def disassociate_portprofile(self, tenant_id, net_id, |
1488 | + port_id, portprofile_id): |
1489 | + portprofile = self._get_portprofile(tenant_id, portprofile_id) |
1490 | + associations = portprofile[const.PROFILE_ASSOCIATIONS] |
1491 | + associations.remove(port_id) |
1492 | + |
1493 | + def create_defaultPProfile(self, tenant_id, network_id, profile_name, |
1494 | + vlan_id): |
1495 | + pass |
1496 | + |
1497 | + """ |
1498 | + Private functions |
1499 | + """ |
1500 | + def _invokeDevicePlugins(self, function_name, args): |
1501 | + """ |
1502 | + All device-specific calls are delegate to the model |
1503 | + """ |
1504 | + getattr(self._model, function_name)(args) |
1505 | + |
1506 | + def _get_vlan_for_tenant(self, tenant_id, net_name): |
1507 | + # TODO (Sumit): |
1508 | + # The VLAN ID for a tenant might need to be obtained from |
1509 | + # somewhere (from Donabe/Melange?) |
1510 | + # Also need to make sure that the VLAN ID is not being used already |
1511 | + # Currently, just a wrap-around counter ranging from VLAN_START to |
1512 | + # VLAN_END |
1513 | + self._vlan_counter += 1 |
1514 | + self._vlan_counter %= int(conf.VLAN_END) |
1515 | + if self._vlan_counter < int(conf.VLAN_START): |
1516 | + self._vlan_counter = int(conf.VLAN_START) |
1517 | + return self._vlan_counter |
1518 | + |
1519 | + def _get_vlan_name(self, net_id, vlan): |
1520 | + vlan_name = conf.VLAN_NAME_PREFIX + net_id + "-" + vlan |
1521 | + return vlan_name |
1522 | + |
1523 | + def _validate_port_state(self, port_state): |
1524 | + if port_state.upper() not in (const.PORT_UP, const.PORT_DOWN): |
1525 | + raise exc.StateInvalid(port_state=port_state) |
1526 | + return True |
1527 | + |
1528 | + def _validate_attachment(self, tenant_id, network_id, port_id, |
1529 | + remote_interface_id): |
1530 | + network = self._get_network(tenant_id, network_id) |
1531 | + for port in network[const.NET_PORTS].values(): |
1532 | + if port[const.ATTACHMENT] == remote_interface_id: |
1533 | + raise exc.AlreadyAttached(net_id=network_id, |
1534 | + port_id=port_id, |
1535 | + att_id=port[const.ATTACHMENT], |
1536 | + att_port_id=port[const.PORT_ID]) |
1537 | + |
1538 | + def _get_network(self, tenant_id, network_id): |
1539 | + network = self._networks.get(network_id) |
1540 | + if not network: |
1541 | + raise exc.NetworkNotFound(net_id=network_id) |
1542 | + return network |
1543 | + |
1544 | + def _get_tenant(self, tenant_id): |
1545 | + tenant = self._tenants.get(tenant_id) |
1546 | + if not tenant: |
1547 | + LOG.debug("Creating new tenant record with tenant id %s\n" % |
1548 | + tenant_id) |
1549 | + tenant = {const.TENANT_ID: tenant_id, |
1550 | + const.TENANT_NAME: tenant_id, |
1551 | + const.TENANT_NETWORKS: {}, |
1552 | + const.TENANT_PORTPROFILES: {}} |
1553 | + self._tenants[tenant_id] = tenant |
1554 | + return tenant |
1555 | + |
1556 | + def _get_port(self, tenant_id, network_id, port_id): |
1557 | + net = self._get_network(tenant_id, network_id) |
1558 | + port = net[const.NET_PORTS].get(port_id) |
1559 | + if not port: |
1560 | + raise exc.PortNotFound(net_id=network_id, port_id=port_id) |
1561 | + return port |
1562 | + |
1563 | + def _get_portprofile(self, tenant_id, portprofile_id): |
1564 | + portprofile = self._portprofiles.get(portprofile_id) |
1565 | + if not portprofile: |
1566 | + raise cexc.PortProfileNotFound(tenant_id=tenant_id, |
1567 | + portprofile_id=portprofile_id) |
1568 | + return portprofile |
1569 | + |
1570 | + def _get_unique_net_id(self, tenant_id): |
1571 | + self._net_counter += 1 |
1572 | + self._net_counter %= int(conf.MAX_NETWORKS) |
1573 | + id = tenant_id[:3] + \ |
1574 | + "-n-" + ("0" * (6 - len(str(self._net_counter)))) + \ |
1575 | + str(self._net_counter) |
1576 | + # TODO (Sumit): Need to check if the ID has already been allocated |
1577 | + # ID will be generated by DB |
1578 | + return id |
1579 | + |
1580 | + def _get_unique_port_id(self, tenant_id, net_id): |
1581 | + self._port_counter += 1 |
1582 | + self._port_counter %= int(conf.MAX_PORTS) |
1583 | + id = net_id + "-p-" + str(self._port_counter) |
1584 | + # TODO (Sumit): Need to check if the ID has already been allocated |
1585 | + # ID will be generated by DB |
1586 | + return id |
1587 | + |
1588 | + def _get_unique_profile_id(self, tenant_id): |
1589 | + self._portprofile_counter += 1 |
1590 | + self._portprofile_counter %= int(conf.MAX_PORT_PROFILES) |
1591 | + id = tenant_id[:3] + "-pp-" + \ |
1592 | + ("0" * (6 - len(str(self._net_counter)))) \ |
1593 | + + str(self._portprofile_counter) |
1594 | + # TODO (Sumit): Need to check if the ID has already been allocated |
1595 | + # ID will be generated by DB |
1596 | + return id |
1597 | + |
1598 | + def _funcName(self, offset=0): |
1599 | + return inspect.stack()[1 + offset][3] |
1600 | + |
1601 | +""" |
1602 | +TODO (Sumit): |
1603 | +(1) Persistent storage |
1604 | +""" |
1605 | |
1606 | === added file 'quantum/plugins/cisco/l2network_plugin_configuration.py' |
1607 | --- quantum/plugins/cisco/l2network_plugin_configuration.py 1970-01-01 00:00:00 +0000 |
1608 | +++ quantum/plugins/cisco/l2network_plugin_configuration.py 2011-08-15 17:33:23 +0000 |
1609 | @@ -0,0 +1,50 @@ |
1610 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
1611 | +# |
1612 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
1613 | +# |
1614 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
1615 | +# not use this file except in compliance with the License. You may obtain |
1616 | +# a copy of the License at |
1617 | +# |
1618 | +# http://www.apache.org/licenses/LICENSE-2.0 |
1619 | +# |
1620 | +# Unless required by applicable law or agreed to in writing, software |
1621 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
1622 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
1623 | +# License for the specific language governing permissions and limitations |
1624 | +# under the License. |
1625 | +# |
1626 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
1627 | +# |
1628 | + |
1629 | +import os |
1630 | + |
1631 | +from quantum.plugins.cisco.common import cisco_configparser as confp |
1632 | + |
1633 | +CONF_FILE = "conf/l2network_plugin.ini" |
1634 | + |
1635 | +cp = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \ |
1636 | + + "/" + CONF_FILE) |
1637 | + |
1638 | +section = cp['VLANS'] |
1639 | +VLAN_NAME_PREFIX = section['vlan_name_prefix'] |
1640 | +VLAN_START = section['vlan_start'] |
1641 | +VLAN_END = section['vlan_end'] |
1642 | + |
1643 | +section = cp['PORTS'] |
1644 | +MAX_PORTS = section['max_ports'] |
1645 | + |
1646 | +section = cp['PORTPROFILES'] |
1647 | +MAX_PORT_PROFILES = section['max_port_profiles'] |
1648 | + |
1649 | +section = cp['NETWORKS'] |
1650 | +MAX_NETWORKS = section['max_networks'] |
1651 | + |
1652 | +section = cp['MODEL'] |
1653 | +MODEL_CLASS = section['model_class'] |
1654 | + |
1655 | +CONF_FILE = "conf/plugins.ini" |
1656 | + |
1657 | +cp = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \ |
1658 | + + "/" + CONF_FILE) |
1659 | +plugins = cp.walk(cp.dummy) |
1660 | |
1661 | === added directory 'quantum/plugins/cisco/nexus' |
1662 | === added file 'quantum/plugins/cisco/nexus/__init__.py' |
1663 | --- quantum/plugins/cisco/nexus/__init__.py 1970-01-01 00:00:00 +0000 |
1664 | +++ quantum/plugins/cisco/nexus/__init__.py 2011-08-15 17:33:23 +0000 |
1665 | @@ -0,0 +1,18 @@ |
1666 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
1667 | +# |
1668 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
1669 | +# |
1670 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
1671 | +# not use this file except in compliance with the License. You may obtain |
1672 | +# a copy of the License at |
1673 | +# |
1674 | +# http://www.apache.org/licenses/LICENSE-2.0 |
1675 | +# |
1676 | +# Unless required by applicable law or agreed to in writing, software |
1677 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
1678 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
1679 | +# License for the specific language governing permissions and limitations |
1680 | +# under the License. |
1681 | +# |
1682 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
1683 | +# |
1684 | |
1685 | === added file 'quantum/plugins/cisco/nexus/cisco_nexus_configuration.py' |
1686 | --- quantum/plugins/cisco/nexus/cisco_nexus_configuration.py 1970-01-01 00:00:00 +0000 |
1687 | +++ quantum/plugins/cisco/nexus/cisco_nexus_configuration.py 2011-08-15 17:33:23 +0000 |
1688 | @@ -0,0 +1,35 @@ |
1689 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
1690 | +# |
1691 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
1692 | +# |
1693 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
1694 | +# not use this file except in compliance with the License. You may obtain |
1695 | +# a copy of the License at |
1696 | +# |
1697 | +# http://www.apache.org/licenses/LICENSE-2.0 |
1698 | +# |
1699 | +# Unless required by applicable law or agreed to in writing, software |
1700 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
1701 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
1702 | +# License for the specific language governing permissions and limitations |
1703 | +# under the License. |
1704 | +# |
1705 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
1706 | +# @author: Edgar Magana, Cisco Systems, Inc. |
1707 | +# |
1708 | + |
1709 | +import os |
1710 | + |
1711 | +from quantum.plugins.cisco.common import cisco_configparser as confp |
1712 | + |
1713 | +CONF_FILE = "../conf/nexus.ini" |
1714 | + |
1715 | +cp = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \ |
1716 | + + "/" + CONF_FILE) |
1717 | + |
1718 | +section = cp['SWITCH'] |
1719 | +NEXUS_IP_ADDRESS = section['nexus_ip_address'] |
1720 | +NEXUS_PORT = section['nexus_port'] |
1721 | + |
1722 | +section = cp['DRIVER'] |
1723 | +NEXUS_DRIVER = section['name'] |
1724 | |
1725 | === added file 'quantum/plugins/cisco/nexus/cisco_nexus_network_driver.py' |
1726 | --- quantum/plugins/cisco/nexus/cisco_nexus_network_driver.py 1970-01-01 00:00:00 +0000 |
1727 | +++ quantum/plugins/cisco/nexus/cisco_nexus_network_driver.py 2011-08-15 17:33:23 +0000 |
1728 | @@ -0,0 +1,226 @@ |
1729 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
1730 | +# |
1731 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
1732 | +# |
1733 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
1734 | +# not use this file except in compliance with the License. You may obtain |
1735 | +# a copy of the License at |
1736 | +# |
1737 | +# http://www.apache.org/licenses/LICENSE-2.0 |
1738 | +# |
1739 | +# Unless required by applicable law or agreed to in writing, software |
1740 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
1741 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
1742 | +# License for the specific language governing permissions and limitations |
1743 | +# under the License. |
1744 | +# |
1745 | +# @author: Debojyoti Dutta, Cisco Systems, Inc. |
1746 | +# @author: Edgar Magana, Cisco Systems Inc. |
1747 | +# |
1748 | +""" |
1749 | +Implements a Nexus-OS NETCONF over SSHv2 API Client |
1750 | +""" |
1751 | + |
1752 | +import logging as LOG |
1753 | +import string |
1754 | +import subprocess |
1755 | + |
1756 | +from quantum.plugins.cisco.common import cisco_constants as const |
1757 | +from quantum.plugins.cisco.common import cisco_exceptions as cexc |
1758 | + |
1759 | +from ncclient import manager |
1760 | + |
1761 | +LOG.basicConfig(level=LOG.WARN) |
1762 | +LOG.getLogger(const.LOGGER_COMPONENT_NAME) |
1763 | + |
1764 | + |
1765 | +# The following are standard strings, messages used to communicate with Nexus, |
1766 | +#only place holder values change for each message |
1767 | +exec_conf_prefix = """ |
1768 | + <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0"> |
1769 | + <configure xmlns="http://www.cisco.com/nxos:1.0:vlan_mgr_cli"> |
1770 | + <__XML__MODE__exec_configure> |
1771 | +""" |
1772 | + |
1773 | + |
1774 | +exec_conf_postfix = """ |
1775 | + </__XML__MODE__exec_configure> |
1776 | + </configure> |
1777 | + </config> |
1778 | +""" |
1779 | + |
1780 | + |
1781 | +cmd_vlan_conf_snippet = """ |
1782 | + <vlan> |
1783 | + <vlan-id-create-delete> |
1784 | + <__XML__PARAM_value>%s</__XML__PARAM_value> |
1785 | + <__XML__MODE_vlan> |
1786 | + <name> |
1787 | + <vlan-name>%s</vlan-name> |
1788 | + </name> |
1789 | + <state> |
1790 | + <vstate>active</vstate> |
1791 | + </state> |
1792 | + <no> |
1793 | + <shutdown/> |
1794 | + </no> |
1795 | + </__XML__MODE_vlan> |
1796 | + </vlan-id-create-delete> |
1797 | + </vlan> |
1798 | +""" |
1799 | + |
1800 | +cmd_no_vlan_conf_snippet = """ |
1801 | + <no> |
1802 | + <vlan> |
1803 | + <vlan-id-create-delete> |
1804 | + <__XML__PARAM_value>%s</__XML__PARAM_value> |
1805 | + </vlan-id-create-delete> |
1806 | + </vlan> |
1807 | + </no> |
1808 | +""" |
1809 | + |
1810 | +cmd_vlan_int_snippet = """ |
1811 | + <interface> |
1812 | + <ethernet> |
1813 | + <interface>%s</interface> |
1814 | + <__XML__MODE_if-ethernet-switch> |
1815 | + <switchport></switchport> |
1816 | + <switchport> |
1817 | + <trunk> |
1818 | + <allowed> |
1819 | + <vlan> |
1820 | + <__XML__BLK_Cmd_switchport_trunk_allowed_allow-vlans> |
1821 | + <allow-vlans>%s</allow-vlans> |
1822 | + </__XML__BLK_Cmd_switchport_trunk_allowed_allow-vlans> |
1823 | + </vlan> |
1824 | + </allowed> |
1825 | + </trunk> |
1826 | + </switchport> |
1827 | + </__XML__MODE_if-ethernet-switch> |
1828 | + </ethernet> |
1829 | + </interface> |
1830 | +""" |
1831 | + |
1832 | +cmd_port_trunk = """ |
1833 | + <interface> |
1834 | + <ethernet> |
1835 | + <interface>%s</interface> |
1836 | + <__XML__MODE_if-ethernet-switch> |
1837 | + <switchport></switchport> |
1838 | + <switchport> |
1839 | + <mode> |
1840 | + <trunk> |
1841 | + </trunk> |
1842 | + </mode> |
1843 | + </switchport> |
1844 | + </__XML__MODE_if-ethernet-switch> |
1845 | + </ethernet> |
1846 | + </interface> |
1847 | +""" |
1848 | + |
1849 | +cmd_no_switchport = """ |
1850 | + <interface> |
1851 | + <ethernet> |
1852 | + <interface>%s</interface> |
1853 | + <__XML__MODE_if-ethernet-switch> |
1854 | + <no> |
1855 | + <switchport> |
1856 | + </switchport> |
1857 | + </no> |
1858 | + </__XML__MODE_if-ethernet-switch> |
1859 | + </ethernet> |
1860 | + </interface> |
1861 | +""" |
1862 | + |
1863 | + |
1864 | +cmd_no_vlan_int_snippet = """ |
1865 | + <interface> |
1866 | + <ethernet> |
1867 | + <interface>%s</interface> |
1868 | + <__XML__MODE_if-ethernet-switch> |
1869 | + <switchport></switchport> |
1870 | + <no> |
1871 | + <switchport> |
1872 | + <trunk> |
1873 | + <allowed> |
1874 | + <vlan> |
1875 | + <__XML__BLK_Cmd_switchport_trunk_allowed_allow-vlans> |
1876 | + <allow-vlans>%s</allow-vlans> |
1877 | + </__XML__BLK_Cmd_switchport_trunk_allowed_allow-vlans> |
1878 | + </vlan> |
1879 | + </allowed> |
1880 | + </trunk> |
1881 | + </switchport> |
1882 | + </no> |
1883 | + </__XML__MODE_if-ethernet-switch> |
1884 | + </ethernet> |
1885 | + </interface> |
1886 | +""" |
1887 | + |
1888 | + |
1889 | +filter_show_vlan_brief_snippet = """ |
1890 | + <show xmlns="http://www.cisco.com/nxos:1.0:vlan_mgr_cli"> |
1891 | + <vlan> |
1892 | + <brief/> |
1893 | + </vlan> |
1894 | + </show> """ |
1895 | + |
1896 | + |
1897 | +class CiscoNEXUSDriver(): |
1898 | + |
1899 | + def __init__(self): |
1900 | + pass |
1901 | + |
1902 | + def nxos_connect(self, nexus_host, port, nexus_user, nexus_password): |
1903 | + m = manager.connect(host=nexus_host, port=22, username=nexus_user, |
1904 | + password=nexus_password) |
1905 | + return m |
1906 | + |
1907 | + def enable_vlan(self, mgr, vlanid, vlanname): |
1908 | + confstr = cmd_vlan_conf_snippet % (vlanid, vlanname) |
1909 | + confstr = exec_conf_prefix + confstr + exec_conf_postfix |
1910 | + mgr.edit_config(target='running', config=confstr) |
1911 | + |
1912 | + def disable_vlan(self, mgr, vlanid): |
1913 | + confstr = cmd_no_vlan_conf_snippet % vlanid |
1914 | + confstr = exec_conf_prefix + confstr + exec_conf_postfix |
1915 | + mgr.edit_config(target='running', config=confstr) |
1916 | + |
1917 | + def enable_port_trunk(self, mgr, interface): |
1918 | + confstr = cmd_port_trunk % (interface) |
1919 | + confstr = exec_conf_prefix + confstr + exec_conf_postfix |
1920 | + print confstr |
1921 | + mgr.edit_config(target='running', config=confstr) |
1922 | + |
1923 | + def disable_switch_port(self, mgr, interface): |
1924 | + confstr = cmd_no_switchport % (interface) |
1925 | + confstr = exec_conf_prefix + confstr + exec_conf_postfix |
1926 | + print confstr |
1927 | + mgr.edit_config(target='running', config=confstr) |
1928 | + |
1929 | + def enable_vlan_on_trunk_int(self, mgr, interface, vlanid): |
1930 | + confstr = cmd_vlan_int_snippet % (interface, vlanid) |
1931 | + confstr = exec_conf_prefix + confstr + exec_conf_postfix |
1932 | + print confstr |
1933 | + mgr.edit_config(target='running', config=confstr) |
1934 | + |
1935 | + def disable_vlan_on_trunk_int(self, mgr, interface, vlanid): |
1936 | + confstr = cmd_no_vlan_int_snippet % (interface, vlanid) |
1937 | + confstr = exec_conf_prefix + confstr + exec_conf_postfix |
1938 | + print confstr |
1939 | + mgr.edit_config(target='running', config=confstr) |
1940 | + |
1941 | + def create_vlan(self, vlan_name, vlan_id, nexus_host, nexus_user, |
1942 | + nexus_password, nexus_interface): |
1943 | + #TODO (Edgar) Move the SSH port to the configuration file |
1944 | + with self.nxos_connect(nexus_host, 22, nexus_user, |
1945 | + nexus_password) as m: |
1946 | + self.enable_vlan(m, vlan_id, vlan_name) |
1947 | + self.enable_vlan_on_trunk_int(m, nexus_interface, vlan_id) |
1948 | + |
1949 | + def delete_vlan(self, vlan_id, nexus_host, nexus_user, |
1950 | + nexus_password, nexus_interface): |
1951 | + with self.nxos_connect(nexus_host, 22, nexus_user, |
1952 | + nexus_password) as m: |
1953 | + self.disable_vlan(m, vlan_id) |
1954 | + self.disable_switch_port(m, nexus_interface) |
1955 | |
1956 | === added file 'quantum/plugins/cisco/nexus/cisco_nexus_plugin.py' |
1957 | --- quantum/plugins/cisco/nexus/cisco_nexus_plugin.py 1970-01-01 00:00:00 +0000 |
1958 | +++ quantum/plugins/cisco/nexus/cisco_nexus_plugin.py 2011-08-15 17:33:23 +0000 |
1959 | @@ -0,0 +1,168 @@ |
1960 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
1961 | +# |
1962 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
1963 | +# |
1964 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
1965 | +# not use this file except in compliance with the License. You may obtain |
1966 | +# a copy of the License at |
1967 | +# |
1968 | +# http://www.apache.org/licenses/LICENSE-2.0 |
1969 | +# |
1970 | +# Unless required by applicable law or agreed to in writing, software |
1971 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
1972 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
1973 | +# License for the specific language governing permissions and limitations |
1974 | +# under the License. |
1975 | +# |
1976 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
1977 | +# @author: Edgar Magana, Cisco Systems, Inc. |
1978 | +# |
1979 | +import logging as LOG |
1980 | + |
1981 | +from quantum.common import exceptions as exc |
1982 | +from quantum.common import utils |
1983 | +from quantum.plugins.cisco.common import cisco_constants as const |
1984 | +from quantum.plugins.cisco.common import cisco_credentials as cred |
1985 | +from quantum.plugins.cisco.common import cisco_exceptions as cexc |
1986 | +from quantum.plugins.cisco.common import cisco_utils as cutil |
1987 | +from quantum.plugins.cisco.l2device_plugin_base import L2DevicePluginBase |
1988 | +from quantum.plugins.cisco.nexus import cisco_nexus_configuration as conf |
1989 | + |
1990 | +LOG.basicConfig(level=LOG.WARN) |
1991 | +LOG.getLogger(const.LOGGER_COMPONENT_NAME) |
1992 | + |
1993 | + |
1994 | +class NexusPlugin(L2DevicePluginBase): |
1995 | + _networks = {} |
1996 | + |
1997 | + def __init__(self): |
1998 | + self._client = utils.import_object(conf.NEXUS_DRIVER) |
1999 | + LOG.debug("Loaded driver %s\n" % conf.NEXUS_DRIVER) |
2000 | + #TODO (Edgar) Using just one Nexus 7K Switch and Port |
2001 | + self._nexus_ip = conf.NEXUS_IP_ADDRESS |
2002 | + self._nexus_username = cred.Store.getUsername(conf.NEXUS_IP_ADDRESS) |
2003 | + self._nexus_password = cred.Store.getPassword(conf.NEXUS_IP_ADDRESS) |
2004 | + self._nexus_port = conf.NEXUS_PORT |
2005 | + |
2006 | + def get_all_networks(self, tenant_id): |
2007 | + """ |
2008 | + Returns a dictionary containing all |
2009 | + <network_uuid, network_name> for |
2010 | + the specified tenant. |
2011 | + """ |
2012 | + LOG.debug("NexusPlugin:get_all_networks() called\n") |
2013 | + return self._networks.values() |
2014 | + |
2015 | + def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id, |
2016 | + **kwargs): |
2017 | + """ |
2018 | + Create a VLAN in the switch, and configure the appropriate interfaces |
2019 | + for this VLAN |
2020 | + """ |
2021 | + LOG.debug("NexusPlugin:create_network() called\n") |
2022 | + self._client.create_vlan(vlan_name, str(vlan_id), self._nexus_ip, |
2023 | + self._nexus_username, self._nexus_password, self._nexus_port) |
2024 | + |
2025 | + new_net_dict = {const.NET_ID: net_id, |
2026 | + const.NET_NAME: net_name, |
2027 | + const.NET_PORTS: {}, |
2028 | + const.NET_VLAN_NAME: vlan_name, |
2029 | + const.NET_VLAN_ID: vlan_id} |
2030 | + self._networks[net_id] = new_net_dict |
2031 | + return new_net_dict |
2032 | + |
2033 | + def delete_network(self, tenant_id, net_id, **kwargs): |
2034 | + """ |
2035 | + Deletes a VLAN in the switch, and removes the VLAN configuration |
2036 | + from the relevant interfaces |
2037 | + """ |
2038 | + LOG.debug("NexusPlugin:delete_network() called\n") |
2039 | + net = self._networks.get(net_id) |
2040 | + vlan_id = self._get_vlan_id_for_network(tenant_id, net_id) |
2041 | + if net: |
2042 | + self._client.delete_vlan(str(vlan_id), self._nexus_ip, |
2043 | + self._nexus_username, self._nexus_password, self._nexus_port) |
2044 | + self._networks.pop(net_id) |
2045 | + return net |
2046 | + # Network not found |
2047 | + raise exc.NetworkNotFound(net_id=net_id) |
2048 | + |
2049 | + def get_network_details(self, tenant_id, net_id, **kwargs): |
2050 | + """ |
2051 | + Returns the details of a particular network |
2052 | + """ |
2053 | + LOG.debug("NexusPlugin:get_network_details() called\n") |
2054 | + network = self._get_network(tenant_id, net_id) |
2055 | + return network |
2056 | + |
2057 | + def rename_network(self, tenant_id, net_id, new_name, **kwargs): |
2058 | + """ |
2059 | + Updates the symbolic name belonging to a particular |
2060 | + Virtual Network. |
2061 | + """ |
2062 | + #TODO (Edgar) We need to add an update method in the Nexus Driver |
2063 | + LOG.debug("NexusPlugin:rename_network() called\n") |
2064 | + network = self._get_network(tenant_id, net_id) |
2065 | + network[const.NET_NAME] = new_name |
2066 | + return network |
2067 | + |
2068 | + def get_all_ports(self, tenant_id, net_id, **kwargs): |
2069 | + """ |
2070 | + This is probably not applicable to the Nexus plugin. |
2071 | + Delete if not required. |
2072 | + """ |
2073 | + LOG.debug("NexusPlugin:get_all_ports() called\n") |
2074 | + |
2075 | + def create_port(self, tenant_id, net_id, port_state, port_id, **kwargs): |
2076 | + """ |
2077 | + This is probably not applicable to the Nexus plugin. |
2078 | + Delete if not required. |
2079 | + """ |
2080 | + LOG.debug("NexusPlugin:create_port() called\n") |
2081 | + |
2082 | + def delete_port(self, tenant_id, net_id, port_id, **kwargs): |
2083 | + """ |
2084 | + This is probably not applicable to the Nexus plugin. |
2085 | + Delete if not required. |
2086 | + """ |
2087 | + LOG.debug("NexusPlugin:delete_port() called\n") |
2088 | + |
2089 | + def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs): |
2090 | + """ |
2091 | + This is probably not applicable to the Nexus plugin. |
2092 | + Delete if not required. |
2093 | + """ |
2094 | + LOG.debug("NexusPlugin:update_port() called\n") |
2095 | + |
2096 | + def get_port_details(self, tenant_id, net_id, port_id, **kwargs): |
2097 | + """ |
2098 | + This is probably not applicable to the Nexus plugin. |
2099 | + Delete if not required. |
2100 | + """ |
2101 | + LOG.debug("NexusPlugin:get_port_details() called\n") |
2102 | + |
2103 | + def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id, |
2104 | + **kwargs): |
2105 | + """ |
2106 | + This is probably not applicable to the Nexus plugin. |
2107 | + Delete if not required. |
2108 | + """ |
2109 | + LOG.debug("NexusPlugin:plug_interface() called\n") |
2110 | + |
2111 | + def unplug_interface(self, tenant_id, net_id, port_id, **kwargs): |
2112 | + """ |
2113 | + This is probably not applicable to the Nexus plugin. |
2114 | + Delete if not required. |
2115 | + """ |
2116 | + LOG.debug("NexusPlugin:unplug_interface() called\n") |
2117 | + |
2118 | + def _get_vlan_id_for_network(self, tenant_id, network_id): |
2119 | + net = self._get_network(tenant_id, network_id) |
2120 | + vlan_id = net[const.NET_VLAN_ID] |
2121 | + return vlan_id |
2122 | + |
2123 | + def _get_network(self, tenant_id, network_id): |
2124 | + network = self._networks.get(network_id) |
2125 | + if not network: |
2126 | + raise exc.NetworkNotFound(net_id=network_id) |
2127 | + return network |
2128 | |
2129 | === added file 'quantum/plugins/cisco/run_tests.py' |
2130 | --- quantum/plugins/cisco/run_tests.py 1970-01-01 00:00:00 +0000 |
2131 | +++ quantum/plugins/cisco/run_tests.py 2011-08-15 17:33:23 +0000 |
2132 | @@ -0,0 +1,301 @@ |
2133 | +#!/usr/bin/env python |
2134 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
2135 | + |
2136 | +# Copyright 2010 OpenStack, LLC |
2137 | +# All Rights Reserved. |
2138 | +# |
2139 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
2140 | +# you may not use this file except in compliance with the License. |
2141 | +# You may obtain a copy of the License at |
2142 | +# |
2143 | +# http://www.apache.org/licenses/LICENSE-2.0 |
2144 | +# |
2145 | +# Unless required by applicable law or agreed to in writing, software |
2146 | +# distributed under the License is distributed on an "AS IS" BASIS, |
2147 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
2148 | +# See the License for the specific language governing permissions and |
2149 | +# limitations under the License. |
2150 | + |
2151 | +# Colorizer Code is borrowed from Twisted: |
2152 | +# Copyright (c) 2001-2010 Twisted Matrix Laboratories. |
2153 | +# |
2154 | +# Permission is hereby granted, free of charge, to any person obtaining |
2155 | +# a copy of this software and associated documentation files (the |
2156 | +# "Software"), to deal in the Software without restriction, including |
2157 | +# without limitation the rights to use, copy, modify, merge, publish, |
2158 | +# distribute, sublicense, and/or sell copies of the Software, and to |
2159 | +# permit persons to whom the Software is furnished to do so, subject to |
2160 | +# the following conditions: |
2161 | +# |
2162 | +# The above copyright notice and this permission notice shall be |
2163 | +# included in all copies or substantial portions of the Software. |
2164 | +# |
2165 | +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
2166 | +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
2167 | +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
2168 | +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
2169 | +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
2170 | +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
2171 | +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
2172 | + |
2173 | +"""Unittest runner for quantum |
2174 | + |
2175 | +To run all test:: |
2176 | + python run_tests.py |
2177 | + |
2178 | +To run all unit tests:: |
2179 | + python run_tests.py unit |
2180 | + |
2181 | +To run all functional tests:: |
2182 | + python run_tests.py functional |
2183 | + |
2184 | +To run a single unit test:: |
2185 | + python run_tests.py unit.test_stores:TestSwiftBackend.test_get |
2186 | + |
2187 | +To run a single functional test:: |
2188 | + python run_tests.py functional.test_service:TestController.test_create |
2189 | + |
2190 | +To run a single unit test module:: |
2191 | + python run_tests.py unit.test_stores |
2192 | + |
2193 | +To run a single functional test module:: |
2194 | + python run_tests.py functional.test_stores |
2195 | +""" |
2196 | + |
2197 | +import gettext |
2198 | +import logging |
2199 | +import os |
2200 | +import unittest |
2201 | +import sys |
2202 | + |
2203 | +from nose import config |
2204 | +from nose import result |
2205 | +from nose import core |
2206 | + |
2207 | + |
2208 | +class _AnsiColorizer(object): |
2209 | + """ |
2210 | + A colorizer is an object that loosely wraps around a stream, allowing |
2211 | + callers to write text to the stream in a particular color. |
2212 | + |
2213 | + Colorizer classes must implement C{supported()} and C{write(text, color)}. |
2214 | + """ |
2215 | + _colors = dict(black=30, red=31, green=32, yellow=33, |
2216 | + blue=34, magenta=35, cyan=36, white=37) |
2217 | + |
2218 | + def __init__(self, stream): |
2219 | + self.stream = stream |
2220 | + |
2221 | + def supported(cls, stream=sys.stdout): |
2222 | + """ |
2223 | + A class method that returns True if the current platform supports |
2224 | + coloring terminal output using this method. Returns False otherwise. |
2225 | + """ |
2226 | + if not stream.isatty(): |
2227 | + return False # auto color only on TTYs |
2228 | + try: |
2229 | + import curses |
2230 | + except ImportError: |
2231 | + return False |
2232 | + else: |
2233 | + try: |
2234 | + try: |
2235 | + return curses.tigetnum("colors") > 2 |
2236 | + except curses.error: |
2237 | + curses.setupterm() |
2238 | + return curses.tigetnum("colors") > 2 |
2239 | + except: |
2240 | + raise |
2241 | + # guess false in case of error |
2242 | + return False |
2243 | + supported = classmethod(supported) |
2244 | + |
2245 | + def write(self, text, color): |
2246 | + """ |
2247 | + Write the given text to the stream in the given color. |
2248 | + |
2249 | + @param text: Text to be written to the stream. |
2250 | + |
2251 | + @param color: A string label for a color. e.g. 'red', 'white'. |
2252 | + """ |
2253 | + color = self._colors[color] |
2254 | + self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text)) |
2255 | + |
2256 | + |
2257 | +class _Win32Colorizer(object): |
2258 | + """ |
2259 | + See _AnsiColorizer docstring. |
2260 | + """ |
2261 | + def __init__(self, stream): |
2262 | + from win32console import GetStdHandle, STD_OUT_HANDLE, \ |
2263 | + FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \ |
2264 | + FOREGROUND_INTENSITY |
2265 | + red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN, |
2266 | + FOREGROUND_BLUE, FOREGROUND_INTENSITY) |
2267 | + self.stream = stream |
2268 | + self.screenBuffer = GetStdHandle(STD_OUT_HANDLE) |
2269 | + self._colors = { |
2270 | + 'normal': red | green | blue, |
2271 | + 'red': red | bold, |
2272 | + 'green': green | bold, |
2273 | + 'blue': blue | bold, |
2274 | + 'yellow': red | green | bold, |
2275 | + 'magenta': red | blue | bold, |
2276 | + 'cyan': green | blue | bold, |
2277 | + 'white': red | green | blue | bold} |
2278 | + |
2279 | + def supported(cls, stream=sys.stdout): |
2280 | + try: |
2281 | + import win32console |
2282 | + screenBuffer = win32console.GetStdHandle( |
2283 | + win32console.STD_OUT_HANDLE) |
2284 | + except ImportError: |
2285 | + return False |
2286 | + import pywintypes |
2287 | + try: |
2288 | + screenBuffer.SetConsoleTextAttribute( |
2289 | + win32console.FOREGROUND_RED | |
2290 | + win32console.FOREGROUND_GREEN | |
2291 | + win32console.FOREGROUND_BLUE) |
2292 | + except pywintypes.error: |
2293 | + return False |
2294 | + else: |
2295 | + return True |
2296 | + supported = classmethod(supported) |
2297 | + |
2298 | + def write(self, text, color): |
2299 | + color = self._colors[color] |
2300 | + self.screenBuffer.SetConsoleTextAttribute(color) |
2301 | + self.stream.write(text) |
2302 | + self.screenBuffer.SetConsoleTextAttribute(self._colors['normal']) |
2303 | + |
2304 | + |
2305 | +class _NullColorizer(object): |
2306 | + """ |
2307 | + See _AnsiColorizer docstring. |
2308 | + """ |
2309 | + def __init__(self, stream): |
2310 | + self.stream = stream |
2311 | + |
2312 | + def supported(cls, stream=sys.stdout): |
2313 | + return True |
2314 | + supported = classmethod(supported) |
2315 | + |
2316 | + def write(self, text, color): |
2317 | + self.stream.write(text) |
2318 | + |
2319 | + |
2320 | +class QuantumTestResult(result.TextTestResult): |
2321 | + def __init__(self, *args, **kw): |
2322 | + result.TextTestResult.__init__(self, *args, **kw) |
2323 | + self._last_case = None |
2324 | + self.colorizer = None |
2325 | + # NOTE(vish, tfukushima): reset stdout for the terminal check |
2326 | + stdout = sys.__stdout__ |
2327 | + for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]: |
2328 | + if colorizer.supported(): |
2329 | + self.colorizer = colorizer(self.stream) |
2330 | + break |
2331 | + sys.stdout = stdout |
2332 | + |
2333 | + def getDescription(self, test): |
2334 | + return str(test) |
2335 | + |
2336 | + # NOTE(vish, tfukushima): copied from unittest with edit to add color |
2337 | + def addSuccess(self, test): |
2338 | + unittest.TestResult.addSuccess(self, test) |
2339 | + if self.showAll: |
2340 | + self.colorizer.write("OK", 'green') |
2341 | + self.stream.writeln() |
2342 | + elif self.dots: |
2343 | + self.stream.write('.') |
2344 | + self.stream.flush() |
2345 | + |
2346 | + # NOTE(vish, tfukushima): copied from unittest with edit to add color |
2347 | + def addFailure(self, test, err): |
2348 | + unittest.TestResult.addFailure(self, test, err) |
2349 | + if self.showAll: |
2350 | + self.colorizer.write("FAIL", 'red') |
2351 | + self.stream.writeln() |
2352 | + elif self.dots: |
2353 | + self.stream.write('F') |
2354 | + self.stream.flush() |
2355 | + |
2356 | + # NOTE(vish, tfukushima): copied from unittest with edit to add color |
2357 | + def addError(self, test, err): |
2358 | + """Overrides normal addError to add support for errorClasses. |
2359 | + If the exception is a registered class, the error will be added |
2360 | + to the list for that class, not errors. |
2361 | + """ |
2362 | + stream = getattr(self, 'stream', None) |
2363 | + ec, ev, tb = err |
2364 | + try: |
2365 | + exc_info = self._exc_info_to_string(err, test) |
2366 | + except TypeError: |
2367 | + # This is for compatibility with Python 2.3. |
2368 | + exc_info = self._exc_info_to_string(err) |
2369 | + for cls, (storage, label, isfail) in self.errorClasses.items(): |
2370 | + if result.isclass(ec) and issubclass(ec, cls): |
2371 | + if isfail: |
2372 | + test.passwd = False |
2373 | + storage.append((test, exc_info)) |
2374 | + # Might get patched into a streamless result |
2375 | + if stream is not None: |
2376 | + if self.showAll: |
2377 | + message = [label] |
2378 | + detail = result._exception_details(err[1]) |
2379 | + if detail: |
2380 | + message.append(detail) |
2381 | + stream.writeln(": ".join(message)) |
2382 | + elif self.dots: |
2383 | + stream.write(label[:1]) |
2384 | + return |
2385 | + self.errors.append((test, exc_info)) |
2386 | + test.passed = False |
2387 | + if stream is not None: |
2388 | + if self.showAll: |
2389 | + self.colorizer.write("ERROR", 'red') |
2390 | + self.stream.writeln() |
2391 | + elif self.dots: |
2392 | + stream.write('E') |
2393 | + |
2394 | + def startTest(self, test): |
2395 | + unittest.TestResult.startTest(self, test) |
2396 | + current_case = test.test.__class__.__name__ |
2397 | + |
2398 | + if self.showAll: |
2399 | + if current_case != self._last_case: |
2400 | + self.stream.writeln(current_case) |
2401 | + self._last_case = current_case |
2402 | + |
2403 | + self.stream.write( |
2404 | + ' %s' % str(test.test._testMethodName).ljust(60)) |
2405 | + self.stream.flush() |
2406 | + |
2407 | + |
2408 | +class QuantumTestRunner(core.TextTestRunner): |
2409 | + def _makeResult(self): |
2410 | + return QuantumTestResult(self.stream, |
2411 | + self.descriptions, |
2412 | + self.verbosity, |
2413 | + self.config) |
2414 | + |
2415 | + |
2416 | +if __name__ == '__main__': |
2417 | + # Set up test logger. |
2418 | + logger = logging.getLogger() |
2419 | + hdlr = logging.StreamHandler() |
2420 | + formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') |
2421 | + hdlr.setFormatter(formatter) |
2422 | + logger.addHandler(hdlr) |
2423 | + logger.setLevel(logging.DEBUG) |
2424 | + |
2425 | + working_dir = os.path.abspath("tests") |
2426 | + c = config.Config(stream=sys.stdout, |
2427 | + env=os.environ, |
2428 | + verbosity=3, |
2429 | + workingDir=working_dir) |
2430 | + runner = QuantumTestRunner(stream=c.stream, |
2431 | + verbosity=c.verbosity, |
2432 | + config=c) |
2433 | + sys.exit(not core.run(config=c, testRunner=runner)) |
2434 | |
2435 | === added directory 'quantum/plugins/cisco/tests' |
2436 | === added file 'quantum/plugins/cisco/tests/__init__.py' |
2437 | --- quantum/plugins/cisco/tests/__init__.py 1970-01-01 00:00:00 +0000 |
2438 | +++ quantum/plugins/cisco/tests/__init__.py 2011-08-15 17:33:23 +0000 |
2439 | @@ -0,0 +1,18 @@ |
2440 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
2441 | +# |
2442 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
2443 | +# |
2444 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
2445 | +# not use this file except in compliance with the License. You may obtain |
2446 | +# a copy of the License at |
2447 | +# |
2448 | +# http://www.apache.org/licenses/LICENSE-2.0 |
2449 | +# |
2450 | +# Unless required by applicable law or agreed to in writing, software |
2451 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
2452 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
2453 | +# License for the specific language governing permissions and limitations |
2454 | +# under the License. |
2455 | +# |
2456 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
2457 | +# |
2458 | |
2459 | === added directory 'quantum/plugins/cisco/tests/unit' |
2460 | === added file 'quantum/plugins/cisco/tests/unit/__init__.py' |
2461 | --- quantum/plugins/cisco/tests/unit/__init__.py 1970-01-01 00:00:00 +0000 |
2462 | +++ quantum/plugins/cisco/tests/unit/__init__.py 2011-08-15 17:33:23 +0000 |
2463 | @@ -0,0 +1,32 @@ |
2464 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
2465 | + |
2466 | +# Copyright 2011 OpenStack LLC. |
2467 | +# All Rights Reserved. |
2468 | +# |
2469 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
2470 | +# not use this file except in compliance with the License. You may obtain |
2471 | +# a copy of the License at |
2472 | +# |
2473 | +# http://www.apache.org/licenses/LICENSE-2.0 |
2474 | +# |
2475 | +# Unless required by applicable law or agreed to in writing, software |
2476 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
2477 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
2478 | +# License for the specific language governing permissions and limitations |
2479 | +# under the License. |
2480 | + |
2481 | +# See http://code.google.com/p/python-nose/issues/detail?id=373 |
2482 | +# The code below enables nosetests to work with i18n _() blocks |
2483 | +import __builtin__ |
2484 | +import unittest |
2485 | +setattr(__builtin__, '_', lambda x: x) |
2486 | + |
2487 | + |
2488 | +class BaseTest(unittest.TestCase): |
2489 | + |
2490 | + def setUp(self): |
2491 | + pass |
2492 | + |
2493 | + |
2494 | +def setUp(): |
2495 | + pass |
2496 | |
2497 | === added file 'quantum/plugins/cisco/tests/unit/test_l2networkApi.py' |
2498 | --- quantum/plugins/cisco/tests/unit/test_l2networkApi.py 1970-01-01 00:00:00 +0000 |
2499 | +++ quantum/plugins/cisco/tests/unit/test_l2networkApi.py 2011-08-15 17:33:23 +0000 |
2500 | @@ -0,0 +1,892 @@ |
2501 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
2502 | +# |
2503 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
2504 | +# |
2505 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
2506 | +# not use this file except in compliance with the License. You may obtain |
2507 | +# a copy of the License at |
2508 | +# |
2509 | +# http://www.apache.org/licenses/LICENSE-2.0 |
2510 | +# |
2511 | +# Unless required by applicable law or agreed to in writing, software |
2512 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
2513 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
2514 | +# License for the specific language governing permissions and limitations |
2515 | +# under the License. |
2516 | +# |
2517 | +# @author: Shweta Padubidri, Cisco Systems, Inc. |
2518 | +# |
2519 | + |
2520 | +import logging |
2521 | +import unittest |
2522 | +from quantum.common import exceptions as exc |
2523 | +from quantum.plugins.cisco.common import cisco_constants as const |
2524 | +from quantum.plugins.cisco.common import cisco_exceptions as cexc |
2525 | +from quantum.plugins.cisco import l2network_plugin |
2526 | +from quantum.plugins.cisco import l2network_plugin_configuration as conf |
2527 | + |
2528 | +LOG = logging.getLogger('quantum.tests.test_core_api_func') |
2529 | + |
2530 | + |
2531 | +class CoreAPITestFunc(unittest.TestCase): |
2532 | + |
2533 | + def test_create_network(self, net_tenant_id=None, net_name=None): |
2534 | + |
2535 | + """ |
2536 | + Tests creation of new Virtual Network. |
2537 | + """ |
2538 | + |
2539 | + LOG.debug("test_create_network - START") |
2540 | + if net_tenant_id: |
2541 | + tenant_id = net_tenant_id |
2542 | + else: |
2543 | + tenant_id = self.tenant_id |
2544 | + if net_name: |
2545 | + network_name = net_name |
2546 | + else: |
2547 | + network_name = self.network_name |
2548 | + new_net_dict = self._l2network_plugin.create_network( |
2549 | + tenant_id, network_name) |
2550 | + self.assertEqual(new_net_dict[const.NET_NAME], network_name) |
2551 | + self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID]) |
2552 | + LOG.debug("test_create_network - END") |
2553 | + |
2554 | + def test_delete_network(self, net_tenant_id=None): |
2555 | + """ |
2556 | + Tests deletion of a Virtual Network. |
2557 | + """ |
2558 | + LOG.debug("test_delete_network - START") |
2559 | + if net_tenant_id: |
2560 | + tenant_id = net_tenant_id |
2561 | + else: |
2562 | + tenant_id = self.tenant_id |
2563 | + new_net_dict = self._l2network_plugin.create_network( |
2564 | + tenant_id, self.network_name) |
2565 | + delete_net_dict = self._l2network_plugin.delete_network( |
2566 | + tenant_id, new_net_dict[const.NET_ID]) |
2567 | + self.assertEqual( |
2568 | + new_net_dict[const.NET_ID], delete_net_dict[const.NET_ID]) |
2569 | + LOG.debug("test_delete_network - END") |
2570 | + |
2571 | + def test_delete_networkDNE(self, net_tenant_id=None, net_id='0005'): |
2572 | + """ |
2573 | + Tests deletion of a Virtual Network when Network does not exist. |
2574 | + """ |
2575 | + LOG.debug("test_delete_network_not_found - START") |
2576 | + if net_tenant_id: |
2577 | + tenant_id = net_tenant_id |
2578 | + else: |
2579 | + tenant_id = self.tenant_id |
2580 | + self.assertRaises( |
2581 | + exc.NetworkNotFound, self._l2network_plugin.delete_network, |
2582 | + tenant_id, net_id) |
2583 | + LOG.debug("test_delete_network_not_found - END") |
2584 | + |
2585 | + def test_delete_networkInUse(self, tenant_id='test_network'): |
2586 | + """ |
2587 | + Tests deletion of a Virtual Network when Network is in Use. |
2588 | + """ |
2589 | + LOG.debug("test_delete_networkInUse - START") |
2590 | + |
2591 | + new_net_dict = self._l2network_plugin.create_network( |
2592 | + tenant_id, self.network_name) |
2593 | + port_dict = self._l2network_plugin.create_port( |
2594 | + tenant_id, new_net_dict[const.NET_ID], self.port_state) |
2595 | + self._l2network_plugin.plug_interface( |
2596 | + tenant_id, new_net_dict[const.NET_ID], |
2597 | + port_dict[const.PORT_ID], self.remote_interface) |
2598 | + self.assertRaises(exc.NetworkInUse, |
2599 | + self._l2network_plugin.delete_network, tenant_id, |
2600 | + new_net_dict[const.NET_ID]) |
2601 | + self.tearDownNetworkPortInterface( |
2602 | + tenant_id, new_net_dict[const.NET_ID], |
2603 | + port_dict[const.PORT_ID]) |
2604 | + LOG.debug("test_delete_networkInUse - END") |
2605 | + |
2606 | + def test_show_network(self, net_tenant_id=None): |
2607 | + """ |
2608 | + Tests display of details of a Virtual Network . |
2609 | + """ |
2610 | + |
2611 | + LOG.debug("test_show_network - START") |
2612 | + if net_tenant_id: |
2613 | + tenant_id = net_tenant_id |
2614 | + else: |
2615 | + tenant_id = self.tenant_id |
2616 | + new_net_dict = self._l2network_plugin.create_network( |
2617 | + tenant_id, self.network_name) |
2618 | + result_net_dict = self._l2network_plugin.get_network_details( |
2619 | + tenant_id, new_net_dict[const.NET_ID]) |
2620 | + self.assertEqual( |
2621 | + new_net_dict[const.NET_ID], result_net_dict[const.NET_ID]) |
2622 | + self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID]) |
2623 | + LOG.debug("test_show_network - END") |
2624 | + |
2625 | + def test_show_networkDNE(self, net_tenant_id=None, net_id='0005'): |
2626 | + """ |
2627 | + Tests display of a Virtual Network when Network does not exist. |
2628 | + """ |
2629 | + |
2630 | + LOG.debug("test_show_network_not_found - START") |
2631 | + if net_tenant_id: |
2632 | + tenant_id = net_tenant_id |
2633 | + else: |
2634 | + tenant_id = self.tenant_id |
2635 | + self.assertRaises(exc.NetworkNotFound, |
2636 | + self._l2network_plugin.get_network_details, |
2637 | + tenant_id, net_id) |
2638 | + LOG.debug("test_show_network_not_found - END") |
2639 | + |
2640 | + def test_rename_network(self, net_tenant_id=None, |
2641 | + new_name='new_test_network'): |
2642 | + """ |
2643 | + Tests rename of a Virtual Network . |
2644 | + """ |
2645 | + |
2646 | + LOG.debug("test_rename_network - START") |
2647 | + if net_tenant_id: |
2648 | + tenant_id = net_tenant_id |
2649 | + else: |
2650 | + tenant_id = self.tenant_id |
2651 | + new_net_dict = self._l2network_plugin.create_network( |
2652 | + tenant_id, self.network_name) |
2653 | + rename_net_dict = self._l2network_plugin.rename_network( |
2654 | + tenant_id, new_net_dict[const.NET_ID], new_name) |
2655 | + self.assertEqual(new_name, rename_net_dict[const.NET_NAME]) |
2656 | + self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID]) |
2657 | + LOG.debug("test_rename_network - END") |
2658 | + |
2659 | + def test_rename_networkDNE(self, net_tenant_id=None, |
2660 | + net_id='0005', new_name='new_test_network'): |
2661 | + """ |
2662 | + Tests rename of a Virtual Network when Network does not exist. |
2663 | + """ |
2664 | + |
2665 | + LOG.debug("test_rename_network_not_found - START") |
2666 | + if net_tenant_id: |
2667 | + tenant_id = net_tenant_id |
2668 | + else: |
2669 | + tenant_id = self.tenant_id |
2670 | + self.assertRaises(exc.NetworkNotFound, |
2671 | + self._l2network_plugin.rename_network, |
2672 | + tenant_id, net_id, new_name) |
2673 | + LOG.debug("test_rename_network_not_found - END") |
2674 | + |
2675 | + def test_list_networks(self, tenant_id='test_network'): |
2676 | + """ |
2677 | + Tests listing of all the Virtual Networks . |
2678 | + """ |
2679 | + |
2680 | + LOG.debug("test_list_networks - START") |
2681 | + new_net_dict = self._l2network_plugin.create_network( |
2682 | + tenant_id, self.network_name) |
2683 | + new_net_dict2 = self._l2network_plugin.create_network( |
2684 | + tenant_id, 'test_net2') |
2685 | + net_list = self._l2network_plugin.get_all_networks(tenant_id) |
2686 | + net_temp_list = [new_net_dict, new_net_dict2] |
2687 | + self.assertEqual(len(net_list), 2) |
2688 | + self.assertTrue(net_list[0] in net_temp_list) |
2689 | + self.assertTrue(net_list[1] in net_temp_list) |
2690 | + self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID]) |
2691 | + self.tearDownNetwork(tenant_id, new_net_dict2[const.NET_ID]) |
2692 | + LOG.debug("test_list_networks - END") |
2693 | + |
2694 | + def test_list_ports(self, tenant_id='test_network'): |
2695 | + """ |
2696 | + Tests listing of all the Ports. |
2697 | + """ |
2698 | + |
2699 | + LOG.debug("test_list_ports - START") |
2700 | + new_net_dict = self._l2network_plugin.create_network( |
2701 | + tenant_id, self.network_name) |
2702 | + port_dict = self._l2network_plugin.create_port( |
2703 | + tenant_id, new_net_dict[const.NET_ID], self.port_state) |
2704 | + port_dict2 = self._l2network_plugin.create_port( |
2705 | + tenant_id, new_net_dict[const.NET_ID], self.port_state) |
2706 | + port_list = self._l2network_plugin.get_all_ports( |
2707 | + tenant_id, new_net_dict[const.NET_ID]) |
2708 | + port_temp_list = [port_dict, port_dict2] |
2709 | + self.assertEqual(len(port_list), 2) |
2710 | + self.assertTrue(port_list[0] in port_temp_list) |
2711 | + self.assertTrue(port_list[1] in port_temp_list) |
2712 | + |
2713 | + self.tearDownPortOnly(tenant_id, new_net_dict[const.NET_ID], |
2714 | + port_dict[const.PORT_ID]) |
2715 | + self.tearDownNetworkPort(tenant_id, new_net_dict[const.NET_ID], |
2716 | + port_dict2[const.PORT_ID]) |
2717 | + LOG.debug("test_list_ports - END") |
2718 | + |
2719 | + def test_create_port(self, tenant_id='test_network', |
2720 | + port_state=const.PORT_UP): |
2721 | + """ |
2722 | + Tests creation of Ports. |
2723 | + """ |
2724 | + |
2725 | + LOG.debug("test_create_port - START") |
2726 | + new_net_dict = self._l2network_plugin.create_network( |
2727 | + tenant_id, self.network_name) |
2728 | + port_dict = self._l2network_plugin.create_port( |
2729 | + tenant_id, new_net_dict[const.NET_ID], port_state) |
2730 | + self.assertEqual(port_dict[const.PORT_STATE], port_state) |
2731 | + self.tearDownNetworkPort(tenant_id, new_net_dict[const.NET_ID], |
2732 | + port_dict[const.PORT_ID]) |
2733 | + LOG.debug("test_create_port - END") |
2734 | + |
2735 | + def test_create_port_network_DNE( |
2736 | + self, net_tenant_id=None, net_id='0005', port_state=const.PORT_UP): |
2737 | + |
2738 | + """ |
2739 | + Tests creation of Ports when network does not exist. |
2740 | + """ |
2741 | + |
2742 | + LOG.debug("test_create_port_network_DNE - START") |
2743 | + if net_tenant_id: |
2744 | + tenant_id = net_tenant_id |
2745 | + else: |
2746 | + tenant_id = self.tenant_id |
2747 | + self.assertRaises(exc.NetworkNotFound, |
2748 | + self._l2network_plugin.create_port, |
2749 | + tenant_id, net_id, port_state) |
2750 | + LOG.debug("test_create_port_network_DNE - END:") |
2751 | + |
2752 | + def test_delete_port(self, tenant_id='test_tenant', |
2753 | + port_state=const.PORT_UP): |
2754 | + """ |
2755 | + Tests deletion of Ports |
2756 | + """ |
2757 | + |
2758 | + LOG.debug("test_delete_port - START") |
2759 | + new_net_dict = self._l2network_plugin.create_network( |
2760 | + tenant_id, self.network_name) |
2761 | + port_dict = self._l2network_plugin.create_port( |
2762 | + tenant_id, new_net_dict[const.NET_ID], port_state) |
2763 | + delete_port_dict = self._l2network_plugin.delete_port( |
2764 | + tenant_id, new_net_dict[const.NET_ID], |
2765 | + port_dict[const.PORT_ID]) |
2766 | + self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID]) |
2767 | + self.assertEqual(delete_port_dict, None) |
2768 | + LOG.debug("test_delete_port - END") |
2769 | + |
2770 | + def test_delete_port_networkDNE(self, tenant_id='test_tenant', |
2771 | + net_id='0005', port_id='p0005'): |
2772 | + """ |
2773 | + Tests deletion of Ports when network does not exist. |
2774 | + """ |
2775 | + |
2776 | + LOG.debug("test_delete_port_networkDNE - START") |
2777 | + self.assertRaises(exc.NetworkNotFound, |
2778 | + self._l2network_plugin.delete_port, tenant_id, |
2779 | + net_id, port_id) |
2780 | + LOG.debug("test_delete_port_networkDNE - END") |
2781 | + |
2782 | + def test_delete_portDNE(self, tenant_id='test_tenant', port_id='p0005'): |
2783 | + """ |
2784 | + Tests deletion of Ports when port does not exist. |
2785 | + """ |
2786 | + |
2787 | + LOG.debug("test_delete_portDNE - START") |
2788 | + new_net_dict = self._l2network_plugin.create_network( |
2789 | + tenant_id, self.network_name) |
2790 | + self.assertRaises(exc.PortNotFound, self._l2network_plugin.delete_port, |
2791 | + tenant_id, new_net_dict[const.NET_ID], port_id) |
2792 | + self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID]) |
2793 | + LOG.debug("test_delete_portDNE - END") |
2794 | + |
2795 | + def test_delete_portInUse(self, tenant_id='test_tenant'): |
2796 | + """ |
2797 | + Tests deletion of Ports when port is in Use. |
2798 | + """ |
2799 | + |
2800 | + LOG.debug("test_delete_portInUse - START") |
2801 | + new_net_dict = self._l2network_plugin.create_network( |
2802 | + tenant_id, self.network_name) |
2803 | + port_dict = self._l2network_plugin.create_port( |
2804 | + tenant_id, new_net_dict[const.NET_ID], |
2805 | + self.port_state) |
2806 | + self._l2network_plugin.plug_interface( |
2807 | + tenant_id, new_net_dict[const.NET_ID], |
2808 | + port_dict[const.PORT_ID], self.remote_interface) |
2809 | + self.assertRaises(exc.PortInUse, |
2810 | + self._l2network_plugin.delete_port, tenant_id, |
2811 | + new_net_dict[const.NET_ID], port_dict[const.PORT_ID]) |
2812 | + self.tearDownNetworkPortInterface( |
2813 | + tenant_id, new_net_dict[const.NET_ID], port_dict[const.PORT_ID]) |
2814 | + LOG.debug("test_delete_portInUse - END") |
2815 | + |
2816 | + def test_update_port(self, tenant_id='test_tenant', |
2817 | + port_state=const.PORT_DOWN): |
2818 | + """ |
2819 | + Tests updation of Ports. |
2820 | + """ |
2821 | + |
2822 | + LOG.debug("test_update_port - START") |
2823 | + new_net_dict = self._l2network_plugin.create_network( |
2824 | + tenant_id, self.network_name) |
2825 | + port_dict = self._l2network_plugin.create_port( |
2826 | + tenant_id, new_net_dict[const.NET_ID], self.port_state) |
2827 | + update_port_dict = self._l2network_plugin.update_port( |
2828 | + tenant_id, new_net_dict[const.NET_ID], |
2829 | + port_dict[const.PORT_ID], port_state) |
2830 | + self.assertEqual(update_port_dict[const.PORT_STATE], port_state) |
2831 | + self.tearDownNetworkPort(tenant_id, new_net_dict[const.NET_ID], |
2832 | + port_dict[const.PORT_ID]) |
2833 | + LOG.debug("test_update_port - END") |
2834 | + |
2835 | + def test_update_port_networkDNE(self, tenant_id='test_tenant', |
2836 | + net_id='0005', port_id='p0005'): |
2837 | + """ |
2838 | + Tests updation of Ports when network does not exist. |
2839 | + """ |
2840 | + |
2841 | + LOG.debug("test_update_port_networkDNE - START") |
2842 | + self.assertRaises(exc.NetworkNotFound, |
2843 | + self._l2network_plugin.update_port, tenant_id, |
2844 | + net_id, port_id, self.port_state) |
2845 | + LOG.debug("test_update_port_networkDNE - END") |
2846 | + |
2847 | + def test_update_portDNE(self, tenant_id='test_tenant', port_id='p0005'): |
2848 | + """ |
2849 | + Tests updation of Ports when port does not exist. |
2850 | + """ |
2851 | + |
2852 | + LOG.debug("test_update_portDNE - START") |
2853 | + new_net_dict = self._l2network_plugin.create_network( |
2854 | + tenant_id, self.network_name) |
2855 | + self.assertRaises( |
2856 | + exc.PortNotFound, self._l2network_plugin.update_port, tenant_id, |
2857 | + new_net_dict[const.NET_ID], port_id, self.port_state) |
2858 | + self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID]) |
2859 | + LOG.debug("test_update_portDNE - END") |
2860 | + |
2861 | + def test_show_port(self, tenant_id='test_tenant'): |
2862 | + """ |
2863 | + Tests display of Ports |
2864 | + """ |
2865 | + |
2866 | + LOG.debug("test_show_port - START") |
2867 | + new_net_dict = self._l2network_plugin.create_network( |
2868 | + tenant_id, self.network_name) |
2869 | + port_dict = self._l2network_plugin.create_port( |
2870 | + tenant_id, new_net_dict[const.NET_ID], self.port_state) |
2871 | + get_port_dict = self._l2network_plugin.get_port_details( |
2872 | + tenant_id, new_net_dict[const.NET_ID], |
2873 | + port_dict[const.PORT_ID]) |
2874 | + self.assertEqual(get_port_dict[const.PORT_STATE], self.port_state) |
2875 | + self.tearDownNetworkPort(tenant_id, new_net_dict[const.NET_ID], |
2876 | + port_dict[const.PORT_ID]) |
2877 | + LOG.debug("test_show_port - END") |
2878 | + |
2879 | + def test_show_port_networkDNE(self, tenant_id='test_tenant', |
2880 | + net_id='0005', port_id='p0005'): |
2881 | + """ |
2882 | + Tests display of Ports when network does not exist |
2883 | + """ |
2884 | + |
2885 | + LOG.debug("test_show_port_networkDNE - START") |
2886 | + self.assertRaises(exc.NetworkNotFound, |
2887 | + self._l2network_plugin.get_port_details, |
2888 | + tenant_id, net_id, port_id) |
2889 | + LOG.debug("test_show_port_networkDNE - END") |
2890 | + |
2891 | + def test_show_portDNE(self, tenant_id='test_tenant', port_id='p0005'): |
2892 | + """ |
2893 | + Tests display of Ports when port does not exist |
2894 | + """ |
2895 | + |
2896 | + LOG.debug("test_show_portDNE - START") |
2897 | + new_net_dict = self._l2network_plugin.create_network( |
2898 | + tenant_id, self.network_name) |
2899 | + self.assertRaises(exc.PortNotFound, |
2900 | + self._l2network_plugin.get_port_details, tenant_id, |
2901 | + new_net_dict[const.NET_ID], port_id) |
2902 | + self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID]) |
2903 | + LOG.debug("test_show_portDNE - END") |
2904 | + |
2905 | + def test_plug_interface(self, tenant_id='test_tenant', |
2906 | + remote_interface='new_interface'): |
2907 | + """ |
2908 | + Tests attachment of interface to the port |
2909 | + """ |
2910 | + |
2911 | + LOG.debug("test_plug_interface - START") |
2912 | + new_net_dict = self._l2network_plugin.create_network( |
2913 | + tenant_id, self.network_name) |
2914 | + port_dict = self._l2network_plugin.create_port( |
2915 | + tenant_id, new_net_dict[const.NET_ID], self.port_state) |
2916 | + self._l2network_plugin.plug_interface( |
2917 | + tenant_id, new_net_dict[const.NET_ID], |
2918 | + port_dict[const.PORT_ID], remote_interface) |
2919 | + self.assertEqual( |
2920 | + self._l2network_plugin._networks[new_net_dict[const.NET_ID]] |
2921 | + [const.NET_PORTS][port_dict[const.PORT_ID]] |
2922 | + [const.ATTACHMENT], remote_interface) |
2923 | + self.tearDownNetworkPortInterface( |
2924 | + tenant_id, new_net_dict[const.NET_ID], |
2925 | + port_dict[const.PORT_ID]) |
2926 | + LOG.debug("test_plug_interface - END") |
2927 | + |
2928 | + def test_plug_interface_networkDNE( |
2929 | + self, tenant_id='test_tenant', net_id='0005', |
2930 | + port_id='p0005', remote_interface='new_interface'): |
2931 | + """ |
2932 | + Tests attachment of interface network does not exist |
2933 | + """ |
2934 | + |
2935 | + LOG.debug("test_plug_interface_networkDNE - START") |
2936 | + self.assertRaises(exc.NetworkNotFound, |
2937 | + self._l2network_plugin.plug_interface, tenant_id, |
2938 | + net_id, port_id, remote_interface) |
2939 | + LOG.debug("test_plug_interface_networkDNE - END") |
2940 | + |
2941 | + def test_plug_interface_portDNE(self, tenant_id='test_tenant', |
2942 | + port_id='p0005', |
2943 | + remote_interface='new_interface'): |
2944 | + """ |
2945 | + Tests attachment of interface port does not exist |
2946 | + """ |
2947 | + |
2948 | + LOG.debug("test_plug_interface_portDNE - START") |
2949 | + new_net_dict = self._l2network_plugin.create_network( |
2950 | + tenant_id, self.network_name) |
2951 | + self.assertRaises( |
2952 | + exc.PortNotFound, self._l2network_plugin.plug_interface, tenant_id, |
2953 | + new_net_dict[const.NET_ID], port_id, remote_interface) |
2954 | + self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID]) |
2955 | + LOG.debug("test_plug_interface_portDNE - END") |
2956 | + |
2957 | + def test_plug_interface_portInUse(self, tenant_id='test_tenant', |
2958 | + remote_interface='new_interface'): |
2959 | + |
2960 | + """ |
2961 | + Tests attachment of new interface to the port when there is an |
2962 | + existing attachment |
2963 | + """ |
2964 | + |
2965 | + LOG.debug("test_plug_interface_portInUse - START") |
2966 | + new_net_dict = self._l2network_plugin.create_network( |
2967 | + tenant_id, self.network_name) |
2968 | + port_dict = self._l2network_plugin.create_port( |
2969 | + tenant_id, new_net_dict[const.NET_ID], self.port_state) |
2970 | + self._l2network_plugin.plug_interface( |
2971 | + tenant_id, new_net_dict[const.NET_ID], |
2972 | + port_dict[const.PORT_ID], remote_interface) |
2973 | + self.assertRaises(exc.AlreadyAttached, |
2974 | + self._l2network_plugin.plug_interface, tenant_id, |
2975 | + new_net_dict[const.NET_ID], |
2976 | + port_dict[const.PORT_ID], remote_interface) |
2977 | + self.tearDownNetworkPortInterface( |
2978 | + tenant_id, new_net_dict[const.NET_ID], |
2979 | + port_dict[const.PORT_ID]) |
2980 | + LOG.debug("test_plug_interface_portInUse - END") |
2981 | + |
2982 | + def test_unplug_interface(self, tenant_id='test_tenant'): |
2983 | + """ |
2984 | + Tests detaachment of an interface to a port |
2985 | + """ |
2986 | + |
2987 | + LOG.debug("test_unplug_interface - START") |
2988 | + new_net_dict = self._l2network_plugin.create_network( |
2989 | + tenant_id, self.network_name) |
2990 | + port_dict = self._l2network_plugin.create_port( |
2991 | + tenant_id, new_net_dict[const.NET_ID], |
2992 | + self.port_state) |
2993 | + self._l2network_plugin.plug_interface( |
2994 | + tenant_id, new_net_dict[const.NET_ID], |
2995 | + port_dict[const.PORT_ID], self.remote_interface) |
2996 | + self._l2network_plugin.unplug_interface( |
2997 | + tenant_id, new_net_dict[const.NET_ID], |
2998 | + port_dict[const.PORT_ID]) |
2999 | + self.assertEqual(self._l2network_plugin._networks |
3000 | + [new_net_dict[const.NET_ID]][const.NET_PORTS] |
3001 | + [port_dict[const.PORT_ID]][const.ATTACHMENT], None) |
3002 | + self.tearDownNetworkPort(tenant_id, new_net_dict[const.NET_ID], |
3003 | + port_dict[const.PORT_ID]) |
3004 | + LOG.debug("test_unplug_interface - END") |
3005 | + |
3006 | + def test_unplug_interface_networkDNE(self, tenant_id='test_tenant', |
3007 | + net_id='0005', port_id='p0005'): |
3008 | + """ |
3009 | + Tests detaachment of an interface to a port, when the network does |
3010 | + not exist |
3011 | + """ |
3012 | + |
3013 | + LOG.debug("test_unplug_interface_networkDNE - START") |
3014 | + self.assertRaises(exc.NetworkNotFound, |
3015 | + self._l2network_plugin.unplug_interface, |
3016 | + tenant_id, net_id, port_id) |
3017 | + LOG.debug("test_unplug_interface_networkDNE - END") |
3018 | + |
3019 | + def test_unplug_interface_portDNE(self, tenant_id='test_tenant', |
3020 | + port_id='p0005'): |
3021 | + """ |
3022 | + Tests detaachment of an interface to a port, when the port does |
3023 | + not exist |
3024 | + """ |
3025 | + |
3026 | + LOG.debug("test_unplug_interface_portDNE - START") |
3027 | + new_net_dict = self._l2network_plugin.create_network(tenant_id, |
3028 | + self.network_name) |
3029 | + self.assertRaises(exc.PortNotFound, |
3030 | + self._l2network_plugin.unplug_interface, tenant_id, |
3031 | + new_net_dict[const.NET_ID], port_id) |
3032 | + self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID]) |
3033 | + LOG.debug("test_unplug_interface_portDNE - END") |
3034 | + |
3035 | + def test_create_portprofile(self, net_tenant_id=None, |
3036 | + net_profile_name=None, net_vlan_id=None): |
3037 | + """ |
3038 | + Tests creation of a port-profile |
3039 | + """ |
3040 | + |
3041 | + LOG.debug("test_create_portprofile - tenant id: %s - START", |
3042 | + net_tenant_id) |
3043 | + if net_tenant_id: |
3044 | + tenant_id = net_tenant_id |
3045 | + else: |
3046 | + tenant_id = self.tenant_id |
3047 | + if net_profile_name: |
3048 | + profile_name = net_profile_name |
3049 | + else: |
3050 | + profile_name = self.profile_name |
3051 | + if net_vlan_id: |
3052 | + vlan_id = net_vlan_id |
3053 | + else: |
3054 | + vlan_id = self.vlan_id |
3055 | + port_profile_dict = self._l2network_plugin.create_portprofile( |
3056 | + tenant_id, profile_name, vlan_id) |
3057 | + port_profile_id = port_profile_dict['profile-id'] |
3058 | + self.assertEqual( |
3059 | + self._l2network_plugin._portprofiles[port_profile_id]['vlan-id'], |
3060 | + vlan_id) |
3061 | + self.assertEqual( |
3062 | + self._l2network_plugin._portprofiles[port_profile_id] |
3063 | + ['profile-name'], profile_name) |
3064 | + self.tearDownPortProfile(tenant_id, port_profile_id) |
3065 | + LOG.debug("test_create_portprofile - tenant id: %s - END", |
3066 | + net_tenant_id) |
3067 | + |
3068 | + def test_delete_portprofile(self, net_tenant_id=None): |
3069 | + """ |
3070 | + Tests deletion of a port-profile |
3071 | + """ |
3072 | + |
3073 | + LOG.debug("test_delete_portprofile - tenant id: %s - START", |
3074 | + net_tenant_id) |
3075 | + if net_tenant_id: |
3076 | + tenant_id = net_tenant_id |
3077 | + else: |
3078 | + tenant_id = self.tenant_id |
3079 | + port_profile_dict = self._l2network_plugin.create_portprofile( |
3080 | + tenant_id, self.profile_name, self.vlan_id) |
3081 | + port_profile_id = port_profile_dict['profile-id'] |
3082 | + self._l2network_plugin.delete_portprofile(tenant_id, port_profile_id) |
3083 | + self.assertEqual(self._l2network_plugin._portprofiles, {}) |
3084 | + LOG.debug("test_delete_portprofile - tenant id: %s - END", |
3085 | + net_tenant_id) |
3086 | + |
3087 | + def test_delete_portprofileDNE(self, tenant_id='test_tenant', |
3088 | + profile_id='pr0005'): |
3089 | + """ |
3090 | + Tests deletion of a port-profile when netowrk does not exist |
3091 | + """ |
3092 | + |
3093 | + LOG.debug("test_delete_portprofileDNE - START") |
3094 | + self.assertRaises(cexc.PortProfileNotFound, |
3095 | + self._l2network_plugin.delete_portprofile, |
3096 | + tenant_id, profile_id) |
3097 | + LOG.debug("test_delete_portprofileDNE - END") |
3098 | + |
3099 | + def test_delete_portprofileAssociated(self, tenant_id='test_tenant'): |
3100 | + |
3101 | + """ |
3102 | + Tests deletion of an associatedport-profile |
3103 | + """ |
3104 | + |
3105 | + LOG.debug("test_delete_portprofileAssociated - START") |
3106 | + port_profile_dict = self._l2network_plugin.create_portprofile( |
3107 | + tenant_id, self.profile_name, self.vlan_id) |
3108 | + port_profile_id = port_profile_dict['profile-id'] |
3109 | + self._l2network_plugin.associate_portprofile( |
3110 | + tenant_id, self.net_id, self.port_id, port_profile_id) |
3111 | + self.assertRaises(cexc.PortProfileInvalidDelete, |
3112 | + self._l2network_plugin.delete_portprofile, |
3113 | + tenant_id, port_profile_id) |
3114 | + self.tearDownAssociatePortProfile(tenant_id, self.net_id, |
3115 | + self.port_id, port_profile_id) |
3116 | + LOG.debug("test_delete_portprofileAssociated - END") |
3117 | + |
3118 | + def test_list_portprofile(self, tenant_id='test_tenant'): |
3119 | + """ |
3120 | + Tests listing of port-profiles |
3121 | + """ |
3122 | + |
3123 | + LOG.debug("test_list_portprofile - tenant id: %s - START", tenant_id) |
3124 | + profile_name2 = tenant_id + '_port_profile2' |
3125 | + vlan_id2 = tenant_id + '201' |
3126 | + port_profile_dict1 = self._l2network_plugin.create_portprofile( |
3127 | + tenant_id, self.profile_name, self.vlan_id) |
3128 | + port_profile_dict2 = self._l2network_plugin.create_portprofile( |
3129 | + tenant_id, profile_name2, vlan_id2) |
3130 | + port_profile_id1 = port_profile_dict1['profile-id'] |
3131 | + port_profile_id2 = port_profile_dict2['profile-id'] |
3132 | + list_all_portprofiles = self._l2network_plugin.get_all_portprofiles( |
3133 | + tenant_id) |
3134 | + self.assertEqual(self._l2network_plugin._portprofiles |
3135 | + [port_profile_id1]['vlan-id'], self.vlan_id) |
3136 | + self.assertEqual(self._l2network_plugin._portprofiles |
3137 | + [port_profile_id1]['profile-name'], self.profile_name) |
3138 | + self.assertEqual(self._l2network_plugin._portprofiles |
3139 | + [port_profile_id2]['vlan-id'], vlan_id2) |
3140 | + self.assertEqual(self._l2network_plugin._portprofiles |
3141 | + [port_profile_id2]['profile-name'], profile_name2) |
3142 | + LOG.debug("test_create_portprofile - tenant id: %s - END", tenant_id) |
3143 | + |
3144 | + def test_show_portprofile(self, net_tenant_id=None): |
3145 | + """ |
3146 | + Tests display of a port-profile |
3147 | + """ |
3148 | + |
3149 | + LOG.debug("test_show_portprofile - START") |
3150 | + if net_tenant_id: |
3151 | + tenant_id = net_tenant_id |
3152 | + else: |
3153 | + tenant_id = self.tenant_id |
3154 | + port_profile_dict = self._l2network_plugin.create_portprofile( |
3155 | + tenant_id, self.profile_name, self.vlan_id) |
3156 | + port_profile_id = port_profile_dict['profile-id'] |
3157 | + result_port_profile = self._l2network_plugin.get_portprofile_details( |
3158 | + tenant_id, port_profile_id) |
3159 | + self.assertEqual(result_port_profile[const.PROFILE_VLAN_ID], |
3160 | + self.vlan_id) |
3161 | + self.assertEqual(result_port_profile[const.PROFILE_NAME], |
3162 | + self.profile_name) |
3163 | + self.tearDownPortProfile(tenant_id, port_profile_id) |
3164 | + LOG.debug("test_show_portprofile - tenant id: %s - END", net_tenant_id) |
3165 | + |
3166 | + def test_show_portprofileDNE(self, tenant_id='test_tenant', |
3167 | + profile_id='pr0005'): |
3168 | + """ |
3169 | + Tests display of a port-profile when network does not exist |
3170 | + """ |
3171 | + |
3172 | + LOG.debug("test_show_portprofileDNE - START") |
3173 | + self.assertRaises(cexc.PortProfileNotFound, |
3174 | + self._l2network_plugin.get_portprofile_details, |
3175 | + tenant_id, profile_id) |
3176 | + LOG.debug("test_show_portprofileDNE - END") |
3177 | + |
3178 | + def test_rename_portprofile(self, tenant_id='test_tenant', |
3179 | + new_profile_name='new_profile_name'): |
3180 | + """ |
3181 | + Tests rename of a port-profile |
3182 | + """ |
3183 | + |
3184 | + LOG.debug("test_rename_portprofile - START") |
3185 | + port_profile_dict = self._l2network_plugin.create_portprofile( |
3186 | + tenant_id, self.profile_name, self.vlan_id) |
3187 | + port_profile_id = port_profile_dict['profile-id'] |
3188 | + result_port_profile_dict = self._l2network_plugin.rename_portprofile( |
3189 | + tenant_id, port_profile_id, new_profile_name) |
3190 | + self.assertEqual(result_port_profile_dict[const.PROFILE_NAME], |
3191 | + new_profile_name) |
3192 | + self.tearDownPortProfile(tenant_id, port_profile_id) |
3193 | + LOG.debug("test_show_portprofile - tenant id: %s - END") |
3194 | + |
3195 | + def test_rename_portprofileDNE(self, tenant_id='test_tenant', |
3196 | + profile_id='pr0005', |
3197 | + new_profile_name='new_profile_name'): |
3198 | + """ |
3199 | + Tests rename of a port-profile when network does not exist |
3200 | + """ |
3201 | + |
3202 | + LOG.debug("test_rename_portprofileDNE - START") |
3203 | + self.assertRaises(cexc.PortProfileNotFound, |
3204 | + self._l2network_plugin.rename_portprofile, |
3205 | + tenant_id, profile_id, new_profile_name) |
3206 | + LOG.debug("test_rename_portprofileDNE - END") |
3207 | + |
3208 | + def test_associate_portprofile(self, tenant_id='test_tenant', |
3209 | + net_id='0005', port_id='p00005'): |
3210 | + """ |
3211 | + Tests association of a port-profile |
3212 | + """ |
3213 | + |
3214 | + LOG.debug("test_associate_portprofile - START") |
3215 | + port_profile_dict = self._l2network_plugin.create_portprofile( |
3216 | + tenant_id, self.profile_name, self.vlan_id) |
3217 | + port_profile_id = port_profile_dict['profile-id'] |
3218 | + self._l2network_plugin.associate_portprofile( |
3219 | + tenant_id, net_id, port_id, port_profile_id) |
3220 | + self.assertEqual( |
3221 | + self._l2network_plugin._portprofiles[port_profile_id] |
3222 | + [const.PROFILE_ASSOCIATIONS][0], port_id) |
3223 | + self.tearDownAssociatePortProfile(tenant_id, net_id, |
3224 | + port_id, port_profile_id) |
3225 | + LOG.debug("test_associate_portprofile - END") |
3226 | + |
3227 | + def test_associate_portprofileDNE(self, tenant_id='test_tenant', |
3228 | + net_id='0005', port_id='p00005', |
3229 | + profile_id='pr0005'): |
3230 | + """ |
3231 | + Tests association of a port-profile when a network does not exist |
3232 | + """ |
3233 | + |
3234 | + LOG.debug("test_associate_portprofileDNE - START") |
3235 | + self.assertRaises(cexc.PortProfileNotFound, |
3236 | + self._l2network_plugin.associate_portprofile, |
3237 | + tenant_id, net_id, port_id, profile_id) |
3238 | + LOG.debug("test_associate_portprofileDNE - END") |
3239 | + |
3240 | + def test_disassociate_portprofile(self, tenant_id='test_tenant', |
3241 | + net_id='0005', port_id='p00005'): |
3242 | + """ |
3243 | + Tests disassociation of a port-profile |
3244 | + """ |
3245 | + |
3246 | + LOG.debug("test_disassociate_portprofile - START") |
3247 | + port_profile_dict = self._l2network_plugin.create_portprofile( |
3248 | + tenant_id, self.profile_name, self.vlan_id) |
3249 | + port_profile_id = port_profile_dict['profile-id'] |
3250 | + self._l2network_plugin.associate_portprofile(tenant_id, net_id, |
3251 | + port_id, port_profile_id) |
3252 | + self._l2network_plugin.disassociate_portprofile( |
3253 | + tenant_id, net_id, port_id, port_profile_id) |
3254 | + self.assertEqual(self._l2network_plugin._portprofiles |
3255 | + [port_profile_id][const.PROFILE_ASSOCIATIONS], []) |
3256 | + self.tearDownPortProfile(tenant_id, port_profile_id) |
3257 | + LOG.debug("test_disassociate_portprofile - END") |
3258 | + |
3259 | + def test_disassociate_portprofileDNE(self, tenant_id='test_tenant', |
3260 | + net_id='0005', port_id='p00005', profile_id='pr0005'): |
3261 | + """ |
3262 | + Tests disassociation of a port-profile when network does not exist |
3263 | + """ |
3264 | + |
3265 | + LOG.debug("test_disassociate_portprofileDNE - START") |
3266 | + self.assertRaises(cexc.PortProfileNotFound, |
3267 | + self._l2network_plugin.disassociate_portprofile, |
3268 | + tenant_id, net_id, port_id, profile_id) |
3269 | + LOG.debug("test_disassociate_portprofileDNE - END") |
3270 | + |
3271 | +# def test_disassociate_portprofile_Unassociated |
3272 | + |
3273 | + def test_get_tenant(self, net_tenant_id=None): |
3274 | + """ |
3275 | + Tests get tenant |
3276 | + """ |
3277 | + |
3278 | + LOG.debug("test_get_tenant - START") |
3279 | + if net_tenant_id: |
3280 | + tenant_id = net_tenant_id |
3281 | + else: |
3282 | + tenant_id = self.tenant_id |
3283 | + tenant_dict = self._l2network_plugin._get_tenant(tenant_id) |
3284 | + self.assertEqual(tenant_dict[const.TENANT_ID], tenant_id) |
3285 | + self.assertEqual(tenant_dict[const.TENANT_NAME], tenant_id) |
3286 | + LOG.debug("test_get_tenant - END") |
3287 | + |
3288 | + def test_get_vlan_name(self, net_tenant_id=None, vlan_name="NewVlan", |
3289 | + vlan_prefix=conf.VLAN_NAME_PREFIX): |
3290 | + """ |
3291 | + Tests get vlan name |
3292 | + """ |
3293 | + |
3294 | + LOG.debug("test_get_vlan_name - START") |
3295 | + if net_tenant_id: |
3296 | + tenant_id = net_tenant_id |
3297 | + else: |
3298 | + tenant_id = self.tenant_id |
3299 | + result_vlan_name = self._l2network_plugin._get_vlan_name(tenant_id, |
3300 | + vlan_name) |
3301 | + expected_output = vlan_prefix + tenant_id + "-" + vlan_name |
3302 | + self.assertEqual(result_vlan_name, expected_output) |
3303 | + LOG.debug("test_get_vlan_name - END") |
3304 | + |
3305 | + def test_validate_port_state(self, port_state=const.PORT_UP): |
3306 | + """ |
3307 | + Tests validate port state |
3308 | + """ |
3309 | + |
3310 | + LOG.debug("test_validate_port_state - START") |
3311 | + result = self._l2network_plugin._validate_port_state(port_state) |
3312 | + self.assertEqual(result, True) |
3313 | + LOG.debug("test_validate_port_state - END") |
3314 | + |
3315 | + def test_invalid_port_state(self, port_state="BADSTATE"): |
3316 | + """ |
3317 | + Tests invalidate port state |
3318 | + """ |
3319 | + |
3320 | + LOG.debug("test_validate_port_state - START") |
3321 | + self.assertRaises(exc.StateInvalid, |
3322 | + self._l2network_plugin._validate_port_state, |
3323 | + port_state) |
3324 | + LOG.debug("test_validate_port_state - END") |
3325 | + |
3326 | + def test_validate_attachment(self, net_tenant_id=None, |
3327 | + remote_interface_id="new_interface"): |
3328 | + """ |
3329 | + Tests validate attachment |
3330 | + """ |
3331 | + |
3332 | + LOG.debug("test_validate_attachment - START") |
3333 | + if net_tenant_id: |
3334 | + tenant_id = net_tenant_id |
3335 | + else: |
3336 | + tenant_id = self.tenant_id |
3337 | + net_name = self.network_name |
3338 | + new_network_dict = self._l2network_plugin.create_network(tenant_id, |
3339 | + net_name) |
3340 | + network_id = new_network_dict[const.NET_ID] |
3341 | + new_port_dict = self._l2network_plugin.create_port(tenant_id, |
3342 | + network_id) |
3343 | + port_id = new_port_dict[const.PORT_ID] |
3344 | + self._l2network_plugin.plug_interface( |
3345 | + tenant_id, new_network_dict[const.NET_ID], port_id, |
3346 | + remote_interface_id) |
3347 | + self.assertRaises(exc.AlreadyAttached, |
3348 | + self._l2network_plugin._validate_attachment, |
3349 | + tenant_id, network_id, port_id, remote_interface_id) |
3350 | + self.tearDownNetworkPortInterface( |
3351 | + tenant_id, new_network_dict[const.NET_ID], port_id) |
3352 | + LOG.debug("test_validate_attachment - END") |
3353 | + |
3354 | + def setUp(self): |
3355 | + self.tenant_id = "test_tenant" |
3356 | + self.network_name = "test_network" |
3357 | + self.profile_name = "test_tenant_port_profile" |
3358 | + self.vlan_id = "test_tenant_vlanid300" |
3359 | + self.port_state = const.PORT_UP |
3360 | + self.net_id = '00005' |
3361 | + self.port_id = 'p0005' |
3362 | + self.remote_interface = 'new_interface' |
3363 | + self._l2network_plugin = l2network_plugin.L2Network() |
3364 | + |
3365 | + """ |
3366 | + Clean up functions after the tests |
3367 | + """ |
3368 | + |
3369 | + def tearDownNetwork(self, tenant_id, network_dict_id): |
3370 | + self._l2network_plugin.delete_network(tenant_id, network_dict_id) |
3371 | + |
3372 | + def tearDownPortOnly(self, tenant_id, network_dict_id, port_id): |
3373 | + self._l2network_plugin.delete_port(tenant_id, network_dict_id, port_id) |
3374 | + |
3375 | + def tearDownNetworkPort(self, tenant_id, network_dict_id, port_id): |
3376 | + self._l2network_plugin.delete_port(tenant_id, network_dict_id, port_id) |
3377 | + self.tearDownNetwork(tenant_id, network_dict_id) |
3378 | + |
3379 | + def tearDownNetworkPortInterface(self, tenant_id, network_dict_id, |
3380 | + port_id): |
3381 | + self._l2network_plugin.unplug_interface(tenant_id, |
3382 | + network_dict_id, port_id) |
3383 | + self.tearDownNetworkPort(tenant_id, network_dict_id, port_id) |
3384 | + |
3385 | + def tearDownPortProfile(self, tenant_id, port_profile_id): |
3386 | + self._l2network_plugin.delete_portprofile(tenant_id, port_profile_id) |
3387 | + |
3388 | + def tearDownAssociatePortProfile(self, tenant_id, net_id, port_id, |
3389 | + port_profile_id): |
3390 | + self._l2network_plugin.disassociate_portprofile( |
3391 | + tenant_id, net_id, port_id, port_profile_id) |
3392 | + self.tearDownPortProfile(tenant_id, port_profile_id) |
3393 | |
3394 | === added file 'quantum/plugins/cisco/tests/unit/test_nexus_plugin.py' |
3395 | --- quantum/plugins/cisco/tests/unit/test_nexus_plugin.py 1970-01-01 00:00:00 +0000 |
3396 | +++ quantum/plugins/cisco/tests/unit/test_nexus_plugin.py 2011-08-15 17:33:23 +0000 |
3397 | @@ -0,0 +1,282 @@ |
3398 | +# copyright 2011 Cisco Systems, Inc. All rights reserved. |
3399 | +# |
3400 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
3401 | +# not use this file except in compliance with the License. You may obtain |
3402 | +# a copy of the License at |
3403 | +# |
3404 | +# http://www.apache.org/licenses/LICENSE-2.0 |
3405 | +# |
3406 | +# Unless required by applicable law or agreed to in writing, software |
3407 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
3408 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
3409 | +# License for the specific language governing permissions and limitations |
3410 | +# under the License. |
3411 | +# |
3412 | +# @author: Shweta Padubidri, Peter Strunk, Cisco Systems, Inc. |
3413 | +# |
3414 | +import unittest |
3415 | +import logging |
3416 | +from quantum.common import exceptions as exc |
3417 | +from quantum.plugins.cisco.common import cisco_constants as const |
3418 | +from quantum.plugins.cisco.nexus import cisco_nexus_plugin |
3419 | + |
3420 | +LOG = logging.getLogger('quantum.tests.test_nexus') |
3421 | + |
3422 | + |
3423 | +class TestNexusPlugin(unittest.TestCase): |
3424 | + |
3425 | + def setUp(self): |
3426 | + |
3427 | + self.tenant_id = "test_tenant_cisco1" |
3428 | + self.net_name = "test_network_cisco1" |
3429 | + self.net_id = 000007 |
3430 | + self.vlan_name = "q-" + str(self.net_id) + "vlan" |
3431 | + self.vlan_id = 267 |
3432 | + self.port_id = "9" |
3433 | + self._cisco_nexus_plugin = cisco_nexus_plugin.NexusPlugin() |
3434 | + |
3435 | + def test_create_network(self, net_tenant_id=None, network_name=None, |
3436 | + network_id=None, net_vlan_name=None, |
3437 | + net_vlan_id=None): |
3438 | + """ |
3439 | + Tests creation of new Virtual Network. |
3440 | + """ |
3441 | + |
3442 | + LOG.debug("test_create_network - START") |
3443 | + if net_tenant_id: |
3444 | + tenant_id = net_tenant_id |
3445 | + else: |
3446 | + tenant_id = self.tenant_id |
3447 | + if network_name: |
3448 | + net_name = network_name |
3449 | + else: |
3450 | + net_name = self.net_name |
3451 | + if network_id: |
3452 | + net_id = network_id |
3453 | + else: |
3454 | + net_id = self.net_id |
3455 | + if net_vlan_name: |
3456 | + vlan_name = net_vlan_name |
3457 | + else: |
3458 | + vlan_name = self.vlan_name |
3459 | + if net_vlan_id: |
3460 | + vlan_id = net_vlan_id |
3461 | + else: |
3462 | + vlan_id = self.vlan_id |
3463 | + |
3464 | + new_net_dict = self._cisco_nexus_plugin.create_network( |
3465 | + tenant_id, net_name, net_id, vlan_name, vlan_id) |
3466 | + |
3467 | + self.assertEqual(new_net_dict[const.NET_ID], self.net_id) |
3468 | + self.assertEqual(new_net_dict[const.NET_NAME], self.net_name) |
3469 | + self.assertEqual(new_net_dict[const.NET_VLAN_NAME], self.vlan_name) |
3470 | + self.assertEqual(new_net_dict[const.NET_VLAN_ID], self.vlan_id) |
3471 | + self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID]) |
3472 | + LOG.debug("test_create_network - END") |
3473 | + |
3474 | + def test_delete_network(self, net_tenant_id=None, network_id=None): |
3475 | + """ |
3476 | + Tests deletion of a Virtual Network. |
3477 | + """ |
3478 | + |
3479 | + LOG.debug("test_delete_network - START") |
3480 | + |
3481 | + if net_tenant_id: |
3482 | + tenant_id = net_tenant_id |
3483 | + else: |
3484 | + tenant_id = self.tenant_id |
3485 | + if network_id: |
3486 | + net_id = network_id |
3487 | + else: |
3488 | + net_id = self.net_id |
3489 | + |
3490 | + new_net_dict = self._cisco_nexus_plugin.create_network( |
3491 | + tenant_id, self.net_name, net_id, self.vlan_name, self.vlan_id) |
3492 | + deleted_net_dict = self._cisco_nexus_plugin.delete_network( |
3493 | + tenant_id, new_net_dict[const.NET_ID]) |
3494 | + self.assertEqual(deleted_net_dict[const.NET_ID], net_id) |
3495 | + LOG.debug("test_delete_network - END") |
3496 | + |
3497 | + def test_delete_network_DNE(self, net_tenant_id=None, net_id='0005'): |
3498 | + """ |
3499 | + Tests deletion of a Virtual Network when Network does not exist. |
3500 | + """ |
3501 | + |
3502 | + LOG.debug("test_delete_network_DNE - START") |
3503 | + |
3504 | + if net_tenant_id: |
3505 | + tenant_id = net_tenant_id |
3506 | + else: |
3507 | + tenant_id = self.tenant_id |
3508 | + |
3509 | + self.assertRaises(exc.NetworkNotFound, |
3510 | + self._cisco_nexus_plugin.delete_network, |
3511 | + tenant_id, net_id) |
3512 | + |
3513 | + LOG.debug("test_delete_network_DNE - END") |
3514 | + |
3515 | + def test_get_network_details(self, net_tenant_id=None, network_id=None): |
3516 | + """ |
3517 | + Tests displays details of a Virtual Network . |
3518 | + """ |
3519 | + |
3520 | + LOG.debug("test_get_network_details - START") |
3521 | + |
3522 | + if net_tenant_id: |
3523 | + tenant_id = net_tenant_id |
3524 | + else: |
3525 | + tenant_id = self.tenant_id |
3526 | + if network_id: |
3527 | + net_id = network_id |
3528 | + else: |
3529 | + net_id = self.net_id |
3530 | + |
3531 | + new_net_dict = self._cisco_nexus_plugin.create_network( |
3532 | + tenant_id, self.net_name, net_id, self.vlan_name, self.vlan_id) |
3533 | + check_net_dict = self._cisco_nexus_plugin.get_network_details( |
3534 | + tenant_id, net_id) |
3535 | + |
3536 | + self.assertEqual(check_net_dict[const.NET_ID], net_id) |
3537 | + self.assertEqual(check_net_dict[const.NET_NAME], self.net_name) |
3538 | + self.assertEqual(check_net_dict[const.NET_VLAN_NAME], self.vlan_name) |
3539 | + self.assertEqual(check_net_dict[const.NET_VLAN_ID], self.vlan_id) |
3540 | + self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID]) |
3541 | + LOG.debug("test_get_network_details - END") |
3542 | + |
3543 | + def test_get_networkDNE(self, net_tenant_id=None, net_id='0005'): |
3544 | + """ |
3545 | + Tests display of a Virtual Network when Network does not exist. |
3546 | + """ |
3547 | + |
3548 | + LOG.debug("test_get_network_details_network_does_not_exist - START") |
3549 | + |
3550 | + if net_tenant_id: |
3551 | + tenant_id = net_tenant_id |
3552 | + else: |
3553 | + tenant_id = self.tenant_id |
3554 | + |
3555 | + self.assertRaises(exc.NetworkNotFound, |
3556 | + self._cisco_nexus_plugin.get_network_details, |
3557 | + tenant_id, net_id) |
3558 | + |
3559 | + LOG.debug("test_get_network_details_network_does_not_exist - END") |
3560 | + |
3561 | + def test_rename_network(self, new_name="new_network_name", |
3562 | + net_tenant_id=None, network_id=None): |
3563 | + """ |
3564 | + Tests rename of a Virtual Network . |
3565 | + """ |
3566 | + |
3567 | + LOG.debug("test_rename_network - START") |
3568 | + |
3569 | + if net_tenant_id: |
3570 | + tenant_id = net_tenant_id |
3571 | + else: |
3572 | + tenant_id = self.tenant_id |
3573 | + if network_id: |
3574 | + net_id = network_id |
3575 | + else: |
3576 | + net_id = self.net_id |
3577 | + |
3578 | + new_net_dict = self._cisco_nexus_plugin.create_network( |
3579 | + tenant_id, self.net_name, net_id, self.vlan_name, |
3580 | + self.vlan_id) |
3581 | + rename_net_dict = self._cisco_nexus_plugin.rename_network( |
3582 | + tenant_id, new_net_dict[const.NET_ID], new_name) |
3583 | + self.assertEqual(rename_net_dict[const.NET_NAME], new_name) |
3584 | + self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID]) |
3585 | + LOG.debug("test_rename_network - END") |
3586 | + |
3587 | + def test_rename_network_DNE(self, new_name="new_network_name", |
3588 | + net_tenant_id=None, network_id='0005'): |
3589 | + """ |
3590 | + Tests rename of a Virtual Network when Network does not exist. |
3591 | + """ |
3592 | + |
3593 | + LOG.debug("test_rename_network_DNE - START") |
3594 | + |
3595 | + if net_tenant_id: |
3596 | + tenant_id = net_tenant_id |
3597 | + else: |
3598 | + tenant_id = self.tenant_id |
3599 | + if network_id: |
3600 | + net_id = network_id |
3601 | + else: |
3602 | + net_id = self.net_id |
3603 | + |
3604 | + self.assertRaises(exc.NetworkNotFound, |
3605 | + self._cisco_nexus_plugin.rename_network, |
3606 | + new_name, tenant_id, net_id) |
3607 | + |
3608 | + LOG.debug("test_rename_network_DNE - END") |
3609 | + |
3610 | + def test_list_all_networks(self, net_tenant_id=None): |
3611 | + """ |
3612 | + Tests listing of all the Virtual Networks . |
3613 | + """ |
3614 | + |
3615 | + LOG.debug("test_list_all_networks - START") |
3616 | + if net_tenant_id: |
3617 | + tenant_id = net_tenant_id |
3618 | + else: |
3619 | + tenant_id = self.tenant_id |
3620 | + new_net_dict1 = self._cisco_nexus_plugin.create_network( |
3621 | + tenant_id, self.net_name, self.net_id, |
3622 | + self.vlan_name, self.vlan_id) |
3623 | + new_net_dict2 = self._cisco_nexus_plugin.create_network( |
3624 | + tenant_id, "New_Network2", "0011", |
3625 | + "second_vlan", "2003") |
3626 | + list_net_dict = self._cisco_nexus_plugin.get_all_networks(tenant_id) |
3627 | + net_temp_list = [new_net_dict1, new_net_dict2] |
3628 | + self.assertEqual(len(list_net_dict), 2) |
3629 | + self.assertTrue(list_net_dict[0] in net_temp_list) |
3630 | + self.assertTrue(list_net_dict[1] in net_temp_list) |
3631 | + self.tearDownNetwork(tenant_id, new_net_dict1[const.NET_ID]) |
3632 | + self.tearDownNetwork(tenant_id, new_net_dict2[const.NET_ID]) |
3633 | + LOG.debug("test_list_all_networks - END") |
3634 | + |
3635 | + def test_get_vlan_id_for_network(self, net_tenant_id=None, |
3636 | + network_id=None): |
3637 | + """ |
3638 | + Tests retrieval of vlan id for a Virtual Networks . |
3639 | + """ |
3640 | + |
3641 | + LOG.debug("test_get_vlan_id_for_network - START") |
3642 | + if net_tenant_id: |
3643 | + tenant_id = net_tenant_id |
3644 | + else: |
3645 | + tenant_id = self.tenant_id |
3646 | + if network_id: |
3647 | + net_id = network_id |
3648 | + else: |
3649 | + net_id = self.net_id |
3650 | + new_net_dict = self._cisco_nexus_plugin.create_network( |
3651 | + tenant_id, self.net_name, net_id, self.vlan_name, |
3652 | + self.vlan_id) |
3653 | + result_vlan_id = self._cisco_nexus_plugin._get_vlan_id_for_network( |
3654 | + tenant_id, net_id) |
3655 | + self.assertEqual(result_vlan_id, self.vlan_id) |
3656 | + self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID]) |
3657 | + LOG.debug("test_get_vlan_id_for_network - END") |
3658 | + |
3659 | + """ |
3660 | + Clean up functions after the tests |
3661 | + """ |
3662 | + |
3663 | + def tearDownNetwork(self, tenant_id, network_dict_id): |
3664 | + self._cisco_nexus_plugin.delete_network(tenant_id, network_dict_id) |
3665 | + |
3666 | +# def test_create_network(self): |
3667 | +# _test_create_network(self._cisco_nexus_plugin) |
3668 | + |
3669 | +# def test_delete_network(self): |
3670 | +# _test_delete_network(self._cisco_nexus_plugin) |
3671 | + |
3672 | +# def test_rename_network(self): |
3673 | +# _test_rename_network(self._cisco_nexus_plugin) |
3674 | + |
3675 | +# def test_show_network(self): |
3676 | +# _test_get_network_details(self._cisco_nexus_plugin) |
3677 | + |
3678 | +# def test_list_networks(self): |
3679 | +# _test_list_all_networks(self._cisco_nexus_plugin) |
3680 | |
3681 | === added file 'quantum/plugins/cisco/tests/unit/test_ucs_driver.py' |
3682 | --- quantum/plugins/cisco/tests/unit/test_ucs_driver.py 1970-01-01 00:00:00 +0000 |
3683 | +++ quantum/plugins/cisco/tests/unit/test_ucs_driver.py 2011-08-15 17:33:23 +0000 |
3684 | @@ -0,0 +1,165 @@ |
3685 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
3686 | +# |
3687 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
3688 | +# |
3689 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
3690 | +# not use this file except in compliance with the License. You may obtain |
3691 | +# a copy of the License at |
3692 | +# |
3693 | +# http://www.apache.org/licenses/LICENSE-2.0 |
3694 | +# |
3695 | +# Unless required by applicable law or agreed to in writing, software |
3696 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
3697 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
3698 | +# License for the specific language governing permissions and limitations |
3699 | +# under the License. |
3700 | +# |
3701 | +# @author: Shweta Padubidri, Cisco Systems, Inc. |
3702 | +# |
3703 | + |
3704 | +import logging |
3705 | +import unittest |
3706 | + |
3707 | +from quantum.plugins.cisco.ucs import cisco_ucs_network_driver |
3708 | + |
3709 | +LOG = logging.getLogger('quantum.tests.test_ucs_driver') |
3710 | + |
3711 | +create_vlan_output = "<configConfMos cookie=\"cookie_placeholder\" "\ |
3712 | +"inHierarchical=\"true\"> <inConfigs><pair key=\"fabric/lan/net-New Vlan\"> "\ |
3713 | +"<fabricVlan defaultNet=\"no\" dn=\"fabric/lan/net-New Vlan\" id=\"200\" "\ |
3714 | +"name=\"New Vlan\" status=\"created\"></fabricVlan> </pair> </inConfigs> "\ |
3715 | +"</configConfMos>" |
3716 | + |
3717 | +create_profile_output = "<configConfMos cookie=\"cookie_placeholder\" "\ |
3718 | +"inHierarchical=\"true\"> <inConfigs><pair key=\"fabric/lan/profiles/vnic-"\ |
3719 | +"New Profile\"> <vnicProfile descr=\"Profile created by Cisco OpenStack "\ |
3720 | +"Quantum Plugin\" dn=\"fabric/lan/profiles/vnic-New Profile\" maxPorts="\ |
3721 | +"\"64\" name=\"New Profile\" nwCtrlPolicyName=\"\" pinToGroupName=\"\" "\ |
3722 | +"qosPolicyName=\"\" status=\"created\"> <vnicEtherIf defaultNet=\"yes\" "\ |
3723 | +"name=\"New Vlan\" rn=\"if-New Vlan\" > </vnicEtherIf> </vnicProfile> "\ |
3724 | +"</pair> </inConfigs> </configConfMos>" |
3725 | + |
3726 | +change_vlan_output = "<configConfMos cookie=\"cookie_placeholder\" "\ |
3727 | +"inHierarchical=\"true\"> <inConfigs><pair key=\""\ |
3728 | +"fabric/lan/profiles/vnic-New Profile\"> <vnicProfile descr=\"Profile "\ |
3729 | +"created by Cisco OpenStack Quantum Plugin\" "\ |
3730 | +"dn=\"fabric/lan/profiles/vnic-New Profile\" maxPorts=\"64\" "\ |
3731 | +"name=\"New Profile\" nwCtrlPolicyName=\"\" pinToGroupName=\"\" "\ |
3732 | +"qosPolicyName=\"\" status=\"created,modified\"><vnicEtherIf "\ |
3733 | +"rn=\"if-Old Vlan\" status=\"deleted\"> </vnicEtherIf> "\ |
3734 | +"<vnicEtherIf defaultNet=\"yes\" name=\"New Vlan\" rn=\"if-New Vlan\" > "\ |
3735 | +"</vnicEtherIf> </vnicProfile> </pair></inConfigs> </configConfMos>" |
3736 | + |
3737 | +delete_vlan_output = "<configConfMos cookie=\"cookie_placeholder\" "\ |
3738 | +"inHierarchical=\"true\"> <inConfigs><pair key=\"fabric/lan/net-New Vlan\"> "\ |
3739 | +"<fabricVlan dn=\"fabric/lan/net-New Vlan\" status=\"deleted\"> "\ |
3740 | +"</fabricVlan> </pair> </inConfigs></configConfMos>" |
3741 | + |
3742 | +delete_profile_output = "<configConfMos cookie=\"cookie_placeholder\" "\ |
3743 | +"inHierarchical=\"false\"> <inConfigs><pair key=\""\ |
3744 | +"fabric/lan/profiles/vnic-New Profile\"> <vnicProfile "\ |
3745 | +"dn=\"fabric/lan/profiles/vnic-New Profile\" status=\"deleted\"> "\ |
3746 | +"</vnicProfile></pair> </inConfigs> </configConfMos>" |
3747 | + |
3748 | +associate_profile_output = "<configConfMos cookie=\"cookie_placeholder\" "\ |
3749 | +"inHierarchical=\"true\"> <inConfigs> <pair key="\ |
3750 | +"\"fabric/lan/profiles/vnic-New Profile/cl-New Profile Client\">"\ |
3751 | +" <vmVnicProfCl dcName=\".*\" descr=\"\" dn=\"fabric/lan/profiles/vnic-"\ |
3752 | +"New Profile/cl-New Profile Client\"name=\"New Profile Client\" "\ |
3753 | +"orgPath=\".*\" status=\"created\" swName=\"default$\"> </vmVnicProfCl>" \ |
3754 | +"</pair> </inConfigs> </configConfMos>" |
3755 | + |
3756 | + |
3757 | +class TestUCSDriver(unittest.TestCase): |
3758 | + |
3759 | + def setUp(self): |
3760 | + self._ucsmDriver = cisco_ucs_network_driver.CiscoUCSMDriver() |
3761 | + self.vlan_name = 'New Vlan' |
3762 | + self.vlan_id = '200' |
3763 | + self.profile_name = 'New Profile' |
3764 | + self.old_vlan_name = 'Old Vlan' |
3765 | + self.profile_client_name = 'New Profile Client' |
3766 | + |
3767 | + def test_create_vlan_post_data(self, expected_output=create_vlan_output): |
3768 | + """ |
3769 | + Tests creation of vlan post Data |
3770 | + """ |
3771 | + |
3772 | + LOG.debug("test_create_vlan") |
3773 | + vlan_details = self._ucsmDriver._create_vlan_post_data( |
3774 | + self.vlan_name, self.vlan_id) |
3775 | + self.assertEqual(vlan_details, expected_output) |
3776 | + LOG.debug("test_create_vlan - END") |
3777 | + |
3778 | + def test_create_profile_post_data( |
3779 | + self, expected_output=create_profile_output): |
3780 | + """ |
3781 | + Tests creation of profile post Data |
3782 | + """ |
3783 | + |
3784 | + LOG.debug("test_create_profile_post_data - START") |
3785 | + profile_details = self._ucsmDriver._create_profile_post_data( |
3786 | + self.profile_name, self.vlan_name) |
3787 | + self.assertEqual(profile_details, expected_output) |
3788 | + LOG.debug("test_create_profile_post - END") |
3789 | + |
3790 | + def test_change_vlan_in_profile_post_data( |
3791 | + self, expected_output=change_vlan_output): |
3792 | + """ |
3793 | + Tests creation of change vlan in profile post Data |
3794 | + """ |
3795 | + |
3796 | + LOG.debug("test_create_profile_post_data - START") |
3797 | + profile_details = self._ucsmDriver._change_vlan_in_profile_post_data( |
3798 | + self.profile_name, self.old_vlan_name, self.vlan_name) |
3799 | + self.assertEqual(profile_details, expected_output) |
3800 | + LOG.debug("test_create_profile_post - END") |
3801 | + |
3802 | + def test_delete_vlan_post_data(self, expected_output=delete_vlan_output): |
3803 | + LOG.debug("test_create_profile_post_data - START") |
3804 | + """ |
3805 | + Tests deletion of vlan post Data |
3806 | + """ |
3807 | + |
3808 | + vlan_details = self._ucsmDriver._create_vlan_post_data( |
3809 | + self.vlan_name, self.vlan_id) |
3810 | + vlan_delete_details = self._ucsmDriver._delete_vlan_post_data( |
3811 | + self.vlan_name) |
3812 | + self.assertEqual(vlan_delete_details, expected_output) |
3813 | + LOG.debug("test_create_profile_post - END") |
3814 | + |
3815 | + def test_delete_profile_post_data( |
3816 | + self, expected_output=delete_profile_output): |
3817 | + """ |
3818 | + Tests deletion of profile post Data |
3819 | + """ |
3820 | + |
3821 | + LOG.debug("test_create_profile_post_data - START") |
3822 | + profile_details = self._ucsmDriver._create_profile_post_data( |
3823 | + self.profile_name, self.vlan_name) |
3824 | + profile_delete_details = self._ucsmDriver._delete_profile_post_data( |
3825 | + self.profile_name) |
3826 | + self.assertEqual(profile_delete_details, expected_output) |
3827 | + LOG.debug("test_create_profile_post - END") |
3828 | + |
3829 | + def test_create_profile_client_post_data( |
3830 | + self, expected_output=associate_profile_output): |
3831 | + """ |
3832 | + Tests creation of profile client post Data |
3833 | + """ |
3834 | + |
3835 | + LOG.debug("test_create_profile_client_post_data - START") |
3836 | + profile_details = self._ucsmDriver._create_profile_client_post_data( |
3837 | + self.profile_name, self.profile_client_name) |
3838 | + self.assertEqual(profile_details, expected_output) |
3839 | + LOG.debug("test_create_profile_post - END") |
3840 | + |
3841 | + def test_get_next_dynamic_nic(self): |
3842 | + """ |
3843 | + Tests get next dynamic nic |
3844 | + """ |
3845 | + |
3846 | + LOG.debug("test_get_next_dynamic_nic - START") |
3847 | + dynamic_nic_id = self._ucsmDriver._get_next_dynamic_nic() |
3848 | + self.assertTrue(len(dynamic_nic_id) > 0) |
3849 | + LOG.debug("test_get_next_dynamic_nic - END") |
3850 | |
3851 | === added file 'quantum/plugins/cisco/tests/unit/test_ucs_plugin.py' |
3852 | --- quantum/plugins/cisco/tests/unit/test_ucs_plugin.py 1970-01-01 00:00:00 +0000 |
3853 | +++ quantum/plugins/cisco/tests/unit/test_ucs_plugin.py 2011-08-15 17:33:23 +0000 |
3854 | @@ -0,0 +1,480 @@ |
3855 | +#vim: tabstop=4 shiftwidth=4 softtabstop=4 |
3856 | +#copyright 2011 Cisco Systems, Inc. All rights reserved. |
3857 | +# |
3858 | +# Licensed under the Apache License, Version 2.0(the "License"); you may |
3859 | +# not use this file except in compliance with the License. You may obtain |
3860 | +# a copy of the License at |
3861 | +# |
3862 | +# http://www.apache.org/licenses/LICENSE-2.0 |
3863 | +# |
3864 | +# Unless required by applicable law or agreed to in writing, software |
3865 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
3866 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
3867 | +# License for the specific language governing permissions and limitations |
3868 | +# under the License. |
3869 | +# |
3870 | +# @author: Shubhangi Satras, Cisco Systems, Inc. |
3871 | +# |
3872 | +import unittest |
3873 | +import logging as LOG |
3874 | +from quantum.common import exceptions as exc |
3875 | +from quantum.plugins.cisco.common import cisco_constants as const |
3876 | +from quantum.plugins.cisco.ucs import cisco_ucs_plugin |
3877 | +from quantum.plugins.cisco.ucs import cisco_ucs_configuration as conf |
3878 | + |
3879 | +LOG.basicConfig(level=LOG.WARN) |
3880 | +LOG.getLogger("cisco_plugin") |
3881 | + |
3882 | + |
3883 | +class UCSVICTestPlugin(unittest.TestCase): |
3884 | + |
3885 | + def setUp(self): |
3886 | + |
3887 | + self.tenant_id = "test_tenant_cisco12" |
3888 | + self.net_name = "test_network_cisco12" |
3889 | + self.net_id = 000007 |
3890 | + self.vlan_name = "q-" + str(self.net_id) + "vlan" |
3891 | + self.vlan_id = 266 |
3892 | + self.port_id = "4" |
3893 | + self._cisco_ucs_plugin = cisco_ucs_plugin.UCSVICPlugin() |
3894 | + |
3895 | + def test_create_network(self): |
3896 | + """ |
3897 | + Tests creation of new Virtual Network. |
3898 | + """ |
3899 | + LOG.debug("UCSVICTestPlugin:_test_create_network() called\n") |
3900 | + new_net_dict = self._cisco_ucs_plugin.create_network( |
3901 | + self.tenant_id, self.net_name, self.net_id, |
3902 | + self.vlan_name, self.vlan_id) |
3903 | + self.assertEqual(new_net_dict[const.NET_ID], self.net_id) |
3904 | + self.assertEqual(new_net_dict[const.NET_NAME], self.net_name) |
3905 | + self.assertEqual(new_net_dict[const.NET_VLAN_NAME], self.vlan_name) |
3906 | + self.assertEqual(new_net_dict[const.NET_VLAN_ID], self.vlan_id) |
3907 | + self.tearDownNetwork(self.tenant_id, self.net_id) |
3908 | + |
3909 | + def test_delete_network(self): |
3910 | + """ |
3911 | + Tests deletion of the network with the specified network identifier |
3912 | + belonging to the specified tenant. |
3913 | + """ |
3914 | + LOG.debug("UCSVICTestPlugin:test_delete_network() called\n") |
3915 | + self._cisco_ucs_plugin.create_network( |
3916 | + self.tenant_id, self.net_name, self.net_id, |
3917 | + self.vlan_name, self.vlan_id) |
3918 | + new_net_dict = self._cisco_ucs_plugin.delete_network( |
3919 | + self.tenant_id, self.net_id) |
3920 | + self.assertEqual(new_net_dict[const.NET_ID], self.net_id) |
3921 | + |
3922 | + def test_get_network_details(self): |
3923 | + """ |
3924 | + Tests the deletion the Virtual Network belonging to a the |
3925 | + spec |
3926 | + """ |
3927 | + LOG.debug("UCSVICTestPlugin:test_get_network_details() called\n") |
3928 | + self._cisco_ucs_plugin.create_network( |
3929 | + self.tenant_id, self.net_name, self.net_id, |
3930 | + self.vlan_name, self.vlan_id) |
3931 | + new_net_dict = self._cisco_ucs_plugin.get_network_details( |
3932 | + self.tenant_id, self.net_id) |
3933 | + self.assertEqual(new_net_dict[const.NET_ID], self.net_id) |
3934 | + self.assertEqual(new_net_dict[const.NET_VLAN_NAME], self.vlan_name) |
3935 | + self.assertEqual(new_net_dict[const.NET_VLAN_ID], self.vlan_id) |
3936 | + self.tearDownNetwork(self.tenant_id, self.net_id) |
3937 | + |
3938 | + def test_get_all_networks(self): |
3939 | + """ |
3940 | + Tests whether dictionary is returned containing all |
3941 | + <network_uuid, network_name> for |
3942 | + the specified tenant. |
3943 | + """ |
3944 | + LOG.debug("UCSVICTestPlugin:test_get_all_networks() called\n") |
3945 | + new_net_dict1 = self._cisco_ucs_plugin.create_network( |
3946 | + self.tenant_id, self.net_name, self.net_id, |
3947 | + self.vlan_name, self.vlan_id) |
3948 | + new_net_dict2 = self._cisco_ucs_plugin.create_network( |
3949 | + self.tenant_id, "test_network2", |
3950 | + 000006, "q-000006vlan", "6") |
3951 | + net_list = self._cisco_ucs_plugin.get_all_networks(self.tenant_id) |
3952 | + net_id_list = [new_net_dict1, new_net_dict2] |
3953 | + self.assertTrue(net_list[0] in net_id_list) |
3954 | + self.assertTrue(net_list[1] in net_id_list) |
3955 | + self.tearDownNetwork(self.tenant_id, new_net_dict1[const.NET_ID]) |
3956 | + self.tearDownNetwork(self.tenant_id, new_net_dict2[const.NET_ID]) |
3957 | + |
3958 | + def test_get_all_ports(self): |
3959 | + """ |
3960 | + Retrieves all port identifiers belonging to the |
3961 | + specified Virtual Network. |
3962 | + """ |
3963 | + LOG.debug("UCSVICPlugin:get_all_ports() called\n") |
3964 | + new_net_dict = self._cisco_ucs_plugin.create_network( |
3965 | + self.tenant_id, self.net_name, self.net_id, |
3966 | + self.vlan_name, self.vlan_id) |
3967 | + port_dict1 = self._cisco_ucs_plugin.create_port( |
3968 | + self.tenant_id, self.net_id, const.PORT_UP, |
3969 | + self.port_id) |
3970 | + port_dict2 = self._cisco_ucs_plugin.create_port( |
3971 | + self.tenant_id, self.net_id, |
3972 | + const.PORT_UP, "10") |
3973 | + ports_on_net = self._cisco_ucs_plugin.get_all_ports( |
3974 | + self.tenant_id, self.net_id) |
3975 | + port_list = [port_dict1, port_dict2] |
3976 | + self.assertTrue(port_list[0] in ports_on_net) |
3977 | + self.assertTrue(port_list[1] in ports_on_net) |
3978 | + self._cisco_ucs_plugin.delete_port(self.tenant_id, self.net_id, |
3979 | + self.port_id) |
3980 | + self.tearDownNetworkPort(self.tenant_id, new_net_dict[const.NET_ID], |
3981 | + port_dict2[const.PORT_ID]) |
3982 | + |
3983 | + def _test_rename_network(self, new_name): |
3984 | + """ |
3985 | + Tests whether symbolic name is updated for the particular |
3986 | + Virtual Network. |
3987 | + """ |
3988 | + LOG.debug("UCSVICTestPlugin:_test_rename_network() called\n") |
3989 | + self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name, |
3990 | + self.net_id, self.vlan_name, |
3991 | + self.vlan_id) |
3992 | + new_net_dict = self._cisco_ucs_plugin.rename_network( |
3993 | + self.tenant_id, self.net_id, new_name) |
3994 | + self.assertEqual(new_net_dict[const.NET_NAME], new_name) |
3995 | + self.tearDownNetwork(self.tenant_id, self.net_id) |
3996 | + |
3997 | + def test_rename_network(self): |
3998 | + self._test_rename_network("new_test_network1") |
3999 | + |
4000 | + def _test_create_port(self, port_state): |
4001 | + """ |
4002 | + Tests creation of a port on the specified Virtual Network. |
4003 | + """ |
4004 | + LOG.debug("UCSVICTestPlugin:_test_create_port() called\n") |
4005 | + self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name, |
4006 | + self.net_id, self.vlan_name, |
4007 | + self.vlan_id) |
4008 | + new_port_dict = self._cisco_ucs_plugin.create_port( |
4009 | + self.tenant_id, self.net_id, port_state, self.port_id) |
4010 | + self.assertEqual(new_port_dict[const.PORT_ID], self.port_id) |
4011 | + self.assertEqual(new_port_dict[const.PORT_STATE], port_state) |
4012 | + self.assertEqual(new_port_dict[const.ATTACHMENT], None) |
4013 | + profile_name = self._cisco_ucs_plugin._get_profile_name(self.port_id) |
4014 | + new_port_profile = new_port_dict[const.PORT_PROFILE] |
4015 | + self.assertEqual(new_port_profile[const.PROFILE_NAME], profile_name) |
4016 | + self.assertEqual(new_port_profile[const.PROFILE_VLAN_NAME], |
4017 | + conf.DEFAULT_VLAN_NAME) |
4018 | + self.assertEqual(new_port_profile[const.PROFILE_VLAN_ID], |
4019 | + conf.DEFAULT_VLAN_ID) |
4020 | + self.tearDownNetworkPort(self.tenant_id, self.net_id, self.port_id) |
4021 | + |
4022 | + def test_create_port(self): |
4023 | + self._test_create_port(const.PORT_UP) |
4024 | + |
4025 | + def _test_delete_port(self, port_state): |
4026 | + """ |
4027 | + Tests Deletion of a port on a specified Virtual Network, |
4028 | + if the port contains a remote interface attachment, |
4029 | + the remote interface should first be un-plugged and |
4030 | + then the port can be deleted. |
4031 | + """ |
4032 | + LOG.debug("UCSVICTestPlugin:_test_delete_port() called\n") |
4033 | + self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name, |
4034 | + self.net_id, self.vlan_name, |
4035 | + self.vlan_id) |
4036 | + self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id, |
4037 | + port_state, self.port_id) |
4038 | + self._cisco_ucs_plugin.delete_port(self.tenant_id, self.net_id, |
4039 | + self.port_id) |
4040 | + net = self._cisco_ucs_plugin._get_network(self.tenant_id, self.net_id) |
4041 | + self.assertEqual(net[const.NET_PORTS], {}) |
4042 | + self.tearDownNetwork(self.tenant_id, self.net_id) |
4043 | + |
4044 | + def test_delete_port(self): |
4045 | + self._test_delete_port(const.PORT_UP) |
4046 | + |
4047 | + def _test_update_port(self, port_state): |
4048 | + """ |
4049 | + Tests Updation of the state of a port on the specified Virtual Network. |
4050 | + """ |
4051 | + LOG.debug("UCSVICTestPlugin:_test_update_port() called\n") |
4052 | + self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name, |
4053 | + self.net_id, self.vlan_name, |
4054 | + self.vlan_id) |
4055 | + self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id, |
4056 | + port_state, self.port_id) |
4057 | + port = self._cisco_ucs_plugin.update_port( |
4058 | + self.tenant_id, self.net_id, |
4059 | + self.port_id, port_state) |
4060 | + self.assertEqual(port[const.PORT_STATE], port_state) |
4061 | + self.tearDownNetworkPort(self.tenant_id, self.net_id, self.port_id) |
4062 | + |
4063 | + def test_update_port_state_up(self): |
4064 | + self._test_update_port(const.PORT_UP) |
4065 | + |
4066 | + def test_update_port_state_down(self): |
4067 | + self._test_update_port(const.PORT_DOWN) |
4068 | + |
4069 | + def _test_get_port_details_state_up(self, port_state): |
4070 | + """ |
4071 | + Tests whether user is able to retrieve a remote interface |
4072 | + that is attached to this particular port when port state is Up. |
4073 | + """ |
4074 | + LOG.debug("UCSVICTestPlugin:_test_get_port_details_state_up()" + |
4075 | + "called\n") |
4076 | + self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name, |
4077 | + self.net_id, self.vlan_name, |
4078 | + self.vlan_id) |
4079 | + self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id, |
4080 | + port_state, self.port_id) |
4081 | + port = self._cisco_ucs_plugin.get_port_details( |
4082 | + self.tenant_id, self.net_id, self.port_id) |
4083 | + self.assertEqual(port[const.PORT_ID], self.port_id) |
4084 | + self.assertEqual(port[const.PORT_STATE], port_state) |
4085 | + self.assertEqual(port[const.ATTACHMENT], None) |
4086 | + new_port_profile = port[const.PORT_PROFILE] |
4087 | + profile_name = self._cisco_ucs_plugin._get_profile_name(self.port_id) |
4088 | + self.assertEqual(new_port_profile[const.PROFILE_VLAN_NAME], |
4089 | + conf.DEFAULT_VLAN_NAME) |
4090 | + self.assertEqual(new_port_profile[const.PROFILE_VLAN_ID], |
4091 | + conf.DEFAULT_VLAN_ID) |
4092 | + self.assertEqual(new_port_profile[const.PROFILE_NAME], profile_name) |
4093 | + self.tearDownNetworkPort(self.tenant_id, self.net_id, self.port_id) |
4094 | + |
4095 | + def _test_get_port_details_state_down(self, port_state): |
4096 | + """ |
4097 | + Tests whether user is able to retrieve a remote interface |
4098 | + that is attached to this particular port when port state is down. |
4099 | + """ |
4100 | + LOG.debug("UCSVICTestPlugin:_test_get_port_details_state_down()" + |
4101 | + "called\n") |
4102 | + self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name, |
4103 | + self.net_id, self.vlan_name, |
4104 | + self.vlan_id) |
4105 | + self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id, |
4106 | + port_state, self.port_id) |
4107 | + port = self._cisco_ucs_plugin.get_port_details(self.tenant_id, |
4108 | + self.net_id, |
4109 | + self.port_id) |
4110 | + self.assertEqual(port[const.PORT_ID], self.port_id) |
4111 | + self.assertNotEqual(port[const.PORT_STATE], port_state) |
4112 | + self.assertEqual(port[const.ATTACHMENT], None) |
4113 | + new_port_profile = port[const.PORT_PROFILE] |
4114 | + profile_name = self._cisco_ucs_plugin._get_profile_name(self.port_id) |
4115 | + self.assertEqual(new_port_profile[const.PROFILE_VLAN_NAME], |
4116 | + conf.DEFAULT_VLAN_NAME) |
4117 | + self.assertEqual(new_port_profile[const.PROFILE_VLAN_ID], |
4118 | + conf.DEFAULT_VLAN_ID) |
4119 | + self.assertEqual(new_port_profile[const.PROFILE_NAME], profile_name) |
4120 | + self.tearDownNetworkPort(self.tenant_id, self.net_id, self.port_id) |
4121 | + |
4122 | + def test_get_port_details_state_up(self): |
4123 | + self._test_get_port_details_state_up(const.PORT_UP) |
4124 | + |
4125 | + def test_get_port_details_state_down(self): |
4126 | + self._test_get_port_details_state_down(const.PORT_DOWN) |
4127 | + |
4128 | + def test_create_port_profile(self): |
4129 | + LOG.debug("UCSVICTestPlugin:test_create_port_profile() called\n") |
4130 | + new_port_profile = self._cisco_ucs_plugin._create_port_profile( |
4131 | + self.tenant_id, self.net_id, self.port_id, |
4132 | + self.vlan_name, self.vlan_id) |
4133 | + profile_name = self._cisco_ucs_plugin._get_profile_name(self.port_id) |
4134 | + self.assertEqual(new_port_profile[const.PROFILE_NAME], profile_name) |
4135 | + self.assertEqual(new_port_profile[const.PROFILE_VLAN_NAME], |
4136 | + self.vlan_name) |
4137 | + self.assertEqual(new_port_profile[const.PROFILE_VLAN_ID], self.vlan_id) |
4138 | + self._cisco_ucs_plugin._delete_port_profile(self.port_id, profile_name) |
4139 | + |
4140 | + def test_delete_port_profile(self): |
4141 | + LOG.debug("UCSVICTestPlugin:test_delete_port_profile() called\n") |
4142 | + self._cisco_ucs_plugin._create_port_profile( |
4143 | + self.tenant_id, self.net_id, self.port_id, self.vlan_name, |
4144 | + self.vlan_id) |
4145 | + profile_name = self._cisco_ucs_plugin._get_profile_name(self.port_id) |
4146 | + counter1 = self._cisco_ucs_plugin._port_profile_counter |
4147 | + self._cisco_ucs_plugin._delete_port_profile(self.port_id, |
4148 | + profile_name) |
4149 | + counter2 = self._cisco_ucs_plugin._port_profile_counter |
4150 | + self.assertNotEqual(counter1, counter2) |
4151 | + |
4152 | + def _test_plug_interface(self, remote_interface_id): |
4153 | + """ |
4154 | + Attaches a remote interface to the specified port on the |
4155 | + specified Virtual Network. |
4156 | + """ |
4157 | + LOG.debug("UCSVICTestPlugin:_test_plug_interface() called\n") |
4158 | + self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name, |
4159 | + self.net_id, self.vlan_name, |
4160 | + self.vlan_id) |
4161 | + self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id, |
4162 | + const.PORT_UP, self.port_id) |
4163 | + self._cisco_ucs_plugin.plug_interface(self.tenant_id, self.net_id, |
4164 | + self.port_id, |
4165 | + remote_interface_id) |
4166 | + port = self._cisco_ucs_plugin._get_port( |
4167 | + self.tenant_id, self.net_id, self.port_id) |
4168 | + self.assertEqual(port[const.ATTACHMENT], remote_interface_id) |
4169 | + port_profile = port[const.PORT_PROFILE] |
4170 | + profile_name = port_profile[const.PROFILE_NAME] |
4171 | + new_vlan_name = self._cisco_ucs_plugin._get_vlan_name_for_network( |
4172 | + self.tenant_id, self.net_id) |
4173 | + new_vlan_id = self._cisco_ucs_plugin._get_vlan_id_for_network( |
4174 | + self.tenant_id, self.net_id) |
4175 | + self.assertEqual(port_profile[const.PROFILE_VLAN_NAME], new_vlan_name) |
4176 | + self.assertEqual(port_profile[const.PROFILE_VLAN_ID], new_vlan_id) |
4177 | + self.tearDownNetworkPortInterface(self.tenant_id, self.net_id, |
4178 | + self.port_id) |
4179 | + |
4180 | + def test_plug_interface(self): |
4181 | + self._test_plug_interface("4") |
4182 | + |
4183 | + def _test_unplug_interface(self, remote_interface_id): |
4184 | + """ |
4185 | + Tests whether remote interface detaches from the specified port on the |
4186 | + specified Virtual Network. |
4187 | + """ |
4188 | + LOG.debug("UCSVICTestPlugin:_test_unplug_interface() called\n") |
4189 | + self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name, |
4190 | + self.net_id, self.vlan_name, |
4191 | + self.vlan_id) |
4192 | + self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id, |
4193 | + const.PORT_UP, self.port_id) |
4194 | + self._cisco_ucs_plugin.plug_interface(self.tenant_id, self.net_id, |
4195 | + self.port_id, |
4196 | + remote_interface_id) |
4197 | + self._cisco_ucs_plugin.unplug_interface(self.tenant_id, self.net_id, |
4198 | + self.port_id) |
4199 | + port = self._cisco_ucs_plugin._get_port( |
4200 | + self.tenant_id, self.net_id, self.port_id) |
4201 | + self.assertEqual(port[const.ATTACHMENT], None) |
4202 | + port_profile = port[const.PORT_PROFILE] |
4203 | + profile_name = port_profile[const.PROFILE_NAME] |
4204 | + self.assertEqual(port_profile[const.PROFILE_VLAN_NAME], |
4205 | + conf.DEFAULT_VLAN_NAME) |
4206 | + self.assertEqual(port_profile[const.PROFILE_VLAN_ID], |
4207 | + conf.DEFAULT_VLAN_ID) |
4208 | + self.tearDownNetworkPort(self.tenant_id, self.net_id, self.port_id) |
4209 | + |
4210 | + def test_unplug_interface(self): |
4211 | + self._test_unplug_interface("4") |
4212 | + |
4213 | + def test_get_vlan_name_for_network(self): |
4214 | + LOG.debug("UCSVICTestPlugin:test_get_vlan_name_for_network() called\n") |
4215 | + net = self._cisco_ucs_plugin.create_network( |
4216 | + self.tenant_id, self.net_name, self.net_id, |
4217 | + self.vlan_name, self.vlan_id) |
4218 | + self.assertEqual(net[const.NET_VLAN_NAME], self.vlan_name) |
4219 | + self.tearDownNetwork(self.tenant_id, self.net_id) |
4220 | + |
4221 | + def test_get_vlan_id_for_network(self): |
4222 | + LOG.debug("UCSVICTestPlugin:test_get_vlan_id_for_network() called\n") |
4223 | + net = self._cisco_ucs_plugin.create_network( |
4224 | + self.tenant_id, self.net_name, self.net_id, self.vlan_name, |
4225 | + self.vlan_id) |
4226 | + self.assertEqual(net[const.NET_VLAN_ID], self.vlan_id) |
4227 | + self.tearDownNetwork(self.tenant_id, self.net_id) |
4228 | + |
4229 | + def test_get_network(self): |
4230 | + LOG.debug("UCSVICTestPlugin:test_get_network() called\n") |
4231 | + net = self._cisco_ucs_plugin.create_network( |
4232 | + self.tenant_id, self.net_name, self.net_id, self.vlan_name, |
4233 | + self.vlan_id) |
4234 | + self.assertEqual(net[const.NET_ID], self.net_id) |
4235 | + self.tearDownNetwork(self.tenant_id, self.net_id) |
4236 | + |
4237 | + def test_get_port(self): |
4238 | + LOG.debug("UCSVICTestPlugin:test_get_port() called\n") |
4239 | + self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name, |
4240 | + self.net_id, self.vlan_name, |
4241 | + self.vlan_id) |
4242 | + new_port_dict = self._cisco_ucs_plugin.create_port( |
4243 | + self.tenant_id, self.net_id, |
4244 | + const.PORT_UP, self.port_id) |
4245 | + self.assertEqual(new_port_dict[const.PORT_ID], self.port_id) |
4246 | + self.tearDownNetworkPort(self.tenant_id, self.net_id, self.port_id) |
4247 | + |
4248 | + def test_get_network_NetworkNotFound(self): |
4249 | + self.assertRaises(exc.NetworkNotFound, |
4250 | + self._cisco_ucs_plugin._get_network, |
4251 | + *(self.tenant_id, self.net_id)) |
4252 | + |
4253 | + def test_delete_network_NetworkNotFound(self): |
4254 | + self.assertRaises(exc.NetworkNotFound, |
4255 | + self._cisco_ucs_plugin.delete_network, |
4256 | + *(self.tenant_id, self.net_id)) |
4257 | + |
4258 | + def test_delete_port_PortInUse(self): |
4259 | + self._test_delete_port_PortInUse("4") |
4260 | + |
4261 | + def _test_delete_port_PortInUse(self, remote_interface_id): |
4262 | + self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name, |
4263 | + self.net_id, self.vlan_name, |
4264 | + self.vlan_id) |
4265 | + self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id, |
4266 | + const.PORT_UP, self.port_id) |
4267 | + self._cisco_ucs_plugin.plug_interface(self.tenant_id, self.net_id, |
4268 | + self.port_id, |
4269 | + remote_interface_id) |
4270 | + self.assertRaises(exc.PortInUse, self._cisco_ucs_plugin.delete_port, |
4271 | + *(self.tenant_id, self.net_id, self.port_id)) |
4272 | + self.tearDownNetworkPortInterface(self.tenant_id, self.net_id, |
4273 | + self.port_id) |
4274 | + |
4275 | + def test_delete_port_PortNotFound(self): |
4276 | + self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name, |
4277 | + self.net_id, self.vlan_name, |
4278 | + self.vlan_id) |
4279 | + self.assertRaises(exc.PortNotFound, self._cisco_ucs_plugin.delete_port, |
4280 | + *(self.tenant_id, self.net_id, self.port_id)) |
4281 | + self.tearDownNetwork(self.tenant_id, self.net_id) |
4282 | + |
4283 | + def test_plug_interface_PortInUse(self): |
4284 | + self._test_plug_interface_PortInUse("6", "5") |
4285 | + |
4286 | + def _test_plug_interface_PortInUse(self, remote_interface_id1, |
4287 | + remote_interface_id2): |
4288 | + LOG.debug("UCSVICTestPlugin:_test_plug_interface_PortInUse() called\n") |
4289 | + self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name, |
4290 | + self.net_id, self.vlan_name, |
4291 | + self.vlan_id) |
4292 | + self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id, |
4293 | + const.PORT_UP, self.port_id) |
4294 | + self._cisco_ucs_plugin.plug_interface(self.tenant_id, self.net_id, |
4295 | + self.port_id, |
4296 | + remote_interface_id1) |
4297 | + self.assertRaises(exc.PortInUse, self._cisco_ucs_plugin.plug_interface, |
4298 | + *(self.tenant_id, self.net_id, self.port_id, |
4299 | + remote_interface_id2)) |
4300 | + self.tearDownNetworkPortInterface(self.tenant_id, self.net_id, |
4301 | + self.port_id) |
4302 | + |
4303 | + def test_validate_attachment_AlreadyAttached(self): |
4304 | + LOG.debug("UCSVICTestPlugin:testValidateAttachmentAlreadyAttached") |
4305 | + self._test_validate_attachment_AlreadyAttached("4") |
4306 | + |
4307 | + def _test_validate_attachment_AlreadyAttached(self, remote_interface_id): |
4308 | + LOG.debug("UCSVICTestPlugin:_test_validate_attachmentAlreadyAttached") |
4309 | + self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name, |
4310 | + self.net_id, self.vlan_name, |
4311 | + self.vlan_id) |
4312 | + self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id, |
4313 | + const.PORT_UP, self.port_id) |
4314 | + self._cisco_ucs_plugin.plug_interface(self.tenant_id, self.net_id, |
4315 | + self.port_id, |
4316 | + remote_interface_id) |
4317 | + self.assertRaises( |
4318 | + exc.AlreadyAttached, self._cisco_ucs_plugin._validate_attachment, |
4319 | + *(self.tenant_id, self.net_id, self.port_id, remote_interface_id)) |
4320 | + self.tearDownNetworkPortInterface(self.tenant_id, self.net_id, |
4321 | + self.port_id) |
4322 | + |
4323 | + def tearDownNetwork(self, tenant_id, net_id): |
4324 | + self._cisco_ucs_plugin.delete_network(tenant_id, net_id) |
4325 | + |
4326 | + def tearDownNetworkPort(self, tenant_id, net_id, port_id): |
4327 | + self._cisco_ucs_plugin.delete_port(tenant_id, net_id, |
4328 | + port_id) |
4329 | + self.tearDownNetwork(tenant_id, net_id) |
4330 | + |
4331 | + def tearDownNetworkPortInterface(self, tenant_id, net_id, port_id): |
4332 | + self._cisco_ucs_plugin.unplug_interface(tenant_id, net_id, |
4333 | + port_id) |
4334 | + self.tearDownNetworkPort(tenant_id, net_id, port_id) |
4335 | |
4336 | === added directory 'quantum/plugins/cisco/ucs' |
4337 | === added file 'quantum/plugins/cisco/ucs/__init__.py' |
4338 | --- quantum/plugins/cisco/ucs/__init__.py 1970-01-01 00:00:00 +0000 |
4339 | +++ quantum/plugins/cisco/ucs/__init__.py 2011-08-15 17:33:23 +0000 |
4340 | @@ -0,0 +1,18 @@ |
4341 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
4342 | +# |
4343 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
4344 | +# |
4345 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
4346 | +# not use this file except in compliance with the License. You may obtain |
4347 | +# a copy of the License at |
4348 | +# |
4349 | +# http://www.apache.org/licenses/LICENSE-2.0 |
4350 | +# |
4351 | +# Unless required by applicable law or agreed to in writing, software |
4352 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
4353 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
4354 | +# License for the specific language governing permissions and limitations |
4355 | +# under the License. |
4356 | +# |
4357 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
4358 | +# |
4359 | |
4360 | === added file 'quantum/plugins/cisco/ucs/cisco_getvif.py' |
4361 | --- quantum/plugins/cisco/ucs/cisco_getvif.py 1970-01-01 00:00:00 +0000 |
4362 | +++ quantum/plugins/cisco/ucs/cisco_getvif.py 2011-08-15 17:33:23 +0000 |
4363 | @@ -0,0 +1,51 @@ |
4364 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
4365 | +# |
4366 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
4367 | +# |
4368 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
4369 | +# not use this file except in compliance with the License. You may obtain |
4370 | +# a copy of the License at |
4371 | +# |
4372 | +# http://www.apache.org/licenses/LICENSE-2.0 |
4373 | +# |
4374 | +# Unless required by applicable law or agreed to in writing, software |
4375 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
4376 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
4377 | +# License for the specific language governing permissions and limitations |
4378 | +# under the License. |
4379 | +# |
4380 | +# @author: Rohit Agarwalla, Cisco Systems Inc. |
4381 | +# |
4382 | +import sys |
4383 | +import subprocess |
4384 | + |
4385 | + |
4386 | +def get_next_dynic(argv=[]): |
4387 | + cmd = ["ifconfig", "-a"] |
4388 | + f_cmd_output = subprocess.Popen(cmd, stdout=subprocess.PIPE).\ |
4389 | + communicate()[0] |
4390 | + eths = [lines.split(' ')[0] for lines in f_cmd_output.splitlines() \ |
4391 | + if "eth" in lines] |
4392 | + #print eths |
4393 | + for eth in eths: |
4394 | + cmd = ["ethtool", "-i", eth] |
4395 | + f_cmd_output = subprocess.Popen(cmd, stdout=subprocess.PIPE).\ |
4396 | + communicate()[0] |
4397 | + bdf = [lines.split(' ')[1] for lines in f_cmd_output.splitlines() \ |
4398 | + if "bus-info" in lines] |
4399 | + #print bdf |
4400 | + cmd = ["lspci", "-n", "-s", bdf[0]] |
4401 | + f_cmd_output = subprocess.Popen(cmd, stdout=subprocess.PIPE).\ |
4402 | + communicate()[0] |
4403 | + deviceid = [(lines.split(':')[3]).split(' ')[0] \ |
4404 | + for lines in f_cmd_output.splitlines()] |
4405 | + #print deviceid |
4406 | + if deviceid[0] == "0044": |
4407 | + cmd = ["/sbin/ip", "link", "show", eth] |
4408 | + f_cmd_output = subprocess.Popen(cmd, stdout=subprocess.PIPE).\ |
4409 | + communicate()[0] |
4410 | + used = [lines for lines in f_cmd_output.splitlines() \ |
4411 | + if "UP" in lines] |
4412 | + if not used: |
4413 | + break |
4414 | + return eth |
4415 | |
4416 | === added file 'quantum/plugins/cisco/ucs/cisco_ucs_configuration.py' |
4417 | --- quantum/plugins/cisco/ucs/cisco_ucs_configuration.py 1970-01-01 00:00:00 +0000 |
4418 | +++ quantum/plugins/cisco/ucs/cisco_ucs_configuration.py 2011-08-15 17:33:23 +0000 |
4419 | @@ -0,0 +1,37 @@ |
4420 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
4421 | +# |
4422 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
4423 | +# |
4424 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
4425 | +# not use this file except in compliance with the License. You may obtain |
4426 | +# a copy of the License at |
4427 | +# |
4428 | +# http://www.apache.org/licenses/LICENSE-2.0 |
4429 | +# |
4430 | +# Unless required by applicable law or agreed to in writing, software |
4431 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
4432 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
4433 | +# License for the specific language governing permissions and limitations |
4434 | +# under the License. |
4435 | +# |
4436 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
4437 | +# |
4438 | + |
4439 | +import os |
4440 | + |
4441 | +from quantum.plugins.cisco.common import cisco_configparser as confp |
4442 | + |
4443 | +CONF_FILE = "../conf/ucs.ini" |
4444 | + |
4445 | +cp = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \ |
4446 | + + "/" + CONF_FILE) |
4447 | + |
4448 | +section = cp['UCSM'] |
4449 | +UCSM_IP_ADDRESS = section['ip_address'] |
4450 | +DEFAULT_VLAN_NAME = section['default_vlan_name'] |
4451 | +DEFAULT_VLAN_ID = section['default_vlan_id'] |
4452 | +MAX_UCSM_PORT_PROFILES = section['max_ucsm_port_profiles'] |
4453 | +PROFILE_NAME_PREFIX = section['profile_name_prefix'] |
4454 | + |
4455 | +section = cp['DRIVER'] |
4456 | +UCSM_DRIVER = section['name'] |
4457 | |
4458 | === added file 'quantum/plugins/cisco/ucs/cisco_ucs_network_driver.py' |
4459 | --- quantum/plugins/cisco/ucs/cisco_ucs_network_driver.py 1970-01-01 00:00:00 +0000 |
4460 | +++ quantum/plugins/cisco/ucs/cisco_ucs_network_driver.py 2011-08-15 17:33:23 +0000 |
4461 | @@ -0,0 +1,235 @@ |
4462 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
4463 | +# |
4464 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
4465 | +# |
4466 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
4467 | +# not use this file except in compliance with the License. You may obtain |
4468 | +# a copy of the License at |
4469 | +# |
4470 | +# http://www.apache.org/licenses/LICENSE-2.0 |
4471 | +# |
4472 | +# Unless required by applicable law or agreed to in writing, software |
4473 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
4474 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
4475 | +# License for the specific language governing permissions and limitations |
4476 | +# under the License. |
4477 | +# |
4478 | +# @author: Sumit Naiksatam, Cisco Systems Inc. |
4479 | +# |
4480 | +""" |
4481 | +Implements a UCSM XML API Client |
4482 | +""" |
4483 | + |
4484 | +import httplib |
4485 | +import logging as LOG |
4486 | +import string |
4487 | +import subprocess |
4488 | +from xml.etree import ElementTree as et |
4489 | +import urllib |
4490 | + |
4491 | +from quantum.plugins.cisco.common import cisco_constants as const |
4492 | +from quantum.plugins.cisco.common import cisco_exceptions as cexc |
4493 | +from quantum.plugins.cisco.ucs import cisco_getvif as gvif |
4494 | + |
4495 | + |
4496 | +LOG.basicConfig(level=LOG.WARN) |
4497 | +LOG.getLogger(const.LOGGER_COMPONENT_NAME) |
4498 | + |
4499 | +COOKIE_VALUE = "cookie_placeholder" |
4500 | +PROFILE_NAME = "profilename_placeholder" |
4501 | +PROFILE_CLIENT = "profileclient_placeholder" |
4502 | +VLAN_NAME = "vlanname_placeholder" |
4503 | +VLAN_ID = "vlanid_placeholder" |
4504 | +OLD_VLAN_NAME = "old_vlanname_placeholder" |
4505 | +DYNAMIC_NIC_PREFIX = "eth" |
4506 | + |
4507 | +# The following are standard strings, messages used to communicate with UCSM, |
4508 | +#only place holder values change for each message |
4509 | +HEADERS = {"Content-Type": "text/xml"} |
4510 | +METHOD = "POST" |
4511 | +URL = "/nuova" |
4512 | + |
4513 | +CREATE_VLAN = "<configConfMos cookie=\"" + COOKIE_VALUE + \ |
4514 | +"\" inHierarchical=\"true\"> <inConfigs>" \ |
4515 | +"<pair key=\"fabric/lan/net-" + VLAN_NAME + \ |
4516 | +"\"> <fabricVlan defaultNet=\"no\" " \ |
4517 | +"dn=\"fabric/lan/net-" + VLAN_NAME + \ |
4518 | +"\" id=\"" + VLAN_ID + "\" name=\"" + \ |
4519 | +VLAN_NAME + "\" status=\"created\">" \ |
4520 | +"</fabricVlan> </pair> </inConfigs> </configConfMos>" |
4521 | + |
4522 | +CREATE_PROFILE = "<configConfMos cookie=\"" + COOKIE_VALUE + \ |
4523 | +"\" inHierarchical=\"true\"> <inConfigs>" \ |
4524 | +"<pair key=\"fabric/lan/profiles/vnic-" + PROFILE_NAME + \ |
4525 | +"\"> <vnicProfile descr=\"Profile created by " \ |
4526 | +"Cisco OpenStack Quantum Plugin\" " \ |
4527 | +"dn=\"fabric/lan/profiles/vnic-" + PROFILE_NAME + \ |
4528 | +"\" maxPorts=\"64\" name=\"" + PROFILE_NAME + \ |
4529 | +"\" nwCtrlPolicyName=\"\" pinToGroupName=\"\" " \ |
4530 | +"qosPolicyName=\"\" status=\"created\"> " \ |
4531 | +"<vnicEtherIf defaultNet=\"yes\" name=\"" + VLAN_NAME + \ |
4532 | +"\" rn=\"if-" + VLAN_NAME + "\" > </vnicEtherIf> " \ |
4533 | +"</vnicProfile> </pair> </inConfigs> </configConfMos>" |
4534 | + |
4535 | +ASSOCIATE_PROFILE = "<configConfMos cookie=\"" + COOKIE_VALUE + \ |
4536 | +"\" inHierarchical=\"true\"> <inConfigs> <pair " \ |
4537 | +"key=\"fabric/lan/profiles/vnic-" + PROFILE_NAME + \ |
4538 | +"/cl-" + PROFILE_CLIENT + "\"> <vmVnicProfCl dcName=\".*\" " \ |
4539 | +"descr=\"\" dn=\"fabric/lan/profiles/vnic-" + \ |
4540 | +PROFILE_NAME + "/cl-" + PROFILE_CLIENT + \ |
4541 | +"\"name=\"" + PROFILE_CLIENT + "\" orgPath=\".*\" " \ |
4542 | +"status=\"created\" swName=\"default$\"> </vmVnicProfCl>" \ |
4543 | +"</pair> </inConfigs> </configConfMos>" |
4544 | + |
4545 | +CHANGE_VLAN_IN_PROFILE = "<configConfMos cookie=\"" + COOKIE_VALUE + \ |
4546 | +"\" inHierarchical=\"true\"> <inConfigs>" \ |
4547 | +"<pair key=\"fabric/lan/profiles/vnic-" + \ |
4548 | +PROFILE_NAME + "\"> <vnicProfile descr=\"Profile " \ |
4549 | +"created by Cisco OpenStack Quantum Plugin\" " \ |
4550 | +"dn=\"fabric/lan/profiles/vnic-" + \ |
4551 | +PROFILE_NAME + "\" maxPorts=\"64\" name=\"" + \ |
4552 | +PROFILE_NAME + "\" nwCtrlPolicyName=\"\" " \ |
4553 | +"pinToGroupName=\"\" qosPolicyName=\"\" " \ |
4554 | +"status=\"created,modified\">" \ |
4555 | +"<vnicEtherIf rn=\"if-" + OLD_VLAN_NAME + \ |
4556 | +"\" status=\"deleted\"> </vnicEtherIf> <vnicEtherIf " \ |
4557 | +"defaultNet=\"yes\" name=\"" + \ |
4558 | +VLAN_NAME + "\" rn=\"if-" + VLAN_NAME + \ |
4559 | +"\" > </vnicEtherIf> </vnicProfile> </pair>" \ |
4560 | +"</inConfigs> </configConfMos>" |
4561 | + |
4562 | +DELETE_VLAN = "<configConfMos cookie=\"" + COOKIE_VALUE + \ |
4563 | +"\" inHierarchical=\"true\"> <inConfigs>" \ |
4564 | +"<pair key=\"fabric/lan/net-" + VLAN_NAME + \ |
4565 | +"\"> <fabricVlan dn=\"fabric/lan/net-" + VLAN_NAME + \ |
4566 | +"\" status=\"deleted\"> </fabricVlan> </pair> </inConfigs>" \ |
4567 | +"</configConfMos>" |
4568 | + |
4569 | +DELETE_PROFILE = "<configConfMos cookie=\"" + COOKIE_VALUE + \ |
4570 | +"\" inHierarchical=\"false\"> <inConfigs>" \ |
4571 | +"<pair key=\"fabric/lan/profiles/vnic-" + PROFILE_NAME + \ |
4572 | +"\"> <vnicProfile dn=\"fabric/lan/profiles/vnic-" + \ |
4573 | +PROFILE_NAME + "\" status=\"deleted\"> </vnicProfile>" \ |
4574 | +"</pair> </inConfigs> </configConfMos>" |
4575 | + |
4576 | + |
4577 | +class CiscoUCSMDriver(): |
4578 | + |
4579 | + def __init__(self): |
4580 | + pass |
4581 | + |
4582 | + def _post_data(self, ucsm_ip, ucsm_username, ucsm_password, data): |
4583 | + conn = httplib.HTTPConnection(ucsm_ip) |
4584 | + login_data = "<aaaLogin inName=\"" + ucsm_username + \ |
4585 | + "\" inPassword=\"" + ucsm_password + "\" />" |
4586 | + conn.request(METHOD, URL, login_data, HEADERS) |
4587 | + response = conn.getresponse() |
4588 | + response_data = response.read() |
4589 | + LOG.debug(response.status) |
4590 | + LOG.debug(response.reason) |
4591 | + LOG.debug(response_data) |
4592 | + # TODO (Sumit): If login is not successful, throw exception |
4593 | + xmlTree = et.XML(response_data) |
4594 | + cookie = xmlTree.attrib["outCookie"] |
4595 | + |
4596 | + data = data.replace(COOKIE_VALUE, cookie) |
4597 | + LOG.debug("POST: %s" % data) |
4598 | + conn.request(METHOD, URL, data, HEADERS) |
4599 | + response = conn.getresponse() |
4600 | + response_data = response.read() |
4601 | + LOG.debug(response.status) |
4602 | + LOG.debug(response.reason) |
4603 | + LOG.debug("UCSM Response: %s" % response_data) |
4604 | + |
4605 | + logout_data = "<aaaLogout inCookie=\"" + cookie + "\" />" |
4606 | + conn.request(METHOD, URL, logout_data, HEADERS) |
4607 | + response = conn.getresponse() |
4608 | + response_data = response.read() |
4609 | + LOG.debug(response.status) |
4610 | + LOG.debug(response.reason) |
4611 | + LOG.debug(response_data) |
4612 | + |
4613 | + def _create_vlan_post_data(self, vlan_name, vlan_id): |
4614 | + data = CREATE_VLAN.replace(VLAN_NAME, vlan_name) |
4615 | + data = data.replace(VLAN_ID, vlan_id) |
4616 | + return data |
4617 | + |
4618 | + def _create_profile_post_data(self, profile_name, vlan_name): |
4619 | + data = CREATE_PROFILE.replace(PROFILE_NAME, profile_name) |
4620 | + data = data.replace(VLAN_NAME, vlan_name) |
4621 | + return data |
4622 | + |
4623 | + def _create_profile_client_post_data(self, profile_name, |
4624 | + profile_client_name): |
4625 | + data = ASSOCIATE_PROFILE.replace(PROFILE_NAME, profile_name) |
4626 | + data = data.replace(PROFILE_CLIENT, profile_client_name) |
4627 | + return data |
4628 | + |
4629 | + def _change_vlan_in_profile_post_data(self, profile_name, old_vlan_name, |
4630 | + new_vlan_name): |
4631 | + data = CHANGE_VLAN_IN_PROFILE.replace(PROFILE_NAME, profile_name) |
4632 | + data = data.replace(OLD_VLAN_NAME, old_vlan_name) |
4633 | + data = data.replace(VLAN_NAME, new_vlan_name) |
4634 | + return data |
4635 | + |
4636 | + def _delete_vlan_post_data(self, vlan_name): |
4637 | + data = DELETE_VLAN.replace(VLAN_NAME, vlan_name) |
4638 | + return data |
4639 | + |
4640 | + def _delete_profile_post_data(self, profile_name): |
4641 | + data = DELETE_PROFILE.replace(PROFILE_NAME, profile_name) |
4642 | + return data |
4643 | + |
4644 | + def _get_next_dynamic_nic(self): |
4645 | + dynamic_nic_id = gvif.get_next_dynic() |
4646 | + if len(dynamic_nic_id) > 0: |
4647 | + return dynamic_nic_id |
4648 | + else: |
4649 | + raise cisco_exceptions.NoMoreNics(net_id=net_id, port_id=port_id) |
4650 | + |
4651 | + def create_vlan(self, vlan_name, vlan_id, ucsm_ip, ucsm_username, |
4652 | + ucsm_password): |
4653 | + data = self._create_vlan_post_data(vlan_name, vlan_id) |
4654 | + self._post_data(ucsm_ip, ucsm_username, ucsm_password, data) |
4655 | + |
4656 | + def create_profile(self, profile_name, vlan_name, ucsm_ip, ucsm_username, |
4657 | + ucsm_password): |
4658 | + data = self._create_profile_post_data(profile_name, vlan_name) |
4659 | + self._post_data(ucsm_ip, ucsm_username, ucsm_password, data) |
4660 | + data = self._create_profile_client_post_data(profile_name, |
4661 | + profile_name[-16:]) |
4662 | + self._post_data(ucsm_ip, ucsm_username, ucsm_password, data) |
4663 | + |
4664 | + def change_vlan_in_profile(self, profile_name, old_vlan_name, |
4665 | + new_vlan_name, ucsm_ip, ucsm_username, |
4666 | + ucsm_password): |
4667 | + data = self._change_vlan_in_profile_post_data(profile_name, |
4668 | + old_vlan_name, |
4669 | + new_vlan_name) |
4670 | + self._post_data(ucsm_ip, ucsm_username, ucsm_password, data) |
4671 | + |
4672 | + def get_dynamic_nic(self, host): |
4673 | + # TODO (Sumit): Check availability per host |
4674 | + # TODO (Sumit): If not available raise exception |
4675 | + # TODO (Sumit): This simple logic assumes that create-port and |
4676 | + # spawn-VM happens in lock-step |
4677 | + # But we should support multiple create-port calls, |
4678 | + # followed by spawn-VM calls |
4679 | + # That would require managing a pool of available |
4680 | + # dynamic vnics per host |
4681 | + dynamic_nic_name = self._get_next_dynamic_nic() |
4682 | + LOG.debug("Reserving dynamic nic %s" % dynamic_nic_name) |
4683 | + return dynamic_nic_name |
4684 | + |
4685 | + def delete_vlan(self, vlan_name, ucsm_ip, ucsm_username, ucsm_password): |
4686 | + data = self._delete_vlan_post_data(vlan_name) |
4687 | + self._post_data(ucsm_ip, ucsm_username, ucsm_password, data) |
4688 | + |
4689 | + def delete_profile(self, profile_name, ucsm_ip, ucsm_username, |
4690 | + ucsm_password): |
4691 | + data = self._delete_profile_post_data(profile_name) |
4692 | + self._post_data(ucsm_ip, ucsm_username, ucsm_password, data) |
4693 | + |
4694 | + def release_dynamic_nic(self, host): |
4695 | + # TODO (Sumit): Release on a specific host |
4696 | + pass |
4697 | |
4698 | === added file 'quantum/plugins/cisco/ucs/cisco_ucs_plugin.py' |
4699 | --- quantum/plugins/cisco/ucs/cisco_ucs_plugin.py 1970-01-01 00:00:00 +0000 |
4700 | +++ quantum/plugins/cisco/ucs/cisco_ucs_plugin.py 2011-08-15 17:33:23 +0000 |
4701 | @@ -0,0 +1,298 @@ |
4702 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
4703 | +# |
4704 | +# Copyright 2011 Cisco Systems, Inc. All rights reserved. |
4705 | +# |
4706 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
4707 | +# not use this file except in compliance with the License. You may obtain |
4708 | +# a copy of the License at |
4709 | +# |
4710 | +# http://www.apache.org/licenses/LICENSE-2.0 |
4711 | +# |
4712 | +# Unless required by applicable law or agreed to in writing, software |
4713 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
4714 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
4715 | +# License for the specific language governing permissions and limitations |
4716 | +# under the License. |
4717 | +# |
4718 | +# @author: Sumit Naiksatam, Cisco Systems, Inc. |
4719 | +# |
4720 | + |
4721 | +import logging as LOG |
4722 | + |
4723 | +from quantum.common import exceptions as exc |
4724 | +from quantum.common import utils |
4725 | +from quantum.plugins.cisco.common import cisco_constants as const |
4726 | +from quantum.plugins.cisco.common import cisco_credentials as cred |
4727 | +from quantum.plugins.cisco.common import cisco_exceptions as cexc |
4728 | +from quantum.plugins.cisco.common import cisco_utils as cutil |
4729 | +from quantum.plugins.cisco.l2device_plugin_base import L2DevicePluginBase |
4730 | +from quantum.plugins.cisco.ucs import cisco_ucs_configuration as conf |
4731 | + |
4732 | +LOG.basicConfig(level=LOG.WARN) |
4733 | +LOG.getLogger(const.LOGGER_COMPONENT_NAME) |
4734 | + |
4735 | + |
4736 | +class UCSVICPlugin(L2DevicePluginBase): |
4737 | + _networks = {} |
4738 | + |
4739 | + def __init__(self): |
4740 | + self._client = utils.import_object(conf.UCSM_DRIVER) |
4741 | + LOG.debug("Loaded driver %s\n" % conf.UCSM_DRIVER) |
4742 | + self._utils = cutil.DBUtils() |
4743 | + # TODO (Sumit) This is for now, when using only one chassis |
4744 | + self._ucsm_ip = conf.UCSM_IP_ADDRESS |
4745 | + self._ucsm_username = cred.Store.getUsername(conf.UCSM_IP_ADDRESS) |
4746 | + self._ucsm_password = cred.Store.getPassword(conf.UCSM_IP_ADDRESS) |
4747 | + # TODO (Sumit) Make the counter per UCSM |
4748 | + self._port_profile_counter = 0 |
4749 | + |
4750 | + def get_all_networks(self, tenant_id, **kwargs): |
4751 | + """ |
4752 | + Returns a dictionary containing all |
4753 | + <network_uuid, network_name> for |
4754 | + the specified tenant. |
4755 | + """ |
4756 | + LOG.debug("UCSVICPlugin:get_all_networks() called\n") |
4757 | + return self._networks.values() |
4758 | + |
4759 | + def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id, |
4760 | + **kwargs): |
4761 | + """ |
4762 | + Creates a new Virtual Network, and assigns it |
4763 | + a symbolic name. |
4764 | + """ |
4765 | + LOG.debug("UCSVICPlugin:create_network() called\n") |
4766 | + self._client.create_vlan(vlan_name, str(vlan_id), self._ucsm_ip, |
4767 | + self._ucsm_username, self._ucsm_password) |
4768 | + new_net_dict = {const.NET_ID: net_id, |
4769 | + const.NET_NAME: net_name, |
4770 | + const.NET_PORTS: {}, |
4771 | + const.NET_VLAN_NAME: vlan_name, |
4772 | + const.NET_VLAN_ID: vlan_id} |
4773 | + self._networks[net_id] = new_net_dict |
4774 | + return new_net_dict |
4775 | + |
4776 | + def delete_network(self, tenant_id, net_id, **kwargs): |
4777 | + """ |
4778 | + Deletes the network with the specified network identifier |
4779 | + belonging to the specified tenant. |
4780 | + """ |
4781 | + LOG.debug("UCSVICPlugin:delete_network() called\n") |
4782 | + net = self._networks.get(net_id) |
4783 | + # TODO (Sumit) : Verify that no attachments are plugged into the |
4784 | + # network |
4785 | + if net: |
4786 | + # TODO (Sumit) : Before deleting the network, make sure all the |
4787 | + # ports associated with this network are also deleted |
4788 | + self._client.delete_vlan(net[const.NET_VLAN_NAME], self._ucsm_ip, |
4789 | + self._ucsm_username, self._ucsm_password) |
4790 | + self._networks.pop(net_id) |
4791 | + return net |
4792 | + raise exc.NetworkNotFound(net_id=net_id) |
4793 | + |
4794 | + def get_network_details(self, tenant_id, net_id, **kwargs): |
4795 | + """ |
4796 | + Deletes the Virtual Network belonging to a the |
4797 | + spec |
4798 | + """ |
4799 | + LOG.debug("UCSVICPlugin:get_network_details() called\n") |
4800 | + network = self._get_network(tenant_id, net_id) |
4801 | + return network |
4802 | + |
4803 | + def rename_network(self, tenant_id, net_id, new_name, **kwargs): |
4804 | + """ |
4805 | + Updates the symbolic name belonging to a particular |
4806 | + Virtual Network. |
4807 | + """ |
4808 | + LOG.debug("UCSVICPlugin:rename_network() called\n") |
4809 | + network = self._get_network(tenant_id, net_id) |
4810 | + network[const.NET_NAME] = new_name |
4811 | + return network |
4812 | + |
4813 | + def get_all_ports(self, tenant_id, net_id, **kwargs): |
4814 | + """ |
4815 | + Retrieves all port identifiers belonging to the |
4816 | + specified Virtual Network. |
4817 | + """ |
4818 | + LOG.debug("UCSVICPlugin:get_all_ports() called\n") |
4819 | + network = self._get_network(tenant_id, net_id) |
4820 | + ports_on_net = network[const.NET_PORTS].values() |
4821 | + return ports_on_net |
4822 | + |
4823 | + def create_port(self, tenant_id, net_id, port_state, port_id, **kwargs): |
4824 | + """ |
4825 | + Creates a port on the specified Virtual Network. |
4826 | + """ |
4827 | + LOG.debug("UCSVICPlugin:create_port() called\n") |
4828 | + net = self._get_network(tenant_id, net_id) |
4829 | + ports = net[const.NET_PORTS] |
4830 | + # TODO (Sumit): This works on a single host deployment, |
4831 | + # in multi-host environment, dummy needs to be replaced with the |
4832 | + # hostname |
4833 | + dynamic_nic_name = self._client.get_dynamic_nic("dummy") |
4834 | + new_port_profile = self._create_port_profile(tenant_id, net_id, |
4835 | + port_id, |
4836 | + conf.DEFAULT_VLAN_NAME, |
4837 | + conf.DEFAULT_VLAN_ID) |
4838 | + profile_name = new_port_profile[const.PROFILE_NAME] |
4839 | + sql_query = "INSERT INTO ports (port_id, profile_name, dynamic_vnic," \ |
4840 | + "host, instance_name, instance_nic_name, used) VALUES" \ |
4841 | + "('%s', '%s', '%s', 'dummy', NULL, NULL, 0)" % \ |
4842 | + (port_id, profile_name, dynamic_nic_name) |
4843 | + self._utils.execute_db_query(sql_query) |
4844 | + new_port_dict = {const.PORT_ID: port_id, |
4845 | + const.PORT_STATE: const.PORT_UP, |
4846 | + const.ATTACHMENT: None, |
4847 | + const.PORT_PROFILE: new_port_profile} |
4848 | + ports[port_id] = new_port_dict |
4849 | + return new_port_dict |
4850 | + |
4851 | + def delete_port(self, tenant_id, net_id, port_id, **kwargs): |
4852 | + """ |
4853 | + Deletes a port on a specified Virtual Network, |
4854 | + if the port contains a remote interface attachment, |
4855 | + the remote interface should first be un-plugged and |
4856 | + then the port can be deleted. |
4857 | + """ |
4858 | + LOG.debug("UCSVICPlugin:delete_port() called\n") |
4859 | + port = self._get_port(tenant_id, net_id, port_id) |
4860 | + if port[const.ATTACHMENT]: |
4861 | + raise exc.PortInUse(net_id=net_id, port_id=port_id, |
4862 | + att_id=port[const.ATTACHMENT]) |
4863 | + try: |
4864 | + #TODO (Sumit): Before deleting port profile make sure that there |
4865 | + # is no VM using this port profile |
4866 | + self._client.release_dynamic_nic("dummy") |
4867 | + port_profile = port[const.PORT_PROFILE] |
4868 | + self._delete_port_profile(port_id, |
4869 | + port_profile[const.PROFILE_NAME]) |
4870 | + sql_query = "delete from ports where port_id = \"%s\"" % \ |
4871 | + (port[const.PORT_ID]) |
4872 | + self._utils.execute_db_query(sql_query) |
4873 | + net = self._get_network(tenant_id, net_id) |
4874 | + net[const.NET_PORTS].pop(port_id) |
4875 | + except KeyError: |
4876 | + raise exc.PortNotFound(net_id=net_id, port_id=port_id) |
4877 | + |
4878 | + def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs): |
4879 | + """ |
4880 | + Updates the state of a port on the specified Virtual Network. |
4881 | + """ |
4882 | + LOG.debug("UCSVICPlugin:update_port() called\n") |
4883 | + port = self._get_port(tenant_id, net_id, port_id) |
4884 | + self._validate_port_state(port_state) |
4885 | + port[const.PORT_STATE] = port_state |
4886 | + return port |
4887 | + |
4888 | + def get_port_details(self, tenant_id, net_id, port_id, **kwargs): |
4889 | + """ |
4890 | + This method allows the user to retrieve a remote interface |
4891 | + that is attached to this particular port. |
4892 | + """ |
4893 | + LOG.debug("UCSVICPlugin:get_port_details() called\n") |
4894 | + return self._get_port(tenant_id, net_id, port_id) |
4895 | + |
4896 | + def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id, |
4897 | + **kwargs): |
4898 | + """ |
4899 | + Attaches a remote interface to the specified port on the |
4900 | + specified Virtual Network. |
4901 | + """ |
4902 | + LOG.debug("UCSVICPlugin:plug_interface() called\n") |
4903 | + self._validate_attachment(tenant_id, net_id, port_id, |
4904 | + remote_interface_id) |
4905 | + port = self._get_port(tenant_id, net_id, port_id) |
4906 | + if port[const.ATTACHMENT]: |
4907 | + raise exc.PortInUse(net_id=net_id, port_id=port_id, |
4908 | + att_id=port[const.ATTACHMENT]) |
4909 | + port[const.ATTACHMENT] = remote_interface_id |
4910 | + port_profile = port[const.PORT_PROFILE] |
4911 | + profile_name = port_profile[const.PROFILE_NAME] |
4912 | + old_vlan_name = port_profile[const.PROFILE_VLAN_NAME] |
4913 | + new_vlan_name = self._get_vlan_name_for_network(tenant_id, net_id) |
4914 | + new_vlan_id = self._get_vlan_id_for_network(tenant_id, net_id) |
4915 | + self._client.change_vlan_in_profile(profile_name, old_vlan_name, |
4916 | + new_vlan_name, self._ucsm_ip, |
4917 | + self._ucsm_username, |
4918 | + self._ucsm_password) |
4919 | + port_profile[const.PROFILE_VLAN_NAME] = new_vlan_name |
4920 | + port_profile[const.PROFILE_VLAN_ID] = new_vlan_id |
4921 | + |
4922 | + def unplug_interface(self, tenant_id, net_id, port_id, **kwargs): |
4923 | + """ |
4924 | + Detaches a remote interface from the specified port on the |
4925 | + specified Virtual Network. |
4926 | + """ |
4927 | + LOG.debug("UCSVICPlugin:unplug_interface() called\n") |
4928 | + port = self._get_port(tenant_id, net_id, port_id) |
4929 | + port[const.ATTACHMENT] = None |
4930 | + port_profile = port[const.PORT_PROFILE] |
4931 | + profile_name = port_profile[const.PROFILE_NAME] |
4932 | + old_vlan_name = port_profile[const.PROFILE_VLAN_NAME] |
4933 | + new_vlan_name = conf.DEFAULT_VLAN_NAME |
4934 | + self._client.change_vlan_in_profile(profile_name, old_vlan_name, |
4935 | + new_vlan_name, self._ucsm_ip, |
4936 | + self._ucsm_username, |
4937 | + self._ucsm_password) |
4938 | + port_profile[const.PROFILE_VLAN_NAME] = conf.DEFAULT_VLAN_NAME |
4939 | + port_profile[const.PROFILE_VLAN_ID] = conf.DEFAULT_VLAN_ID |
4940 | + |
4941 | + def _get_profile_name(self, port_id): |
4942 | + profile_name = conf.PROFILE_NAME_PREFIX + port_id |
4943 | + return profile_name |
4944 | + |
4945 | + def _validate_port_state(self, port_state): |
4946 | + if port_state.upper() not in (const.PORT_UP, const.PORT_DOWN): |
4947 | + raise exc.StateInvalid(port_state=port_state) |
4948 | + return True |
4949 | + |
4950 | + def _validate_attachment(self, tenant_id, network_id, port_id, |
4951 | + remote_interface_id): |
4952 | + network = self._get_network(tenant_id, network_id) |
4953 | + for port in network[const.NET_PORTS].values(): |
4954 | + if port[const.ATTACHMENT] == remote_interface_id: |
4955 | + raise exc.AlreadyAttached(net_id=network_id, |
4956 | + port_id=port_id, |
4957 | + att_id=port[const.ATTACHMENT], |
4958 | + att_port_id=port[const.PORT_ID]) |
4959 | + |
4960 | + def _get_network(self, tenant_id, network_id): |
4961 | + network = self._networks.get(network_id) |
4962 | + if not network: |
4963 | + raise exc.NetworkNotFound(net_id=network_id) |
4964 | + return network |
4965 | + |
4966 | + def _get_vlan_name_for_network(self, tenant_id, network_id): |
4967 | + net = self._get_network(tenant_id, network_id) |
4968 | + vlan_name = net[const.NET_VLAN_NAME] |
4969 | + return vlan_name |
4970 | + |
4971 | + def _get_vlan_id_for_network(self, tenant_id, network_id): |
4972 | + net = self._get_network(tenant_id, network_id) |
4973 | + vlan_id = net[const.NET_VLAN_ID] |
4974 | + return vlan_id |
4975 | + |
4976 | + def _get_port(self, tenant_id, network_id, port_id): |
4977 | + net = self._get_network(tenant_id, network_id) |
4978 | + port = net[const.NET_PORTS].get(port_id) |
4979 | + if not port: |
4980 | + raise exc.PortNotFound(net_id=network_id, port_id=port_id) |
4981 | + return port |
4982 | + |
4983 | + def _create_port_profile(self, tenant_id, net_id, port_id, vlan_name, |
4984 | + vlan_id): |
4985 | + if self._port_profile_counter >= int(conf.MAX_UCSM_PORT_PROFILES): |
4986 | + raise cexc.UCSMPortProfileLimit(net_id=net_id, port_id=port_id) |
4987 | + profile_name = self._get_profile_name(port_id) |
4988 | + self._client.create_profile(profile_name, vlan_name, self._ucsm_ip, |
4989 | + self._ucsm_username, self._ucsm_password) |
4990 | + self._port_profile_counter += 1 |
4991 | + new_port_profile = {const.PROFILE_NAME: profile_name, |
4992 | + const.PROFILE_VLAN_NAME: vlan_name, |
4993 | + const.PROFILE_VLAN_ID: vlan_id} |
4994 | + return new_port_profile |
4995 | + |
4996 | + def _delete_port_profile(self, port_id, profile_name): |
4997 | + self._client.delete_profile(profile_name, self._ucsm_ip, |
4998 | + self._ucsm_username, self._ucsm_password) |
4999 | + self._port_profile_counter -= 1 |
Hi Sumit,
This is a massive piece of work, well done!
The code looks great. It is well written and easily understandable.
- README states that this plugin provides "A reference implementation of plugin framework for L2 network". My understanding of a "reference implementation" is that it should be a relatively basic implementation of a standardized interface, which could be executed by a large number of users. Probably the cisco plugin should not be considered a "reference" implementation for Quantum, as it has pretty much strict sw & hw requirements.
- It would be great if you can improve a bit pylint score for your branch. Your branch scores 6.53/10, it would be great if could bring that result to at leasr 7.5/10. We are already doing some work for improving pylint in trunk code.
-As you pointed out in comments, the plugin currently does not work on multi-host. This should be explictly noted in the README file.
- Several modules have a main method, which was probably used for testing during developed. From what I see from the code, this main methods can be removed.
Before approving for merge, it would be great if you could answer some questions:
- README mentions some changes are required to nova, and that these changes should be merged in nova in time for diablo release. Are you referring to nova-refactoring work led by Ryu? Are these changes specific for the Cisco plugin?
- How does this branch implement the blue print for the Quantum plugin interface? It contains only code specific for the Cisco plugin.
- It seems to be possible to configure the Cisco plugin to work with the Nexus sub-plugin only excluding the UCS one; however in that case the plugin will not work correctly as some operations need the UCS plugin. Would it be the case to make the UCS compulsory?
- id generation - Looks like you're using absolute counters. Have you considered using per-tenant counters?
- Plugin returns some extra parameters which will be ignored by API. Is there a plan to make these parameters available through extensions?
- A single logger if being used for the whole plugin. As there are several thousands lines of code, it may be the case to have per-module loggers. This is not really important, but it would improved debugging.