[PATCH V2 0/3] Issue #302

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> V1 -> V2 re-create the default network if ip conflict let 'create' attr of networks model to create the default network ShaoHe Feng (3): move _default_network_check from top model to networks model add a new function to get an available network address Issue #302: let 'create' attr of networks model to create default network src/kimchi/model/model.py | 35 -------------------------- src/kimchi/model/networks.py | 58 ++++++++++++++++++++++++++++++++++++++------ src/kimchi/network.py | 7 ++++-- 3 files changed, 56 insertions(+), 44 deletions(-) -- 1.8.4.2

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Top model should load other model and structure preparing. The default network check is network function, move to networks model is more reasonable. And the important: it is more convenient to call attr of networks model Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/model/model.py | 35 ----------------------------------- src/kimchi/model/networks.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/kimchi/model/model.py b/src/kimchi/model/model.py index aa5eab5..a7d843e 100644 --- a/src/kimchi/model/model.py +++ b/src/kimchi/model/model.py @@ -39,7 +39,6 @@ class Model(BaseModel): if 'qemu:///' in libvirt_uri: self._default_pool_check() - self._default_network_check() this = os.path.basename(__file__) this_mod = os.path.splitext(this)[0] @@ -58,40 +57,6 @@ class Model(BaseModel): return super(Model, self).__init__(models) - def _default_network_check(self): - conn = self.conn.get() - xml = """ - <network> - <name>default</name> - <forward mode='nat'/> - <bridge name='virbr0' stp='on' delay='0' /> - <ip address='192.168.122.1' netmask='255.255.255.0'> - <dhcp> - <range start='192.168.122.2' end='192.168.122.254' /> - </dhcp> - </ip> - </network> - """ - try: - net = conn.networkLookupByName("default") - except libvirt.libvirtError: - try: - net = conn.networkDefineXML(xml) - except libvirt.libvirtError, e: - cherrypy.log.error("Fatal: Cannot create default network " - "because of %s, exit kimchid" % e.message, - severity=logging.ERROR) - sys.exit(1) - - if net.isActive() == 0: - try: - net.create() - except libvirt.libvirtError, e: - cherrypy.log.error("Fatal: Cannot activate default network " - "because of %s, exit kimchid" % e.message, - severity=logging.ERROR) - sys.exit(1) - def _default_pool_check(self): conn = self.conn.get() xml = """ diff --git a/src/kimchi/model/networks.py b/src/kimchi/model/networks.py index 0d6ccec..a9b4e77 100644 --- a/src/kimchi/model/networks.py +++ b/src/kimchi/model/networks.py @@ -32,6 +32,42 @@ from kimchi.rollbackcontext import RollbackContext class NetworksModel(object): def __init__(self, **kargs): self.conn = kargs['conn'] + if 'qemu:///' in self.conn.get().getURI(): + self._default_network_check() + + def _default_network_check(self): + conn = self.conn.get() + xml = """ + <network> + <name>default</name> + <forward mode='nat'/> + <bridge name='virbr0' stp='on' delay='0' /> + <ip address='192.168.122.1' netmask='255.255.255.0'> + <dhcp> + <range start='192.168.122.2' end='192.168.122.254' /> + </dhcp> + </ip> + </network> + """ + try: + net = conn.networkLookupByName("default") + except libvirt.libvirtError: + try: + net = conn.networkDefineXML(xml) + except libvirt.libvirtError, e: + cherrypy.log.error("Fatal: Cannot create default network " + "because of %s, exit kimchid" % e.message, + severity=logging.ERROR) + sys.exit(1) + + if net.isActive() == 0: + try: + net.create() + except libvirt.libvirtError, e: + cherrypy.log.error("Fatal: Cannot activate default network " + "because of %s, exit kimchid" % e.message, + severity=logging.ERROR) + sys.exit(1) def create(self, params): conn = self.conn.get() -- 1.8.4.2

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Then the default network check code can make use it. Also add a new argument for get_one_free_network to make it more flexible Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/model/networks.py | 18 +++++++++++------- src/kimchi/network.py | 4 ++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/kimchi/model/networks.py b/src/kimchi/model/networks.py index a9b4e77..300ed1c 100644 --- a/src/kimchi/model/networks.py +++ b/src/kimchi/model/networks.py @@ -103,17 +103,21 @@ class NetworksModel(object): conn = self.conn.get() return sorted(conn.listNetworks() + conn.listDefinedNetworks()) + def _get_available_address(self, addr_pools=[]): + invalid_addrs=[] + for net_name in self.get_list(): + network = NetworkModel.get_network(self.conn.get(), net_name) + xml = network.XMLDesc(0) + subnet = NetworkModel.get_network_from_xml(xml)['subnet'] + subnet and invalid_addrs.append(ipaddr.IPNetwork(subnet)) + addr_pools = addr_pools if addr_pools else knetwork.PrivateNets + return knetwork.get_one_free_network(invalid_addrs, addr_pools) + def _set_network_subnet(self, params): netaddr = params.get('subnet', '') - net_addrs = [] # lookup a free network address for nat and isolated automatically if not netaddr: - for net_name in self.get_list(): - network = NetworkModel.get_network(self.conn.get(), net_name) - xml = network.XMLDesc(0) - subnet = NetworkModel.get_network_from_xml(xml)['subnet'] - subnet and net_addrs.append(ipaddr.IPNetwork(subnet)) - netaddr = knetwork.get_one_free_network(net_addrs) + netaddr = self._get_available_address() if not netaddr: raise OperationFailed("KCHNET0009E", {'name': params['name']}) diff --git a/src/kimchi/network.py b/src/kimchi/network.py index 24439df..9ad95b4 100644 --- a/src/kimchi/network.py +++ b/src/kimchi/network.py @@ -44,7 +44,7 @@ def get_dev_netaddrs(): # used_nets should include all the subnet allocated in libvirt network # will get host network by get_dev_netaddrs -def get_one_free_network(used_nets): +def get_one_free_network(used_nets, nets_pool=PrivateNets): def _get_free_network(nets, used_nets): for net in nets.subnet(new_prefix=24): if not any(net.overlaps(used) for used in used_nets): @@ -52,7 +52,7 @@ def get_one_free_network(used_nets): return None used_nets = used_nets + get_dev_netaddrs() - for nets in PrivateNets: + for nets in nets_pool: net = _get_free_network(nets, used_nets) if net: return net -- 1.8.4.2

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Then the IP and Bridge name will not be hard coded. Also in some scenarios, such as in nested KVM, the default network created by kimchi on L2 guest can avoid the same network address of L2. https://github.com/kimchi-project/kimchi/issues/302 Test this patch: 1. change the IP address of default network to the subnet that one of your host interface is in. $ sudo virsh net-destroy default $ sudo virsh net-edit default 2. restart kimchi. 3. check the ip address changes. $ sudo virsh net-dumpxml default 4. delete the default network. $ sudo virsh net-destroy default $ sudo virsh net-undefine default 5. restart kimchi. 6. check the ip address changes. $ sudo virsh net-dumpxml default Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/model/networks.py | 50 ++++++++++++++++++++++++-------------------- src/kimchi/network.py | 3 +++ 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/kimchi/model/networks.py b/src/kimchi/model/networks.py index 300ed1c..c704792 100644 --- a/src/kimchi/model/networks.py +++ b/src/kimchi/model/networks.py @@ -17,6 +17,8 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +import sys + import ipaddr import libvirt @@ -27,6 +29,7 @@ from kimchi import xmlutils from kimchi.exception import InvalidOperation, InvalidParameter from kimchi.exception import MissingParameter, NotFoundError, OperationFailed from kimchi.rollbackcontext import RollbackContext +from kimchi.utils import kimchi_log class NetworksModel(object): @@ -36,38 +39,39 @@ class NetworksModel(object): self._default_network_check() def _default_network_check(self): + def create_defautl_network(): + try: + subnet = self._get_available_address(knetwork.DefaultNetsPool) + params = {"name": "default", "connection": "nat", + "subnet": subnet} + self.create(params) + return conn.networkLookupByName("default") + except libvirt.libvirtError, e: + kimchi_log.error("Fatal: Cannot create default network " + "because of %s, exit kimchid", e.message) + sys.exit(1) + conn = self.conn.get() - xml = """ - <network> - <name>default</name> - <forward mode='nat'/> - <bridge name='virbr0' stp='on' delay='0' /> - <ip address='192.168.122.1' netmask='255.255.255.0'> - <dhcp> - <range start='192.168.122.2' end='192.168.122.254' /> - </dhcp> - </ip> - </network> - """ try: net = conn.networkLookupByName("default") except libvirt.libvirtError: - try: - net = conn.networkDefineXML(xml) - except libvirt.libvirtError, e: - cherrypy.log.error("Fatal: Cannot create default network " - "because of %s, exit kimchid" % e.message, - severity=logging.ERROR) - sys.exit(1) + net = create_defautl_network() if net.isActive() == 0: try: net.create() except libvirt.libvirtError, e: - cherrypy.log.error("Fatal: Cannot activate default network " - "because of %s, exit kimchid" % e.message, - severity=logging.ERROR) - sys.exit(1) + # FIXME we can not distinguish this error from internal error + # by error code + if "Network is already in use by interface" in e.message: + # libvirt do not support update IP element, so delete the + # the network and create new one. + net.undefine() + create_defautl_network() + else: + kimchi_log.error("Fatal: Cannot activate default network " + "because of %s, exit kimchid", e.message) + sys.exit(1) def create(self, params): conn = self.conn.get() diff --git a/src/kimchi/network.py b/src/kimchi/network.py index 9ad95b4..9bf0c66 100644 --- a/src/kimchi/network.py +++ b/src/kimchi/network.py @@ -26,6 +26,9 @@ APrivateNets = ipaddr.IPNetwork("10.0.0.0/8") BPrivateNets = ipaddr.IPNetwork("172.16.0.0/12") CPrivateNets = ipaddr.IPNetwork('192.168.0.0/16') PrivateNets = [CPrivateNets, BPrivateNets, APrivateNets] +DefaultNetsPool = [ipaddr.IPNetwork('192.168.122.0/23'), + ipaddr.IPNetwork('192.168.124.0/22'), + ipaddr.IPNetwork('192.168.128.0/17')] def get_dev_netaddr(dev): -- 1.8.4.2

On 03/11/2014 09:37 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
Then the IP and Bridge name will not be hard coded.
Also in some scenarios, such as in nested KVM, the default network created by kimchi on L2 guest can avoid the same network address of L2.
https://github.com/kimchi-project/kimchi/issues/302
Test this patch: 1. change the IP address of default network to the subnet that one of your host interface is in. $ sudo virsh net-destroy default $ sudo virsh net-edit default
2. restart kimchi.
3. check the ip address changes. $ sudo virsh net-dumpxml default
4. delete the default network. $ sudo virsh net-destroy default $ sudo virsh net-undefine default
5. restart kimchi.
6. check the ip address changes. $ sudo virsh net-dumpxml default
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/model/networks.py | 50 ++++++++++++++++++++++++-------------------- src/kimchi/network.py | 3 +++ 2 files changed, 30 insertions(+), 23 deletions(-)
diff --git a/src/kimchi/model/networks.py b/src/kimchi/model/networks.py index 300ed1c..c704792 100644 --- a/src/kimchi/model/networks.py +++ b/src/kimchi/model/networks.py @@ -17,6 +17,8 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+import sys + import ipaddr import libvirt
@@ -27,6 +29,7 @@ from kimchi import xmlutils from kimchi.exception import InvalidOperation, InvalidParameter from kimchi.exception import MissingParameter, NotFoundError, OperationFailed from kimchi.rollbackcontext import RollbackContext +from kimchi.utils import kimchi_log
class NetworksModel(object): @@ -36,38 +39,39 @@ class NetworksModel(object): self._default_network_check()
def _default_network_check(self): + def create_defautl_network(): + try: + subnet = self._get_available_address(knetwork.DefaultNetsPool) + params = {"name": "default", "connection": "nat", + "subnet": subnet} + self.create(params) + return conn.networkLookupByName("default") + except libvirt.libvirtError, e: + kimchi_log.error("Fatal: Cannot create default network " + "because of %s, exit kimchid", e.message) + sys.exit(1) + conn = self.conn.get() - xml = """ - <network> - <name>default</name> - <forward mode='nat'/> - <bridge name='virbr0' stp='on' delay='0' /> - <ip address='192.168.122.1' netmask='255.255.255.0'> - <dhcp> - <range start='192.168.122.2' end='192.168.122.254' /> - </dhcp> - </ip> - </network> - """ try: net = conn.networkLookupByName("default") except libvirt.libvirtError: - try: - net = conn.networkDefineXML(xml) - except libvirt.libvirtError, e: - cherrypy.log.error("Fatal: Cannot create default network " - "because of %s, exit kimchid" % e.message, - severity=logging.ERROR) - sys.exit(1) + net = create_defautl_network()
if net.isActive() == 0: try: net.create() except libvirt.libvirtError, e: - cherrypy.log.error("Fatal: Cannot activate default network " - "because of %s, exit kimchid" % e.message, - severity=logging.ERROR) - sys.exit(1) + # FIXME we can not distinguish this error from internal error + # by error code
=/
+ if "Network is already in use by interface" in e.message:
Let's use lower() to avoid errors with upper/lower case
+ # libvirt do not support update IP element, so delete the + # the network and create new one. + net.undefine() + create_defautl_network() + else: + kimchi_log.error("Fatal: Cannot activate default network " + "because of %s, exit kimchid", e.message) + sys.exit(1)
def create(self, params): conn = self.conn.get() diff --git a/src/kimchi/network.py b/src/kimchi/network.py index 9ad95b4..9bf0c66 100644 --- a/src/kimchi/network.py +++ b/src/kimchi/network.py @@ -26,6 +26,9 @@ APrivateNets = ipaddr.IPNetwork("10.0.0.0/8") BPrivateNets = ipaddr.IPNetwork("172.16.0.0/12") CPrivateNets = ipaddr.IPNetwork('192.168.0.0/16') PrivateNets = [CPrivateNets, BPrivateNets, APrivateNets] +DefaultNetsPool = [ipaddr.IPNetwork('192.168.122.0/23'), + ipaddr.IPNetwork('192.168.124.0/22'), + ipaddr.IPNetwork('192.168.128.0/17')]
def get_dev_netaddr(dev):
participants (2)
-
Aline Manera
-
shaohef@linux.vnet.ibm.com