Libvirt OOB driver
- Create a driver to support OOB actions via libvirt API - Update Makefile with external dependency target - Update Makefile and tooling to support new chart pipeline - Add 'drydock' make target for chart building - Add step to install helm binary Change-Id: I8a3984d8fd70f99a82a954b7a869eab8e30145b4
This commit is contained in:
parent
cd9770a979
commit
dbad75775b
48
Makefile
48
Makefile
@ -12,11 +12,12 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
BUILD_DIR := $(shell mktemp -d)
|
||||
DOCKER_REGISTRY ?= quay.io
|
||||
IMAGE_NAME ?= drydock
|
||||
IMAGE_PREFIX ?= attcomdev
|
||||
IMAGE_TAG ?= latest
|
||||
HELM ?= helm
|
||||
HELM := $(BUILD_DIR)/helm
|
||||
PROXY ?= http://one.proxy.att.com:8080
|
||||
USE_PROXY ?= false
|
||||
PUSH_IMAGE ?= false
|
||||
@ -34,21 +35,36 @@ run_images: run_drydock
|
||||
|
||||
# Run tests
|
||||
.PHONY: tests
|
||||
tests: coverage_test
|
||||
tests: external_dep pep8 security docs unit_tests
|
||||
|
||||
# Intall external (not managed by tox/pip) dependencies
|
||||
.PHONY: external_dep
|
||||
external_dep:
|
||||
sudo ./hostdeps.sh
|
||||
|
||||
# Run unit and Postgres integration tests in coverage mode
|
||||
.PHONY: coverage_test
|
||||
coverage_test: build_drydock
|
||||
tox -e coverage
|
||||
coverage_test: build_drydock external_dep
|
||||
tox -re coverage
|
||||
|
||||
# Run just unit tests
|
||||
.PHONY: unit_tests
|
||||
unit_tests:
|
||||
tox -re unit
|
||||
|
||||
# Run the drydock container and exercise simple tests
|
||||
.PHONY: run_drydock
|
||||
run_drydock: build_drydock
|
||||
tools/drydock_image_run.sh
|
||||
|
||||
# It seems CICD expects the target 'drydock' to
|
||||
# build the chart
|
||||
.PHONY: drydock
|
||||
drydock: charts
|
||||
|
||||
# Create tgz of the chart
|
||||
.PHONY: charts
|
||||
charts: clean
|
||||
charts: clean helm-init
|
||||
$(HELM) dep up charts/drydock
|
||||
$(HELM) package charts/drydock
|
||||
|
||||
@ -58,18 +74,27 @@ lint: pep8 helm_lint
|
||||
|
||||
# Dry run templating of chart
|
||||
.PHONY: dry-run
|
||||
dry-run: clean
|
||||
tools/helm_tk.sh $(HELM)
|
||||
dry-run: clean helm-init
|
||||
$(HELM) template --set manifests.secret_ssh_key=true --set conf.ssh.private_key=foo charts/drydock
|
||||
|
||||
# Initialize local helm config
|
||||
.PHONY: helm-init
|
||||
helm-init: helm-install
|
||||
tools/helm_tk.sh $(HELM)
|
||||
|
||||
# Install helm binary
|
||||
.PHONY: helm-install
|
||||
helm-install:
|
||||
tools/helm_install.sh $(HELM)
|
||||
|
||||
# Make targets intended for use by the primary targets above.
|
||||
|
||||
.PHONY: build_drydock
|
||||
build_drydock:
|
||||
ifeq ($(USE_PROXY), true)
|
||||
docker build -t $(IMAGE) --label $(LABEL) -f images/drydock/Dockerfile . --build-arg http_proxy=$(PROXY) --build-arg https_proxy=$(PROXY)
|
||||
docker build --network host -t $(IMAGE) --label $(LABEL) -f images/drydock/Dockerfile . --build-arg http_proxy=$(PROXY) --build-arg https_proxy=$(PROXY)
|
||||
else
|
||||
docker build -t $(IMAGE) --label $(LABEL) -f images/drydock/Dockerfile .
|
||||
docker build --network host -t $(IMAGE) --label $(LABEL) -f images/drydock/Dockerfile .
|
||||
endif
|
||||
ifeq ($(PUSH_IMAGE), true)
|
||||
docker push $(IMAGE)
|
||||
@ -78,12 +103,17 @@ endif
|
||||
.PHONY: docs
|
||||
docs: clean drydock_docs
|
||||
|
||||
.PHONY: security
|
||||
security:
|
||||
tox -e bandit
|
||||
|
||||
.PHONY: drydock_docs
|
||||
drydock_docs:
|
||||
tox -e docs
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)/*
|
||||
rm -rf build
|
||||
rm -rf docs/build
|
||||
rm -rf charts/drydock/charts
|
||||
|
13
drydock_provisioner/drivers/oob/libvirt_driver/__init__.py
Normal file
13
drydock_provisioner/drivers/oob/libvirt_driver/__init__.py
Normal file
@ -0,0 +1,13 @@
|
||||
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
377
drydock_provisioner/drivers/oob/libvirt_driver/actions/oob.py
Normal file
377
drydock_provisioner/drivers/oob/libvirt_driver/actions/oob.py
Normal file
@ -0,0 +1,377 @@
|
||||
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Driver for controlling OOB interface via libvirt api."""
|
||||
|
||||
import time
|
||||
import libvirt
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import defusedxml.ElementTree as ET
|
||||
|
||||
from drydock_provisioner.orchestrator.actions.orchestrator import BaseAction
|
||||
|
||||
import drydock_provisioner.error as errors
|
||||
|
||||
import drydock_provisioner.objects.fields as hd_fields
|
||||
|
||||
|
||||
class LibvirtBaseAction(BaseAction):
|
||||
"""Base action for Pyghmi executed actions."""
|
||||
|
||||
def init_session(self, node):
|
||||
"""Initialize a Libvirt session to the node hypervisor.
|
||||
|
||||
:param node: instance of objects.BaremetalNode
|
||||
"""
|
||||
if node.oob_type != 'libvirt':
|
||||
raise errors.DriverError(
|
||||
"Node OOB type %s is not 'libvirt'" % node.oob_type)
|
||||
|
||||
virsh_url = node.oob_parameters.get('libvirt_uri', None)
|
||||
|
||||
if not virsh_url:
|
||||
raise errors.DriverError("Node %s has no 'libvirt_url' defined" %
|
||||
(node.name))
|
||||
|
||||
url_parts = urlparse(virsh_url)
|
||||
|
||||
if url_parts.scheme != "qemu+ssh":
|
||||
raise errors.DriverError(
|
||||
"Node %s has invalid libvirt URL scheme %s. "
|
||||
"Only 'qemu+ssh' supported." % (node.name, url_parts.scheme))
|
||||
|
||||
self.logger.debug("Starting libvirt session to hypervisor %s " %
|
||||
(virsh_url))
|
||||
virsh_ses = libvirt.open(virsh_url)
|
||||
|
||||
if not virsh_ses:
|
||||
raise errors.DriverError(
|
||||
"Unable to establish libvirt session to %s." % virsh_url)
|
||||
|
||||
return virsh_ses
|
||||
|
||||
def set_node_pxe(self, node):
|
||||
"""Set a node to PXE boot first."""
|
||||
ses = self.init_session(node)
|
||||
domain = ses.lookupByName(node.name)
|
||||
domain_xml = domain.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE
|
||||
| libvirt.VIR_DOMAIN_XML_INACTIVE)
|
||||
xmltree = ET.fromstring(domain_xml)
|
||||
|
||||
# Delete all the current boot entries
|
||||
os_tree = xmltree.find("./os")
|
||||
boot_elements = os_tree.findall("./boot")
|
||||
for e in boot_elements:
|
||||
os_tree.remove(e)
|
||||
|
||||
# Now apply our boot order which is 'network' and then 'hd'
|
||||
os_tree.append(ET.fromstring("<boot dev='network' />"))
|
||||
os_tree.append(ET.fromstring("<boot dev='hd' />"))
|
||||
|
||||
# And now save the new XML def to the hypervisor
|
||||
domain_xml = ET.tostring(xmltree, encoding="utf-8")
|
||||
ses.defineXML(domain_xml.decode('utf-8'))
|
||||
ses.close()
|
||||
|
||||
def get_node_status(self, node):
|
||||
"""Get node status via libvirt api."""
|
||||
try:
|
||||
ses = self.init_session(node)
|
||||
domain = ses.lookupByName(node.name)
|
||||
status = domain.isActive()
|
||||
finally:
|
||||
ses.close()
|
||||
|
||||
return status
|
||||
|
||||
def poweroff_node(self, node):
|
||||
"""Power off a node."""
|
||||
ses = self.init_session(node)
|
||||
domain = ses.lookupByName(node.name)
|
||||
|
||||
if domain.isActive():
|
||||
domain.destroy()
|
||||
else:
|
||||
self.logger.debug("Node already powered off.")
|
||||
return
|
||||
|
||||
try:
|
||||
i = 3
|
||||
while i > 0:
|
||||
self.logger.debug("Polling powerstate waiting for success.")
|
||||
power_state = domain.isActive()
|
||||
if not power_state:
|
||||
return
|
||||
time.sleep(10)
|
||||
i = i - 1
|
||||
raise errors.DriverError("Power state never matched off")
|
||||
finally:
|
||||
ses.close()
|
||||
|
||||
def poweron_node(self, node):
|
||||
"""Power on a node."""
|
||||
ses = self.init_session(node)
|
||||
domain = ses.lookupByName(node.name)
|
||||
|
||||
if not domain.isActive():
|
||||
domain.create()
|
||||
else:
|
||||
self.logger.debug("Node already powered on.")
|
||||
return
|
||||
|
||||
try:
|
||||
i = 3
|
||||
while i > 0:
|
||||
self.logger.debug("Polling powerstate waiting for success.")
|
||||
power_state = domain.isActive()
|
||||
if power_state:
|
||||
return
|
||||
time.sleep(10)
|
||||
i = i - 1
|
||||
raise errors.DriverError("Power state never matched on")
|
||||
finally:
|
||||
ses.close()
|
||||
|
||||
|
||||
class ValidateOobServices(LibvirtBaseAction):
|
||||
"""Action to validation OOB services are available."""
|
||||
|
||||
def start(self):
|
||||
self.task.add_status_msg(
|
||||
msg="OOB does not require services.",
|
||||
error=False,
|
||||
ctx='NA',
|
||||
ctx_type='NA')
|
||||
self.task.set_status(hd_fields.TaskStatus.Complete)
|
||||
self.task.success()
|
||||
self.task.save()
|
||||
|
||||
return
|
||||
|
||||
|
||||
class ConfigNodePxe(LibvirtBaseAction):
|
||||
"""Action to configure PXE booting via OOB."""
|
||||
|
||||
def start(self):
|
||||
self.task.set_status(hd_fields.TaskStatus.Running)
|
||||
self.task.save()
|
||||
|
||||
design_status, site_design = self.orchestrator.get_effective_site(
|
||||
self.task.design_ref)
|
||||
node_list = self.orchestrator.process_node_filter(
|
||||
self.task.node_filter, site_design)
|
||||
|
||||
for n in node_list:
|
||||
self.task.add_status_msg(
|
||||
msg="Libvirt doesn't configure PXE options.",
|
||||
error=True,
|
||||
ctx=n.name,
|
||||
ctx_type='node')
|
||||
self.task.set_status(hd_fields.TaskStatus.Complete)
|
||||
self.task.failure()
|
||||
self.task.save()
|
||||
return
|
||||
|
||||
|
||||
class SetNodeBoot(LibvirtBaseAction):
|
||||
"""Action to configure a node to PXE boot."""
|
||||
|
||||
def start(self):
|
||||
self.task.set_status(hd_fields.TaskStatus.Running)
|
||||
self.task.save()
|
||||
|
||||
design_status, site_design = self.orchestrator.get_effective_site(
|
||||
self.task.design_ref)
|
||||
node_list = self.orchestrator.process_node_filter(
|
||||
self.task.node_filter, site_design)
|
||||
|
||||
for n in node_list:
|
||||
self.logger.debug("Setting bootdev to PXE for %s" % n.name)
|
||||
self.task.add_status_msg(
|
||||
msg="Setting node to PXE boot.",
|
||||
error=False,
|
||||
ctx=n.name,
|
||||
ctx_type='node')
|
||||
|
||||
try:
|
||||
self.set_node_pxe(n)
|
||||
except Exception as ex:
|
||||
self.task.add_status_msg(
|
||||
msg="Unable to set bootdev to PXE: %s" % str(ex),
|
||||
error=True,
|
||||
ctx=n.name,
|
||||
ctx_type='node')
|
||||
self.task.failure(focus=n.name)
|
||||
self.logger.warning("Unable to set node %s to PXE boot." %
|
||||
(n.name))
|
||||
else:
|
||||
self.task.add_status_msg(
|
||||
msg="Set bootdev to PXE.",
|
||||
error=False,
|
||||
ctx=n.name,
|
||||
ctx_type='node')
|
||||
self.logger.debug("%s reports bootdev of network" % n.name)
|
||||
self.task.success(focus=n.name)
|
||||
|
||||
self.task.set_status(hd_fields.TaskStatus.Complete)
|
||||
self.task.save()
|
||||
return
|
||||
|
||||
|
||||
class PowerOffNode(LibvirtBaseAction):
|
||||
"""Action to power off a node via libvirt API."""
|
||||
|
||||
def start(self):
|
||||
self.task.set_status(hd_fields.TaskStatus.Running)
|
||||
self.task.save()
|
||||
|
||||
design_status, site_design = self.orchestrator.get_effective_site(
|
||||
self.task.design_ref)
|
||||
node_list = self.orchestrator.process_node_filter(
|
||||
self.task.node_filter, site_design)
|
||||
|
||||
for n in node_list:
|
||||
msg = "Shutting down domain %s" % n.name
|
||||
self.logger.debug(msg)
|
||||
self.task.add_status_msg(
|
||||
msg=msg, error=False, ctx=n.name, ctx_type='node')
|
||||
|
||||
try:
|
||||
self.poweroff_node(n)
|
||||
except Exception as ex:
|
||||
msg = "Node failed to power off: %s" % str(ex)
|
||||
self.task.add_status_msg(
|
||||
msg=msg, error=True, ctx=n.name, ctx_type='node')
|
||||
self.logger.error(msg)
|
||||
self.task.failure(focus=n.name)
|
||||
else:
|
||||
msg = "Node %s powered off." % n.name
|
||||
self.task.add_status_msg(
|
||||
msg=msg, error=False, ctx=n.name, ctx_type='node')
|
||||
self.logger.debug(msg)
|
||||
self.task.success(focus=n.name)
|
||||
|
||||
self.task.set_status(hd_fields.TaskStatus.Complete)
|
||||
self.task.save()
|
||||
return
|
||||
|
||||
|
||||
class PowerOnNode(LibvirtBaseAction):
|
||||
"""Action to power on a node via libvirt API."""
|
||||
|
||||
def start(self):
|
||||
self.task.set_status(hd_fields.TaskStatus.Running)
|
||||
self.task.save()
|
||||
|
||||
design_status, site_design = self.orchestrator.get_effective_site(
|
||||
self.task.design_ref)
|
||||
node_list = self.orchestrator.process_node_filter(
|
||||
self.task.node_filter, site_design)
|
||||
|
||||
for n in node_list:
|
||||
msg = "Starting domain %s" % n.name
|
||||
self.logger.debug(msg)
|
||||
self.task.add_status_msg(
|
||||
msg=msg, error=False, ctx=n.name, ctx_type='node')
|
||||
|
||||
try:
|
||||
self.poweron_node(n)
|
||||
except Exception as ex:
|
||||
msg = "Node failed to power on: %s" % str(ex)
|
||||
self.task.add_status_msg(
|
||||
msg=msg, error=True, ctx=n.name, ctx_type='node')
|
||||
self.logger.error(msg)
|
||||
self.task.failure(focus=n.name)
|
||||
else:
|
||||
msg = "Node %s powered on." % n.name
|
||||
self.task.add_status_msg(
|
||||
msg=msg, error=False, ctx=n.name, ctx_type='node')
|
||||
self.logger.debug(msg)
|
||||
self.task.success(focus=n.name)
|
||||
|
||||
self.task.set_status(hd_fields.TaskStatus.Complete)
|
||||
self.task.save()
|
||||
return
|
||||
|
||||
|
||||
class PowerCycleNode(LibvirtBaseAction):
|
||||
"""Action to hard powercycle a node via IPMI."""
|
||||
|
||||
def start(self):
|
||||
self.task.set_status(hd_fields.TaskStatus.Running)
|
||||
self.task.save()
|
||||
|
||||
design_status, site_design = self.orchestrator.get_effective_site(
|
||||
self.task.design_ref)
|
||||
node_list = self.orchestrator.process_node_filter(
|
||||
self.task.node_filter, site_design)
|
||||
|
||||
for n in node_list:
|
||||
msg = ("Power cycling domain for node %s" % n.name)
|
||||
self.logger.debug(msg)
|
||||
self.task.add_status_msg(
|
||||
msg=msg, error=False, ctx=n.name, ctx_type='node')
|
||||
|
||||
try:
|
||||
self.poweroff_node(n)
|
||||
self.poweron_node(n)
|
||||
except Exception as ex:
|
||||
msg = "Node failed to power cycle: %s" % str(ex)
|
||||
self.task.add_status_msg(
|
||||
msg=msg, error=True, ctx=n.name, ctx_type='node')
|
||||
self.logger.error(msg)
|
||||
self.task.failure(focus=n.name)
|
||||
else:
|
||||
msg = "Node %s power cycled." % n.name
|
||||
self.task.add_status_msg(
|
||||
msg=msg, error=False, ctx=n.name, ctx_type='node')
|
||||
self.logger.debug(msg)
|
||||
self.task.success(focus=n.name)
|
||||
|
||||
self.task.set_status(hd_fields.TaskStatus.Complete)
|
||||
self.task.save()
|
||||
return
|
||||
|
||||
|
||||
class InterrogateOob(LibvirtBaseAction):
|
||||
"""Action to complete a basic interrogation of the node IPMI interface."""
|
||||
|
||||
def start(self):
|
||||
self.task.set_status(hd_fields.TaskStatus.Running)
|
||||
self.task.save()
|
||||
|
||||
design_status, site_design = self.orchestrator.get_effective_site(
|
||||
self.task.design_ref)
|
||||
node_list = self.orchestrator.process_node_filter(
|
||||
self.task.node_filter, site_design)
|
||||
|
||||
for n in node_list:
|
||||
try:
|
||||
node_status = self.get_node_status(n)
|
||||
except Exception as ex:
|
||||
msg = "Node failed tatus check: %s" % str(ex)
|
||||
self.task.add_status_msg(
|
||||
msg=msg, error=True, ctx=n.name, ctx_type='node')
|
||||
self.logger.error(msg)
|
||||
self.task.failure(focus=n.name)
|
||||
else:
|
||||
msg = "Node %s status is %s." % (n.name, node_status)
|
||||
self.task.add_status_msg(
|
||||
msg=msg, error=False, ctx=n.name, ctx_type='node')
|
||||
self.logger.debug(msg)
|
||||
self.task.success(focus=n.name)
|
||||
|
||||
self.task.set_status(hd_fields.TaskStatus.Complete)
|
||||
self.task.save()
|
||||
return
|
156
drydock_provisioner/drivers/oob/libvirt_driver/driver.py
Normal file
156
drydock_provisioner/drivers/oob/libvirt_driver/driver.py
Normal file
@ -0,0 +1,156 @@
|
||||
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Driver for controlling libvirt domains."""
|
||||
|
||||
import uuid
|
||||
import logging
|
||||
import concurrent.futures
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
import drydock_provisioner.error as errors
|
||||
import drydock_provisioner.config as config
|
||||
|
||||
import drydock_provisioner.objects.fields as hd_fields
|
||||
|
||||
import drydock_provisioner.drivers.oob.driver as oob_driver
|
||||
import drydock_provisioner.drivers.driver as generic_driver
|
||||
|
||||
from .actions.oob import ValidateOobServices
|
||||
from .actions.oob import ConfigNodePxe
|
||||
from .actions.oob import SetNodeBoot
|
||||
from .actions.oob import PowerOffNode
|
||||
from .actions.oob import PowerOnNode
|
||||
from .actions.oob import PowerCycleNode
|
||||
from .actions.oob import InterrogateOob
|
||||
|
||||
|
||||
class LibvirtDriver(oob_driver.OobDriver):
|
||||
"""Driver for executing OOB actions via libvirt API."""
|
||||
|
||||
libvirt_driver_options = [
|
||||
cfg.IntOpt(
|
||||
'poll_interval',
|
||||
default=10,
|
||||
help='Polling interval in seconds for querying libvirt status'),
|
||||
]
|
||||
|
||||
oob_types_supported = ['libvirt']
|
||||
|
||||
driver_name = "libvirt_driver"
|
||||
driver_key = "libvirt_driver"
|
||||
driver_desc = "Libvirt OOB Driver"
|
||||
|
||||
action_class_map = {
|
||||
hd_fields.OrchestratorAction.ValidateOobServices: ValidateOobServices,
|
||||
hd_fields.OrchestratorAction.ConfigNodePxe: ConfigNodePxe,
|
||||
hd_fields.OrchestratorAction.SetNodeBoot: SetNodeBoot,
|
||||
hd_fields.OrchestratorAction.PowerOffNode: PowerOffNode,
|
||||
hd_fields.OrchestratorAction.PowerOnNode: PowerOnNode,
|
||||
hd_fields.OrchestratorAction.PowerCycleNode: PowerCycleNode,
|
||||
hd_fields.OrchestratorAction.InterrogateOob: InterrogateOob,
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
cfg.CONF.register_opts(
|
||||
LibvirtDriver.libvirt_driver_options,
|
||||
group=LibvirtDriver.driver_key)
|
||||
|
||||
self.logger = logging.getLogger(
|
||||
config.config_mgr.conf.logging.oobdriver_logger_name)
|
||||
|
||||
def execute_task(self, task_id):
|
||||
task = self.state_manager.get_task(task_id)
|
||||
|
||||
if task is None:
|
||||
self.logger.error("Invalid task %s" % (task_id))
|
||||
raise errors.DriverError("Invalid task %s" % (task_id))
|
||||
|
||||
if task.action not in self.supported_actions:
|
||||
self.logger.error("Driver %s doesn't support task action %s" %
|
||||
(self.driver_desc, task.action))
|
||||
raise errors.DriverError(
|
||||
"Driver %s doesn't support task action %s" % (self.driver_desc,
|
||||
task.action))
|
||||
|
||||
task.set_status(hd_fields.TaskStatus.Running)
|
||||
task.save()
|
||||
|
||||
target_nodes = self.orchestrator.get_target_nodes(task)
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=16) as e:
|
||||
subtask_futures = dict()
|
||||
for n in target_nodes:
|
||||
sub_nf = self.orchestrator.create_nodefilter_from_nodelist([n])
|
||||
subtask = self.orchestrator.create_task(
|
||||
action=task.action,
|
||||
design_ref=task.design_ref,
|
||||
node_filter=sub_nf)
|
||||
task.register_subtask(subtask)
|
||||
self.logger.debug(
|
||||
"Starting Libvirt subtask %s for action %s on node %s" %
|
||||
(str(subtask.get_id()), task.action, n.name))
|
||||
|
||||
action_class = self.action_class_map.get(task.action, None)
|
||||
if action_class is None:
|
||||
self.logger.error(
|
||||
"Could not find action resource for action %s" %
|
||||
task.action)
|
||||
self.task.failure()
|
||||
break
|
||||
action = action_class(subtask, self.orchestrator,
|
||||
self.state_manager)
|
||||
subtask_futures[subtask.get_id().bytes] = e.submit(
|
||||
action.start)
|
||||
|
||||
timeout = config.config_mgr.conf.timeouts.drydock_timeout
|
||||
finished, running = concurrent.futures.wait(
|
||||
subtask_futures.values(), timeout=(timeout * 60))
|
||||
|
||||
for t, f in subtask_futures.items():
|
||||
if not f.done():
|
||||
task.add_status_msg(
|
||||
"Subtask %s timed out before completing.",
|
||||
error=True,
|
||||
ctx=str(uuid.UUID(bytes=t)),
|
||||
ctx_type='task')
|
||||
task.failure()
|
||||
else:
|
||||
if f.exception():
|
||||
self.logger.error(
|
||||
"Uncaught exception in subtask %s" % str(
|
||||
uuid.UUID(bytes=t)),
|
||||
exc_info=f.exception())
|
||||
task.align_result()
|
||||
task.bubble_results()
|
||||
task.set_status(hd_fields.TaskStatus.Complete)
|
||||
task.save()
|
||||
|
||||
return
|
||||
|
||||
|
||||
class LibvirtActionRunner(generic_driver.DriverActionRunner):
|
||||
"""Threaded runner for a Libvirt Action."""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.logger = logging.getLogger(
|
||||
config.config_mgr.conf.logging.oobdriver_logger_name)
|
||||
|
||||
|
||||
def list_opts():
|
||||
return {LibvirtDriver.driver_key: LibvirtDriver.libvirt_driver_options}
|
9
hostdeps.sh
Executable file
9
hostdeps.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
# Install host-level package dependencies
|
||||
# needed for local testing
|
||||
if [[ ! -z $(uname -a | grep Ubuntu) ]]
|
||||
then
|
||||
apt install -y --no-install-recommends $(grep -v '^#' requirements-host.txt)
|
||||
else
|
||||
echo "Only support testing on Ubuntu hosts at this time."
|
||||
fi
|
@ -13,13 +13,15 @@
|
||||
# limitations under the License.
|
||||
FROM python:3.5
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
ENV container docker
|
||||
ENV PORT 9000
|
||||
ENV LC_ALL C.UTF-8
|
||||
ENV LANG C.UTF-8
|
||||
|
||||
# Copy direct dependency requirements only to build a dependency layer
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt update && \
|
||||
apt install -y libvirt-dev --no-install-recommends
|
||||
|
||||
COPY ./requirements-lock.txt /tmp/drydock/
|
||||
RUN pip3 install \
|
||||
--no-cache-dir \
|
||||
|
@ -22,3 +22,4 @@ jsonschema==2.6.0
|
||||
jinja2==2.9.6
|
||||
ulid2==0.1.1
|
||||
defusedxml===0.5.0
|
||||
libvirt-python==3.10.0
|
||||
|
8
requirements-host.txt
Normal file
8
requirements-host.txt
Normal file
@ -0,0 +1,8 @@
|
||||
# These are host packages needed for Drydock
|
||||
# that don't come on a minimal Ubuntu install
|
||||
libvirt-dev
|
||||
pkg-config
|
||||
python3-dev
|
||||
python-tox
|
||||
docker.io
|
||||
gcc
|
@ -20,6 +20,7 @@ jsonschema==2.6.0
|
||||
keystoneauth1==2.13.0
|
||||
keystonemiddleware==4.9.1
|
||||
kombu==4.1.0
|
||||
libvirt-python==3.10.0
|
||||
Mako==1.0.7
|
||||
MarkupSafe==1.0
|
||||
monotonic==1.5
|
||||
|
69
tests/unit/test_libvirt_driver.py
Normal file
69
tests/unit/test_libvirt_driver.py
Normal file
@ -0,0 +1,69 @@
|
||||
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import logging
|
||||
|
||||
import libvirt
|
||||
import pytest
|
||||
|
||||
from drydock_provisioner.error import DriverError
|
||||
from drydock_provisioner.drivers.oob.libvirt_driver.actions.oob import LibvirtBaseAction
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestLibvirtOobDriver():
|
||||
def test_libvirt_init_session(self, mocker, deckhand_orchestrator,
|
||||
input_files, setup):
|
||||
"""Test session initialization."""
|
||||
mocker.patch('libvirt.open')
|
||||
input_file = input_files.join("deckhand_fullsite_libvirt.yaml")
|
||||
|
||||
design_ref = "file://%s" % str(input_file)
|
||||
|
||||
design_status, design_data = deckhand_orchestrator.get_effective_site(
|
||||
design_ref)
|
||||
|
||||
action = LibvirtBaseAction(None, None, None)
|
||||
|
||||
# controller01 should have valid libvirt OOB description
|
||||
node = design_data.get_baremetal_node('controller01')
|
||||
|
||||
LOG.debug("%s", str(node.obj_to_simple()))
|
||||
|
||||
action.init_session(node)
|
||||
|
||||
expected_calls = [mocker.call('qemu+ssh://dummy@somehost/system')]
|
||||
|
||||
libvirt.open.assert_has_calls(expected_calls)
|
||||
|
||||
def test_libvirt_invalid_uri(self, mocker, deckhand_orchestrator,
|
||||
input_files, setup):
|
||||
"""Test session initialization."""
|
||||
mocker.patch('libvirt.open')
|
||||
input_file = input_files.join("deckhand_fullsite_libvirt.yaml")
|
||||
|
||||
design_ref = "file://%s" % str(input_file)
|
||||
|
||||
design_status, design_data = deckhand_orchestrator.get_effective_site(
|
||||
design_ref)
|
||||
|
||||
action = LibvirtBaseAction(None, None, None)
|
||||
|
||||
# compute01 should have invalid libvirt OOB description
|
||||
node = design_data.get_baremetal_node('compute01')
|
||||
|
||||
LOG.debug("%s", str(node.obj_to_simple()))
|
||||
|
||||
with pytest.raises(DriverError):
|
||||
action.init_session(node)
|
424
tests/yaml_samples/deckhand_fullsite_libvirt.yaml
Normal file
424
tests/yaml_samples/deckhand_fullsite_libvirt.yaml
Normal file
@ -0,0 +1,424 @@
|
||||
#Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
####################
|
||||
#
|
||||
# deckhand_fullsite_libvirt.yaml - Full site definition in Deckhand format for libvirt based nodes
|
||||
#
|
||||
####################
|
||||
---
|
||||
schema: 'drydock/Region/v1'
|
||||
metadata:
|
||||
schema: 'metadata/Document/v1'
|
||||
name: 'sitename'
|
||||
storagePolicy: 'cleartext'
|
||||
labels:
|
||||
application: 'drydock'
|
||||
data:
|
||||
tag_definitions:
|
||||
- tag: 'test'
|
||||
definition_type: 'lshw_xpath'
|
||||
definition: "//node[@id=\"display\"]/'clock units=\"Hz\"' > 1000000000"
|
||||
authorized_keys:
|
||||
- |
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDENeyO5hLPbLLQRZ0oafTYWs1ieo5Q+XgyZQs51Ju
|
||||
jDGc8lKlWsg1/6yei2JewKMgcwG2Buu1eqU92Xn1SvMZLyt9GZURuBkyjcfVc/8GiU5QP1Of8B7CV0c
|
||||
kfUpHWYJ17olTzT61Hgz10ioicBF6cjgQrLNcyn05xoaJHD2Vpf8Unxzi0YzA2e77yRqBo9jJVRaX2q
|
||||
wUJuZrzb62x3zw8Knz6GGSZBn8xRKLaw1SKFpd1hwvL62GfqX5ZBAT1AYTZP1j8GcAoK8AFVn193SEU
|
||||
vjSdUFa+RNWuJhkjBRfylJczIjTIFb5ls0jpbA3bMA9DE7lFKVQl6vVwFmiIVBI1 samplekey
|
||||
---
|
||||
schema: 'drydock/NetworkLink/v1'
|
||||
metadata:
|
||||
schema: 'metadata/Document/v1'
|
||||
name: pxe
|
||||
storagePolicy: 'cleartext'
|
||||
labels:
|
||||
application: 'drydock'
|
||||
data:
|
||||
bonding:
|
||||
mode: disabled
|
||||
mtu: 1500
|
||||
linkspeed: auto
|
||||
trunking:
|
||||
mode: disabled
|
||||
default_network: pxe
|
||||
allowed_networks:
|
||||
- pxe
|
||||
---
|
||||
schema: 'drydock/NetworkLink/v1'
|
||||
metadata:
|
||||
schema: 'metadata/Document/v1'
|
||||
name: gp
|
||||
storagePolicy: 'cleartext'
|
||||
labels:
|
||||
application: 'drydock'
|
||||
data:
|
||||
bonding:
|
||||
mode: 802.3ad
|
||||
hash: layer3+4
|
||||
peer_rate: slow
|
||||
mtu: 9000
|
||||
linkspeed: auto
|
||||
trunking:
|
||||
mode: 802.1q
|
||||
default_network: mgmt
|
||||
allowed_networks:
|
||||
- public
|
||||
- private
|
||||
- mgmt
|
||||
---
|
||||
schema: 'drydock/Rack/v1'
|
||||
metadata:
|
||||
schema: 'metadata/Document/v1'
|
||||
name: rack1
|
||||
storagePolicy: 'cleartext'
|
||||
labels:
|
||||
application: 'drydock'
|
||||
data:
|
||||
tor_switches:
|
||||
switch01name:
|
||||
mgmt_ip: 1.1.1.1
|
||||
sdn_api_uri: polo+https://polo-api.web.att.com/switchmgmt?switch=switch01name
|
||||
switch02name:
|
||||
mgmt_ip: 1.1.1.2
|
||||
sdn_api_uri: polo+https://polo-api.web.att.com/switchmgmt?switch=switch02name
|
||||
location:
|
||||
clli: HSTNTXMOCG0
|
||||
grid: EG12
|
||||
local_networks:
|
||||
- pxe-rack1
|
||||
---
|
||||
schema: 'drydock/Network/v1'
|
||||
metadata:
|
||||
schema: 'metadata/Document/v1'
|
||||
name: pxe
|
||||
storagePolicy: 'cleartext'
|
||||
labels:
|
||||
application: 'drydock'
|
||||
data:
|
||||
dhcp_relay:
|
||||
self_ip: 172.16.0.4
|
||||
upstream_target: 172.16.5.5
|
||||
mtu: 1500
|
||||
cidr: 172.16.0.0/24
|
||||
ranges:
|
||||
- type: dhcp
|
||||
start: 172.16.0.5
|
||||
end: 172.16.0.254
|
||||
dns:
|
||||
domain: admin.sitename.att.com
|
||||
servers: 172.16.0.10
|
||||
---
|
||||
schema: 'drydock/Network/v1'
|
||||
metadata:
|
||||
schema: 'metadata/Document/v1'
|
||||
name: mgmt
|
||||
storagePolicy: 'cleartext'
|
||||
labels:
|
||||
application: 'drydock'
|
||||
data:
|
||||
vlan: '100'
|
||||
mtu: 1500
|
||||
cidr: 172.16.1.0/24
|
||||
ranges:
|
||||
- type: static
|
||||
start: 172.16.1.15
|
||||
end: 172.16.1.254
|
||||
routes:
|
||||
- subnet: 0.0.0.0/0
|
||||
gateway: 172.16.1.1
|
||||
metric: 10
|
||||
dns:
|
||||
domain: mgmt.sitename.example.com
|
||||
servers: 172.16.1.9,172.16.1.10
|
||||
---
|
||||
schema: 'drydock/Network/v1'
|
||||
metadata:
|
||||
schema: 'metadata/Document/v1'
|
||||
name: private
|
||||
storagePolicy: 'cleartext'
|
||||
labels:
|
||||
application: 'drydock'
|
||||
data:
|
||||
vlan: '101'
|
||||
mtu: 9000
|
||||
cidr: 172.16.2.0/24
|
||||
ranges:
|
||||
- type: static
|
||||
start: 172.16.2.15
|
||||
end: 172.16.2.254
|
||||
dns:
|
||||
domain: priv.sitename.example.com
|
||||
servers: 172.16.2.9,172.16.2.10
|
||||
---
|
||||
schema: 'drydock/Network/v1'
|
||||
metadata:
|
||||
schema: 'metadata/Document/v1'
|
||||
name: public
|
||||
storagePolicy: 'cleartext'
|
||||
labels:
|
||||
application: 'drydock'
|
||||
data:
|
||||
vlan: '102'
|
||||
mtu: 1500
|
||||
cidr: 172.16.3.0/24
|
||||
ranges:
|
||||
- type: static
|
||||
start: 172.16.3.15
|
||||
end: 172.16.3.254
|
||||
routes:
|
||||
- subnet: 0.0.0.0/0
|
||||
gateway: 172.16.3.1
|
||||
metric: 10
|
||||
dns:
|
||||
domain: sitename.example.com
|
||||
servers: 8.8.8.8
|
||||
---
|
||||
schema: 'drydock/HostProfile/v1'
|
||||
metadata:
|
||||
schema: 'metadata/Document/v1'
|
||||
name: defaults
|
||||
storagePolicy: 'cleartext'
|
||||
labels:
|
||||
application: 'drydock'
|
||||
data:
|
||||
oob:
|
||||
type: libvirt
|
||||
libvirt_uri: qemu+ssh://dummy@somehost/system
|
||||
storage:
|
||||
physical_devices:
|
||||
sda:
|
||||
labels:
|
||||
role: rootdisk
|
||||
partitions:
|
||||
- name: root
|
||||
size: 20g
|
||||
bootable: true
|
||||
filesystem:
|
||||
mountpoint: '/'
|
||||
fstype: 'ext4'
|
||||
mount_options: 'defaults'
|
||||
- name: boot
|
||||
size: 1g
|
||||
bootable: false
|
||||
filesystem:
|
||||
mountpoint: '/boot'
|
||||
fstype: 'ext4'
|
||||
mount_options: 'defaults'
|
||||
sdb:
|
||||
volume_group: 'log_vg'
|
||||
volume_groups:
|
||||
log_vg:
|
||||
logical_volumes:
|
||||
- name: 'log_lv'
|
||||
size: '500m'
|
||||
filesystem:
|
||||
mountpoint: '/var/log'
|
||||
fstype: 'xfs'
|
||||
mount_options: 'defaults'
|
||||
hardware_profile: HPGen9v3
|
||||
primary_network: mgmt
|
||||
interfaces:
|
||||
pxe:
|
||||
device_link: pxe
|
||||
labels:
|
||||
noconfig: true
|
||||
slaves:
|
||||
- prim_nic01
|
||||
networks:
|
||||
- pxe
|
||||
bond0:
|
||||
device_link: gp
|
||||
slaves:
|
||||
- prim_nic01
|
||||
- prim_nic02
|
||||
networks:
|
||||
- mgmt
|
||||
- private
|
||||
sriov:
|
||||
vf_count: 2
|
||||
trustedmode: false
|
||||
platform:
|
||||
image: 'xenial'
|
||||
kernel: 'ga-16.04'
|
||||
kernel_params:
|
||||
quiet: true
|
||||
console: ttyS2
|
||||
metadata:
|
||||
owner_data:
|
||||
foo: bar
|
||||
---
|
||||
schema: 'drydock/BaremetalNode/v1'
|
||||
metadata:
|
||||
schema: 'metadata/Document/v1'
|
||||
name: controller01
|
||||
storagePolicy: 'cleartext'
|
||||
labels:
|
||||
application: 'drydock'
|
||||
data:
|
||||
host_profile: defaults
|
||||
addressing:
|
||||
- network: pxe
|
||||
address: dhcp
|
||||
- network: mgmt
|
||||
address: 172.16.1.20
|
||||
- network: public
|
||||
address: 172.16.3.20
|
||||
metadata:
|
||||
rack: rack1
|
||||
---
|
||||
schema: 'drydock/BaremetalNode/v1'
|
||||
metadata:
|
||||
schema: 'metadata/Document/v1'
|
||||
name: compute01
|
||||
storagePolicy: 'cleartext'
|
||||
labels:
|
||||
application: 'drydock'
|
||||
data:
|
||||
host_profile: defaults
|
||||
oob:
|
||||
type: libvirt
|
||||
# This is an invalid libvirt uri, use it for testing
|
||||
# sanity checks
|
||||
libvirt_uri: http://dummy@somehost/system
|
||||
addressing:
|
||||
- network: pxe
|
||||
address: dhcp
|
||||
- network: mgmt
|
||||
address: 172.16.1.21
|
||||
- network: private
|
||||
address: 172.16.2.21
|
||||
- network: oob
|
||||
address: 172.16.100.21
|
||||
platform:
|
||||
kernel_params:
|
||||
isolcpus: hardwareprofile:cpuset.sriov
|
||||
hugepagesz: hardwareprofile:hugepages.sriov.size
|
||||
hugepages: hardwareprofile:hugepages.sriov.count
|
||||
metadata:
|
||||
rack: rack2
|
||||
---
|
||||
schema: 'drydock/HardwareProfile/v1'
|
||||
metadata:
|
||||
schema: 'metadata/Document/v1'
|
||||
name: HPGen9v3
|
||||
storagePolicy: 'cleartext'
|
||||
labels:
|
||||
application: 'drydock'
|
||||
data:
|
||||
vendor: HP
|
||||
generation: '8'
|
||||
hw_version: '3'
|
||||
bios_version: '2.2.3'
|
||||
boot_mode: bios
|
||||
bootstrap_protocol: pxe
|
||||
pxe_interface: 0
|
||||
device_aliases:
|
||||
prim_nic01:
|
||||
address: '0000:00:03.0'
|
||||
dev_type: '82540EM Gigabit Ethernet Controller'
|
||||
bus_type: 'pci'
|
||||
prim_nic02:
|
||||
address: '0000:00:04.0'
|
||||
dev_type: '82540EM Gigabit Ethernet Controller'
|
||||
bus_type: 'pci'
|
||||
primary_boot:
|
||||
address: '2:0.0.0'
|
||||
dev_type: 'VBOX HARDDISK'
|
||||
bus_type: 'scsi'
|
||||
cpu_sets:
|
||||
sriov: '2,4'
|
||||
hugepages:
|
||||
sriov:
|
||||
size: '1G'
|
||||
count: 300
|
||||
dpdk:
|
||||
size: '2M'
|
||||
count: 530000
|
||||
---
|
||||
schema: 'drydock/BootAction/v1'
|
||||
metadata:
|
||||
schema: 'metadata/Document/v1'
|
||||
name: hw_filtered
|
||||
storagePolicy: 'cleartext'
|
||||
labels:
|
||||
application: 'drydock'
|
||||
data:
|
||||
signaling: false
|
||||
node_filter:
|
||||
filter_set_type: 'union'
|
||||
filter_set:
|
||||
- filter_type: 'union'
|
||||
node_names:
|
||||
- 'compute01'
|
||||
assets:
|
||||
- path: /var/tmp/hello.sh
|
||||
type: file
|
||||
permissions: '555'
|
||||
data: |-
|
||||
IyEvYmluL2Jhc2gKCmVjaG8gJ0hlbGxvIFdvcmxkISAtZnJvbSB7eyBub2RlLmhvc3RuYW1lIH19
|
||||
Jwo=
|
||||
data_pipeline:
|
||||
- base64_decode
|
||||
- utf8_decode
|
||||
- template
|
||||
- path: /lib/systemd/system/hello.service
|
||||
type: unit
|
||||
permissions: '600'
|
||||
data: |-
|
||||
W1VuaXRdCkRlc2NyaXB0aW9uPUhlbGxvIFdvcmxkCgpbU2VydmljZV0KVHlwZT1vbmVzaG90CkV4
|
||||
ZWNTdGFydD0vdmFyL3RtcC9oZWxsby5zaAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIu
|
||||
dGFyZ2V0Cg==
|
||||
data_pipeline:
|
||||
- base64_decode
|
||||
- utf8_decode
|
||||
...
|
||||
---
|
||||
schema: 'drydock/BootAction/v1'
|
||||
metadata:
|
||||
schema: 'metadata/Document/v1'
|
||||
name: helloworld
|
||||
storagePolicy: 'cleartext'
|
||||
labels:
|
||||
application: 'drydock'
|
||||
data:
|
||||
assets:
|
||||
- path: /var/tmp/hello.sh
|
||||
type: file
|
||||
permissions: '555'
|
||||
data: |-
|
||||
IyEvYmluL2Jhc2gKCmVjaG8gJ0hlbGxvIFdvcmxkISAtZnJvbSB7eyBub2RlLmhvc3RuYW1lIH19
|
||||
Jwo=
|
||||
data_pipeline:
|
||||
- base64_decode
|
||||
- utf8_decode
|
||||
- template
|
||||
- path: /lib/systemd/system/hello.service
|
||||
type: unit
|
||||
permissions: '600'
|
||||
data: |-
|
||||
W1VuaXRdCkRlc2NyaXB0aW9uPUhlbGxvIFdvcmxkCgpbU2VydmljZV0KVHlwZT1vbmVzaG90CkV4
|
||||
ZWNTdGFydD0vdmFyL3RtcC9oZWxsby5zaAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIu
|
||||
dGFyZ2V0Cg==
|
||||
data_pipeline:
|
||||
- base64_decode
|
||||
- utf8_decode
|
||||
- path: /var/tmp/designref.sh
|
||||
type: file
|
||||
permissions: '500'
|
||||
data: e3sgYWN0aW9uLmRlc2lnbl9yZWYgfX0K
|
||||
data_pipeline:
|
||||
- base64_decode
|
||||
- utf8_decode
|
||||
- template
|
||||
...
|
43
tools/helm_install.sh
Executable file
43
tools/helm_install.sh
Executable file
@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
set -x
|
||||
|
||||
HELM=$1
|
||||
HELM_ARTIFACT_URL=${HELM_ARTIFACT_URL:-"https://storage.googleapis.com/kubernetes-helm/helm-v2.7.2-linux-amd64.tar.gz"}
|
||||
|
||||
|
||||
function install_helm_binary {
|
||||
if [[ -z "${HELM}" ]]
|
||||
then
|
||||
echo "No Helm binary target location."
|
||||
exit -1
|
||||
fi
|
||||
|
||||
if [[ -w "$(dirname ${HELM})" ]]
|
||||
then
|
||||
TMP_DIR=${BUILD_DIR:-$(mktemp -d)}
|
||||
curl -o "${TMP_DIR}/helm.tar.gz" "${HELM_ARTIFACT_URL}"
|
||||
cd ${TMP_DIR}
|
||||
tar -xvzf helm.tar.gz
|
||||
cp "${TMP_DIR}/linux-amd64/helm" "${HELM}"
|
||||
else
|
||||
echo "Cannot write to ${HELM}"
|
||||
exit -1
|
||||
fi
|
||||
}
|
||||
|
||||
install_helm_binary
|
@ -18,6 +18,7 @@
|
||||
HELM=$1
|
||||
HTK_REPO=${HTK_REPO:-"https://github.com/openstack/openstack-helm"}
|
||||
HTK_PATH=${HTK_PATH:-""}
|
||||
HTK_STABLE_COMMIT=${HTK_COMMIT:-"f902cd14fac7de4c4c9f7d019191268a6b4e9601"}
|
||||
DEP_UP_LIST=${DEP_UP_LIST:-"drydock"}
|
||||
|
||||
if [[ ! -z $(echo $http_proxy) ]]
|
||||
@ -52,10 +53,10 @@ function helm_serve {
|
||||
|
||||
mkdir -p build
|
||||
pushd build
|
||||
git clone --depth 1 $HTK_REPO || true
|
||||
git clone $HTK_REPO || true
|
||||
pushd openstack-helm/$HTK_PATH
|
||||
git reset --hard "${HTK_STABLE_COMMIT}"
|
||||
|
||||
git pull
|
||||
helm_serve
|
||||
make helm-toolkit
|
||||
popd && popd
|
||||
|
Loading…
Reference in New Issue
Block a user