'Work out which changes need to happen to the local state to match
the given desired state.
Currently this involves the following phases:
1. Change proxies to point to new addresses (should really be
last, see https://clusterhq.atlassian.net/browse/FLOC-380)
2. Stop all relevant containers.
3. Start and restart any containers that should be running
locally, so long as their required datasets are available.'

def calculate_changes(self, desired_configuration, current_cluster_state, local_state):

    current_node_state = current_cluster_state.get_node(self.node_uuid, hostname=self.hostname)
    if (current_node_state.applications is None):
        return sequentially(changes=[])
    phases = []
    for node in desired_configuration.nodes.values():
        if (node.uuid == self.node_uuid):
            desired_node_applications = node.applications
            break
    else:
        desired_node_applications = {}
    all_applications = current_node_state.applications.values()
    local_application_names = {app.name for app in all_applications}
    desired_local_state = frozenset(desired_node_applications.keys())
    start_names = desired_local_state.difference(local_application_names)
    stop_names = {app.name for app in all_applications}.difference(desired_local_state)
    start_containers = [StartApplication(application=app, node_state=current_node_state) for app in desired_node_applications.values() if ((app.name in start_names) and ((app.volume is None) or (app.volume.manifestation.dataset_id in current_node_state.manifestations)))]
    stop_containers = [StopApplication(application=app) for app in all_applications if (app.name in stop_names)]
    restart_containers = []
    applications_to_inspect = ({app.name for app in all_applications} & desired_local_state)
    current_applications_dict = dict(zip([a.name for a in all_applications], all_applications))
    desired_applications_dict = desired_node_applications
    for application_name in applications_to_inspect:
        inspect_desired = desired_applications_dict[application_name]
        inspect_current = current_applications_dict[application_name]
        if self._restart_for_application_change(current_node_state, inspect_current, inspect_desired):
            restart_containers.append(sequentially(changes=[StopApplication(application=inspect_current), StartApplication(application=inspect_desired, node_state=current_node_state)]))
    if stop_containers:
        phases.append(in_parallel(changes=stop_containers))
    start_restart = (start_containers + restart_containers)
    if start_restart:
        phases.append(in_parallel(changes=start_restart))
    return sequentially(changes=phases, sleep_when_empty=NOOP_SLEEP_TIME)
