Skip to content

Commit

Permalink
handling sense, chameleon, fabric, better state reconciliation during…
Browse files Browse the repository at this point in the history
… exceptions
  • Loading branch information
abessiari committed Mar 11, 2024
1 parent ac87d81 commit 3c6dae1
Show file tree
Hide file tree
Showing 16 changed files with 372 additions and 283 deletions.
116 changes: 14 additions & 102 deletions fabfed/controller/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import List, Union, Dict

from fabfed.exceptions import ControllerException
from fabfed.model.state import BaseState, ProviderState
from fabfed.model.state import ResourceState, ProviderState
from fabfed.util.config import WorkflowConfig
from .helper import ControllerResourceListener, partition_layer3_config
from fabfed.policy.policy_helper import ProviderPolicy
Expand Down Expand Up @@ -333,7 +333,7 @@ def apply(self, provider_states: List[ProviderState]):
raise ControllerException(exceptions)

@staticmethod
def _build_state_map(provider_states: List[ProviderState]) -> Dict[str, List[BaseState]]:
def _build_state_map(provider_states: List[ProviderState]) -> Dict[str, List[ResourceState]]:
resource_state_map = dict()

for provider_state in provider_states:
Expand Down Expand Up @@ -414,116 +414,28 @@ def destroy(self, *, provider_states: List[ProviderState]):
skip_resources.update([external_state.label for external_state in external_states])
exceptions.append(e)

if not remaining_resources:
provider_states.clear()
return

provider_states_copy = provider_states.copy()
provider_states.clear()

for provider_state in provider_states_copy:
provider_state.node_states.clear()
provider_state.network_states.clear()
provider_state.service_states.clear()
provider = self.provider_factory.get_provider(label=provider_state.label)
provider_state.failed = provider.failed

for remaining_resource in remaining_resources:
resource_state = resource_state_map[remaining_resource.label]

if remaining_resource.provider.label == provider_state.label:
if remaining_resource.is_network:
provider_state.network_states.extend(resource_state)
elif remaining_resource.is_node:
provider_state.node_states.extend(resource_state)
elif remaining_resource.is_service:
provider_state.service_states.extend(resource_state)

if provider_state.node_states or provider_state.network_states or provider_state.service_states:
provider_states.append(provider_state)
if self.provider_factory.has_provider(label=provider_state.label):
provider = self.provider_factory.get_provider(label=provider_state.label)
provider_state.failed = provider.failed

if exceptions:
raise ControllerException(exceptions)
for remaining_resource in [r for r in remaining_resources if r.provider.label == provider_state.label]:
resource_states = resource_state_map[remaining_resource.label]
provider_state.add_all(resource_states)

def delete(self, *, provider_states: List[ProviderState]):
exceptions = []
resource_state_map = Controller._build_state_map(provider_states)
provider_resource_map = dict()

for provider_state in provider_states:
key = provider_state.label
provider_resource_map[key] = list()

temp = self.resources
temp.reverse()

for resource in temp:
if resource.label in resource_state_map:
key = resource.provider.label
external_dependencies = resource.attributes.get(Constants.EXTERNAL_DEPENDENCIES, [])
external_states = [resource_state_map[ed.resource.label] for ed in external_dependencies]
resource.attributes[Constants.EXTERNAL_DEPENDENCY_STATES] = sum(external_states, [])
provider_resource_map[key].append(resource)
resource.attributes[Constants.SAVED_STATES] = resource_state_map[resource.label]

remaining_resources = list()
skip_resources = set()

for resource in temp:
if resource.label not in resource_state_map:
continue

provider_label = resource.provider.label
provider = self.provider_factory.get_provider(label=provider_label)
external_states = resource.attributes[Constants.EXTERNAL_DEPENDENCY_STATES]

if resource.label in skip_resources:
self.logger.warning(f"Skipping deleting resource: {resource} with {provider_label}")
remaining_resources.append(resource)
skip_resources.update([external_state.label for external_state in external_states])
continue

fabric_work_around = False
# TODO: THIS FABRIC SPECIFIC AS WE DON"T SUPPORT SLICE MODIFY API JUST YET
for remaining_resource in remaining_resources:
if provider_label == remaining_resource.provider.label \
and "@fabric" in remaining_resource.provider.label:
fabric_work_around = True
break

if fabric_work_around:
self.logger.warning(f"Skipping deleting fabric resource: {resource} with {provider_label}")
remaining_resources.append(resource)
skip_resources.update([external_state.label for external_state in external_states])
continue

try:
provider.delete_resource(resource=resource.attributes)
except Exception as e:
self.logger.warning(f"Exception occurred while deleting resource: {e} using {provider_label}")
remaining_resources.append(resource)
skip_resources.update([external_state.label for external_state in external_states])
exceptions.append(e)

provider_states_copy = provider_states.copy()
provider_states.clear()

for provider_state in provider_states_copy:
provider_state.node_states.clear()
provider_state.network_states.clear()
provider_state.service_states.clear()
provider = self.provider_factory.get_provider(label=provider_state.label)
provider_state.failed = provider.failed

for remaining_resource in remaining_resources:
resource_state = resource_state_map[remaining_resource.label]

if remaining_resource.provider.label == provider_state.label:
if remaining_resource.is_network:
provider_state.network_states.extend(resource_state)
elif remaining_resource.is_node:
provider_state.node_states.extend(resource_state)
elif remaining_resource.is_service:
provider_state.service_states.extend(resource_state)

if provider_state.node_states or provider_state.network_states or provider_state.service_states:
provider_states.append(provider_state)
if provider_state.states():
provider_states.append(provider_state)

if exceptions:
raise ControllerException(exceptions)
Expand Down
8 changes: 6 additions & 2 deletions fabfed/controller/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ def find_node_clusters(*, resources):
visited_networks.append(peer.label)
nodes.extend(find_nodes_related_to_network(network=peer, resources=resources))
visited_nodes.extend([n.label for n in nodes])
clusters.append(nodes)

if nodes:
clusters.append(nodes)

for net in networks:
if net.label not in visited_networks:
Expand All @@ -111,7 +113,9 @@ def find_node_clusters(*, resources):
visited_networks.append(net.label)
nodes = find_nodes_related_to_network(network=net, resources=resources)
visited_nodes.extend([n.label for n in nodes])
clusters.append(nodes)

if nodes:
clusters.append(nodes)

nodes = [r for r in resources if r.is_node]

Expand Down
3 changes: 3 additions & 0 deletions fabfed/controller/provider_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ def init_provider(self, *, type: str, label: str, name: str, attributes, logger)
def providers(self) -> List[Provider]:
return list(self._providers.values())

def has_provider(self, *, label: str) -> bool:
return label in self._providers

def get_provider(self, *, label: str) -> Provider:
return self._providers[label]

Expand Down
91 changes: 83 additions & 8 deletions fabfed/model/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from fabfed.util.config_models import Config, ResourceConfig, BaseConfig, ProviderConfig, Dependency, DependencyInfo
from fabfed.model import ResolvedDependency
from fabfed.util.constants import Constants


class BaseState:
Expand All @@ -13,19 +14,43 @@ def __init__(self, type: str, label: str, attributes: Dict):
self.attributes = attributes


class NetworkState(BaseState):
class ResourceState(BaseState):
def __init__(self, type: str, label: str, attributes: Dict):
super().__init__(type, label, attributes)
self.type = type
self.label = label
self.attributes = attributes

@property
def name(self) -> str:
return self.attributes['name']

@property
def is_node_state(self):
return self.type == Constants.RES_TYPE_NODE

@property
def is_network_state(self):
return self.type == Constants.RES_TYPE_NETWORK

@property
def is_service_state(self):
return self.type == Constants.RES_TYPE_SERVICE


class NetworkState(ResourceState):
def __init__(self, *, label, attributes):
super().__init__("network", label, attributes)
super().__init__(Constants.RES_TYPE_NETWORK, label, attributes)


class NodeState(BaseState):
class NodeState(ResourceState):
def __init__(self, *, label, attributes):
super().__init__("node", label, attributes)
super().__init__(Constants.RES_TYPE_NODE, label, attributes)


class ServiceState(BaseState):
class ServiceState(ResourceState):
def __init__(self, *, label, attributes):
super().__init__("service", label, attributes)
super().__init__(Constants.RES_TYPE_SERVICE, label, attributes)


class ProviderState(BaseState):
Expand All @@ -41,8 +66,57 @@ def __init__(self, label, attributes, network_states: List[NetworkState],
self.failed = failed
self.creation_details = creation_details

def states(self) -> List[BaseState]:
return self.network_states + self.node_states + self.service_states
def add_if_not_found(self, resource_state: ResourceState):
from typing import cast
assert resource_state.type in [Constants.RES_TYPE_NODE,
Constants.RES_TYPE_NETWORK,
Constants.RES_TYPE_SERVICE]

def exists(state, states: list):
temp = next(filter(lambda s: s.label == state.label and s.name == state.name, states), None)

return temp is not None

if exists(resource_state, self.states()):
return

if resource_state.is_node_state:
self.node_states.append(cast(NodeState, resource_state))
elif resource_state.is_network_state:
self.network_states.append(cast(NetworkState, resource_state))
elif resource_state.is_service_state:
self.service_states.append(cast(ServiceState, resource_state))

def add(self, resource_state: ResourceState):
from typing import cast
assert resource_state.type in [Constants.RES_TYPE_NODE,
Constants.RES_TYPE_NETWORK,
Constants.RES_TYPE_SERVICE]

def exists(state, states: list):
temp = next(filter(lambda s: s.label == state.label and s.name == state.name, states), None)

return temp is not None

assert not exists(resource_state, self.states())

if resource_state.is_node_state:
self.node_states.append(cast(NodeState, resource_state))
elif resource_state.is_network_state:
self.network_states.append(cast(NetworkState, resource_state))
elif resource_state.is_service_state:
self.service_states.append(cast(ServiceState, resource_state))

def add_all(self, resource_states: List[ResourceState]):
for resource_state in resource_states:
self.add(resource_state)

def states(self) -> List[ResourceState]:
temp = []
temp.extend(self.network_states)
temp.extend(self.node_states)
temp.extend(self.service_states)
return temp

def number_of_created_resources(self):
count = 0
Expand All @@ -68,6 +142,7 @@ def number_of_total_resources(self):

return count


def provider_constructor(loader: yaml.SafeLoader, node: yaml.nodes.MappingNode) -> ProviderState:
return ProviderState(**loader.construct_mapping(node))

Expand Down
Loading

0 comments on commit 3c6dae1

Please sign in to comment.