[hopem, r=gnuoy]

Fixes network-vlan-ranges parsing

Partially-Closes-Bug: 1451095
This commit is contained in:
Liam Young 2015-05-21 08:09:11 -07:00
commit 12eff6345e
8 changed files with 131 additions and 22 deletions

View File

@ -153,8 +153,10 @@ options:
type: string type: string
default: "physnet1:1000:2000" default: "physnet1:1000:2000"
description: | description: |
Space-delimited list of Neutron network-provider & vlan-id-ranges using Space-delimited list of <physical_network>:<vlan_min>:<vlan_max> or
format "<provider>:<start>:<end> ...". <physical_network> specifying physical_network names usable for VLAN
provider and tenant networks, as well as ranges of VLAN tags on each
available for allocation to tenant networks.
# Network configuration options # Network configuration options
# by default all access is over 'private-address' # by default all access is over 'private-address'
os-data-network: os-data-network:

View File

@ -52,6 +52,8 @@ from charmhelpers.core.strutils import (
bool_from_string, bool_from_string,
) )
DC_RESOURCE_NAME = 'DC'
class HAIncompleteConfig(Exception): class HAIncompleteConfig(Exception):
pass pass
@ -95,6 +97,27 @@ def is_clustered():
return False return False
def is_crm_dc():
"""
Determine leadership by querying the pacemaker Designated Controller
"""
cmd = ['crm', 'status']
try:
status = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
if not isinstance(status, six.text_type):
status = six.text_type(status, "utf-8")
except subprocess.CalledProcessError:
return False
current_dc = ''
for line in status.split('\n'):
if line.startswith('Current DC'):
# Current DC: juju-lytrusty-machine-2 (168108163) - partition with quorum
current_dc = line.split(':')[1].split()[0]
if current_dc == get_unit_hostname():
return True
return False
@retry_on_exception(5, base_delay=2, exc_type=CRMResourceNotFound) @retry_on_exception(5, base_delay=2, exc_type=CRMResourceNotFound)
def is_crm_leader(resource, retry=False): def is_crm_leader(resource, retry=False):
""" """
@ -104,6 +127,8 @@ def is_crm_leader(resource, retry=False):
We allow this operation to be retried to avoid the possibility of getting a We allow this operation to be retried to avoid the possibility of getting a
false negative. See LP #1396246 for more info. false negative. See LP #1396246 for more info.
""" """
if resource == DC_RESOURCE_NAME:
return is_crm_dc()
cmd = ['crm', 'resource', 'show', resource] cmd = ['crm', 'resource', 'show', resource]
try: try:
status = subprocess.check_output(cmd, stderr=subprocess.STDOUT) status = subprocess.check_output(cmd, stderr=subprocess.STDOUT)

View File

@ -256,11 +256,14 @@ def network_manager():
def parse_mappings(mappings): def parse_mappings(mappings):
parsed = {} parsed = {}
if mappings: if mappings:
mappings = mappings.split(' ') mappings = mappings.split()
for m in mappings: for m in mappings:
p = m.partition(':') p = m.partition(':')
if p[1] == ':': key = p[0].strip()
parsed[p[0].strip()] = p[2].strip() if p[1]:
parsed[key] = p[2].strip()
else:
parsed[key] = ''
return parsed return parsed
@ -283,13 +286,13 @@ def parse_data_port_mappings(mappings, default_bridge='br-data'):
Returns dict of the form {bridge:port}. Returns dict of the form {bridge:port}.
""" """
_mappings = parse_mappings(mappings) _mappings = parse_mappings(mappings)
if not _mappings: if not _mappings or list(_mappings.values()) == ['']:
if not mappings: if not mappings:
return {} return {}
# For backwards-compatibility we need to support port-only provided in # For backwards-compatibility we need to support port-only provided in
# config. # config.
_mappings = {default_bridge: mappings.split(' ')[0]} _mappings = {default_bridge: mappings.split()[0]}
bridges = _mappings.keys() bridges = _mappings.keys()
ports = _mappings.values() ports = _mappings.values()
@ -309,6 +312,8 @@ def parse_vlan_range_mappings(mappings):
Mappings must be a space-delimited list of provider:start:end mappings. Mappings must be a space-delimited list of provider:start:end mappings.
The start:end range is optional and may be omitted.
Returns dict of the form {provider: (start, end)}. Returns dict of the form {provider: (start, end)}.
""" """
_mappings = parse_mappings(mappings) _mappings = parse_mappings(mappings)

View File

@ -21,12 +21,14 @@
# Charm Helpers Developers <juju@lists.ubuntu.com> # Charm Helpers Developers <juju@lists.ubuntu.com>
from __future__ import print_function from __future__ import print_function
from functools import wraps
import os import os
import json import json
import yaml import yaml
import subprocess import subprocess
import sys import sys
import errno import errno
import tempfile
from subprocess import CalledProcessError from subprocess import CalledProcessError
import six import six
@ -58,15 +60,17 @@ def cached(func):
will cache the result of unit_get + 'test' for future calls. will cache the result of unit_get + 'test' for future calls.
""" """
@wraps(func)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
global cache global cache
key = str((func, args, kwargs)) key = str((func, args, kwargs))
try: try:
return cache[key] return cache[key]
except KeyError: except KeyError:
res = func(*args, **kwargs) pass # Drop out of the exception handler scope.
cache[key] = res res = func(*args, **kwargs)
return res cache[key] = res
return res
return wrapper return wrapper
@ -178,7 +182,7 @@ def local_unit():
def remote_unit(): def remote_unit():
"""The remote unit for the current relation hook""" """The remote unit for the current relation hook"""
return os.environ['JUJU_REMOTE_UNIT'] return os.environ.get('JUJU_REMOTE_UNIT', None)
def service_name(): def service_name():
@ -250,6 +254,12 @@ class Config(dict):
except KeyError: except KeyError:
return (self._prev_dict or {})[key] return (self._prev_dict or {})[key]
def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default
def keys(self): def keys(self):
prev_keys = [] prev_keys = []
if self._prev_dict is not None: if self._prev_dict is not None:
@ -353,14 +363,29 @@ def relation_set(relation_id=None, relation_settings=None, **kwargs):
"""Set relation information for the current unit""" """Set relation information for the current unit"""
relation_settings = relation_settings if relation_settings else {} relation_settings = relation_settings if relation_settings else {}
relation_cmd_line = ['relation-set'] relation_cmd_line = ['relation-set']
accepts_file = "--file" in subprocess.check_output(
relation_cmd_line + ["--help"])
if relation_id is not None: if relation_id is not None:
relation_cmd_line.extend(('-r', relation_id)) relation_cmd_line.extend(('-r', relation_id))
for k, v in (list(relation_settings.items()) + list(kwargs.items())): settings = relation_settings.copy()
if v is None: settings.update(kwargs)
relation_cmd_line.append('{}='.format(k)) if accepts_file:
else: # --file was introduced in Juju 1.23.2. Use it by default if
relation_cmd_line.append('{}={}'.format(k, v)) # available, since otherwise we'll break if the relation data is
subprocess.check_call(relation_cmd_line) # too big. Ideally we should tell relation-set to read the data from
# stdin, but that feature is broken in 1.23.2: Bug #1454678.
with tempfile.NamedTemporaryFile(delete=False) as settings_file:
settings_file.write(yaml.safe_dump(settings).encode("utf-8"))
subprocess.check_call(
relation_cmd_line + ["--file", settings_file.name])
os.remove(settings_file.name)
else:
for key, value in settings.items():
if value is None:
relation_cmd_line.append('{}='.format(key))
else:
relation_cmd_line.append('{}={}'.format(key, value))
subprocess.check_call(relation_cmd_line)
# Flush cache of any relation-gets for local unit # Flush cache of any relation-gets for local unit
flush(local_unit()) flush(local_unit())
@ -509,6 +534,11 @@ def unit_get(attribute):
return None return None
def unit_public_ip():
"""Get this unit's public IP address"""
return unit_get('public-address')
def unit_private_ip(): def unit_private_ip():
"""Get this unit's private IP address""" """Get this unit's private IP address"""
return unit_get('private-address') return unit_get('private-address')
@ -605,3 +635,49 @@ def action_fail(message):
The results set by action_set are preserved.""" The results set by action_set are preserved."""
subprocess.check_call(['action-fail', message]) subprocess.check_call(['action-fail', message])
def status_set(workload_state, message):
"""Set the workload state with a message
Use status-set to set the workload state with a message which is visible
to the user via juju status. If the status-set command is not found then
assume this is juju < 1.23 and juju-log the message unstead.
workload_state -- valid juju workload state.
message -- status update message
"""
valid_states = ['maintenance', 'blocked', 'waiting', 'active']
if workload_state not in valid_states:
raise ValueError(
'{!r} is not a valid workload state'.format(workload_state)
)
cmd = ['status-set', workload_state, message]
try:
ret = subprocess.call(cmd)
if ret == 0:
return
except OSError as e:
if e.errno != errno.ENOENT:
raise
log_message = 'status-set failed: {} {}'.format(workload_state,
message)
log(log_message, level='INFO')
def status_get():
"""Retrieve the previously set juju workload state
If the status-set command is not found then assume this is juju < 1.23 and
return 'unknown'
"""
cmd = ['status-get']
try:
raw_status = subprocess.check_output(cmd, universal_newlines=True)
status = raw_status.rstrip()
return status
except OSError as e:
if e.errno == errno.ENOENT:
return 'unknown'
else:
raise

View File

@ -90,7 +90,7 @@ def service_available(service_name):
['service', service_name, 'status'], ['service', service_name, 'status'],
stderr=subprocess.STDOUT).decode('UTF-8') stderr=subprocess.STDOUT).decode('UTF-8')
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
return 'unrecognized service' not in e.output return b'unrecognized service' not in e.output
else: else:
return True return True

View File

@ -17,7 +17,7 @@
import os import os
import re import re
import json import json
from collections import Iterable from collections import Iterable, OrderedDict
from charmhelpers.core import host from charmhelpers.core import host
from charmhelpers.core import hookenv from charmhelpers.core import hookenv
@ -119,7 +119,7 @@ class ServiceManager(object):
""" """
self._ready_file = os.path.join(hookenv.charm_dir(), 'READY-SERVICES.json') self._ready_file = os.path.join(hookenv.charm_dir(), 'READY-SERVICES.json')
self._ready = None self._ready = None
self.services = {} self.services = OrderedDict()
for service in services or []: for service in services or []:
service_name = service['service'] service_name = service['service']
self.services[service_name] = service self.services[service_name] = service

View File

@ -158,7 +158,7 @@ def filter_installed_packages(packages):
def apt_cache(in_memory=True): def apt_cache(in_memory=True):
"""Build and return an apt cache""" """Build and return an apt cache"""
import apt_pkg from apt import apt_pkg
apt_pkg.init() apt_pkg.init()
if in_memory: if in_memory:
apt_pkg.config.set("Dir::Cache::pkgcache", "") apt_pkg.config.set("Dir::Cache::pkgcache", "")

View File

@ -930,7 +930,8 @@ class TestQuantumAgentReallocation(CharmTestCase):
'process_name': 'neutron-nvsd-agent', 'process_name': 'neutron-nvsd-agent',
'executable_name': '/usr/local/bin/neutron-nvsd-agent', 'executable_name': '/usr/local/bin/neutron-nvsd-agent',
'config_files': ['/etc/neutron/neutron.conf', 'config_files': ['/etc/neutron/neutron.conf',
'/etc/neutron/plugins/oneconvergence/nvsdplugin.ini'], '/etc/neutron/plugins/oneconvergence/'
'nvsdplugin.ini'],
'log_file': '/var/log/neutron/nvsd-agent.log', 'log_file': '/var/log/neutron/nvsd-agent.log',
} }
neutron_plugin_openflow_context = { neutron_plugin_openflow_context = {