diff --git a/dbmigrate/migrations/20260713120000_add_last_deploy_undeploy_port_channel.migration b/dbmigrate/migrations/20260713120000_add_last_deploy_undeploy_port_channel.migration new file mode 100644 index 00000000..385fe088 --- /dev/null +++ b/dbmigrate/migrations/20260713120000_add_last_deploy_undeploy_port_channel.migration @@ -0,0 +1,14 @@ +#-*- coding:utf-8 -*- +SQL_UP = u""" +-- Adds last_deploy and last_undeploy columns to port_channel table +-- These columns track the datetime of the last deploy and undeploy operations +ALTER TABLE `port_channel` +ADD COLUMN `last_deploy` DATETIME NULL DEFAULT NULL, +ADD COLUMN `last_undeploy` DATETIME NULL DEFAULT NULL; +""" + +SQL_DOWN = u""" +ALTER TABLE `port_channel` +DROP COLUMN `last_deploy`, +DROP COLUMN `last_undeploy`; +""" diff --git a/networkapi/api_interface/facade.py b/networkapi/api_interface/facade.py index cd3c7990..17bfe2b3 100644 --- a/networkapi/api_interface/facade.py +++ b/networkapi/api_interface/facade.py @@ -17,6 +17,7 @@ import logging import os import re +from datetime import datetime from django.core.exceptions import FieldError from django.core.exceptions import ObjectDoesNotExist @@ -39,6 +40,7 @@ from networkapi.interface.models import EnvironmentInterface from networkapi.interface.models import Interface from networkapi.interface.models import PortChannel +from networkapi.interface.models import StatusDeploy from networkapi.interface.models import TipoInterface from networkapi.interface import models from networkapi.system import exceptions as var_exceptions @@ -497,19 +499,28 @@ def generate_and_deploy_channel_config_sync(user, id_channel): equipment_interfaces[interface.equipamento.id] = [] equipment_interfaces[interface.equipamento.id].append(interface) - files_to_deploy = {} - for equipment_id in equipment_interfaces.keys(): - grouped_interfaces = equipment_interfaces[equipment_id] - file_to_deploy = _generate_config_file(grouped_interfaces) - files_to_deploy[equipment_id] = file_to_deploy - - # TODO Deploy config file - # make separate threads - for equipment_id in files_to_deploy.keys(): - lockvar = LOCK_INTERFACE_DEPLOY_CONFIG % (equipment_id) - equipamento = Equipamento.get_by_pk(equipment_id) - status_deploy = deploy_config_in_equipment_synchronous( - files_to_deploy[equipment_id], equipamento, lockvar) + try: + files_to_deploy = {} + for equipment_id in equipment_interfaces.keys(): + grouped_interfaces = equipment_interfaces[equipment_id] + file_to_deploy = _generate_config_file(grouped_interfaces) + files_to_deploy[equipment_id] = file_to_deploy + + # TODO Deploy config file + # make separate threads + for equipment_id in files_to_deploy.keys(): + lockvar = LOCK_INTERFACE_DEPLOY_CONFIG % (equipment_id) + equipamento = Equipamento.get_by_pk(equipment_id) + status_deploy = deploy_config_in_equipment_synchronous( + files_to_deploy[equipment_id], equipamento, lockvar) + except Exception: + channel.status_deploy = StatusDeploy.error[0] + channel.save(user) + raise + + channel.last_deploy = datetime.now() + channel.status_deploy = StatusDeploy.deployed[0] + channel.save(user) return status_deploy diff --git a/networkapi/api_interface/serializers.py b/networkapi/api_interface/serializers.py index b566fc4f..6ffb619c 100644 --- a/networkapi/api_interface/serializers.py +++ b/networkapi/api_interface/serializers.py @@ -59,6 +59,9 @@ class Meta: 'id', 'name', 'lacp', + 'status_deploy', + 'last_deploy', + 'last_undeploy', ) default_fields = fields @@ -66,7 +69,10 @@ class Meta: basic_fields = ( 'id', 'name', - 'lacp' + 'lacp', + 'status_deploy', + 'last_deploy', + 'last_undeploy', ) details_fields = fields diff --git a/networkapi/interface/models.py b/networkapi/interface/models.py index b2c8431a..67862dfc 100644 --- a/networkapi/interface/models.py +++ b/networkapi/interface/models.py @@ -165,6 +165,8 @@ class PortChannel(BaseModel): choices=StatusDeploy.list_type, default=StatusDeploy.pending[0] ) + last_deploy = models.DateTimeField(null=True, blank=True, db_column='last_deploy') + last_undeploy = models.DateTimeField(null=True, blank=True, db_column='last_undeploy') class Meta(BaseModel.Meta): db_table = u'port_channel' diff --git a/networkapi/interface/resource/InterfaceChannelResource.py b/networkapi/interface/resource/InterfaceChannelResource.py index 7cbabc1a..bba6f1fd 100644 --- a/networkapi/interface/resource/InterfaceChannelResource.py +++ b/networkapi/interface/resource/InterfaceChannelResource.py @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import logging +from datetime import datetime from django.forms.models import model_to_dict @@ -31,6 +32,7 @@ from networkapi.interface.models import InterfaceError from networkapi.interface.models import InterfaceNotFoundError from networkapi.interface.models import PortChannel +from networkapi.interface.models import StatusDeploy from networkapi.interface.models import TipoInterface from networkapi.rest import RestResource from networkapi.rest import UserNotAuthorizedError @@ -335,33 +337,43 @@ def handle_delete(self, request, user, *args, **kwargs): tipo = tipo.get_by_name('access') # For each equipment (leaf), undeploy port channel config - for e in equip_dict: - if not keep_db: - for i in equip_dict.get(e): - try: - front = i.ligacao_front.id - except: - front = None - pass - try: - back = i.ligacao_back.id - except: - back = None - pass - i.update(user, - i.id, - interface=i.interface, - protegida=i.protegida, - descricao=i.descricao, - ligacao_front_id=front, - ligacao_back_id=back, - tipo=tipo, - vlan_nativa='1') - - api_interface_facade.delete_channel(user, e, equip_dict.get(e), channel) + try: + for e in equip_dict: + if not keep_db: + for i in equip_dict.get(e): + try: + front = i.ligacao_front.id + except: + front = None + pass + try: + back = i.ligacao_back.id + except: + back = None + pass + i.update(user, + i.id, + interface=i.interface, + protegida=i.protegida, + descricao=i.descricao, + ligacao_front_id=front, + ligacao_back_id=back, + tipo=tipo, + vlan_nativa='1') + + api_interface_facade.delete_channel(user, e, equip_dict.get(e), channel) + except Exception: + if keep_db: + channel.status_deploy = StatusDeploy.error[0] + channel.save(user) + raise if not keep_db: channel.delete(user) + else: + channel.last_undeploy = datetime.now() + channel.status_deploy = StatusDeploy.pending[0] + channel.save(user) return self.response(dumps_networkapi({}))