#!/usr/bin/env python # # Copyright 2014 Huawei Technologies Co. Ltd # # 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. """binary to deploy a cluster by compass client api.""" import os import re import socket import sys import time import yaml import netaddr import requests import json import itertools from collections import defaultdict from restful import Client ROLE_UNASSIGNED = True ROLE_ASSIGNED = False import log as logging LOG = logging.getLogger(__name__) from oslo_config import cfg CONF = cfg.CONF def byteify(input): if isinstance(input, dict): return dict([(byteify(key),byteify(value)) for key,value in input.iteritems()]) elif isinstance(input, list): return [byteify(element) for element in input] elif isinstance(input, unicode): return input.encode('utf-8') else: return input opts = [ cfg.StrOpt('compass_server', help='compass server url', default='http://127.0.0.1/api'), cfg.StrOpt('compass_user_email', help='compass user email', default='admin@huawei.com'), cfg.StrOpt('compass_user_password', help='compass user password', default='admin'), cfg.StrOpt('switch_ips', help='comma seperated switch ips', default=''), cfg.StrOpt('switch_credential', help='comma separated =', default='version=2c,community=public'), cfg.IntOpt('switch_max_retries', help='max retries of poll switch', default=10), cfg.IntOpt('switch_retry_interval', help='interval to repoll switch', default=10), cfg.BoolOpt('poll_switches', help='if the client polls switches', default=True), cfg.StrOpt('machines', help='comma separated mac addresses of machines', default=''), cfg.StrOpt('subnets', help='comma seperated subnets', default=''), cfg.StrOpt('adapter_name', help='adapter name', default=''), cfg.StrOpt('adapter_os_pattern', help='adapter os name', default=r'^(?i)centos.*'), cfg.StrOpt('adapter_target_system_pattern', help='adapter target system name', default='^openstack$'), cfg.StrOpt('adapter_flavor_pattern', help='adapter flavor name', default='allinone'), cfg.StrOpt('cluster_name', help='cluster name', default='cluster1'), cfg.StrOpt('language', help='language', default='EN'), cfg.StrOpt('timezone', help='timezone', default='GMT'), cfg.StrOpt('http_proxy', help='http proxy', default=''), cfg.StrOpt('https_proxy', help='https proxy', default=''), cfg.StrOpt('no_proxy', help='no proxy', default=''), cfg.StrOpt('ntp_server', help='ntp server', default=''), cfg.StrOpt('dns_servers', help='dns servers', default=''), cfg.StrOpt('domain', help='domain', default=''), cfg.StrOpt('search_path', help='search path', default=''), cfg.StrOpt('local_repo_url', help='local repo url', default=''), cfg.StrOpt('default_gateway', help='default gateway', default=''), cfg.StrOpt('server_credential', help=( 'server credential formatted as ' '=' ), default='root=root'), cfg.StrOpt('os_config_json_file', help='json formatted os config file', default=''), cfg.StrOpt('service_credentials', help=( 'comma seperated service credentials formatted as ' ':=,...' ), default=''), cfg.StrOpt('console_credentials', help=( 'comma seperated console credential formated as ' ':=' ), default=''), cfg.StrOpt('hostnames', help='comma seperated hostname', default=''), cfg.StrOpt('host_networks', help=( 'semicomma seperated host name and its networks ' ':=||,...' ), default=''), cfg.StrOpt('partitions', help=( 'comma seperated partitions ' '=' ), default='tmp:percentage=10%,var:percentage=30%,home:percentage=30%'), cfg.StrOpt('network_mapping', help=( 'comma seperated network mapping ' '=' ), default=''), cfg.StrOpt('package_config_json_file', help='json formatted os config file', default=''), cfg.StrOpt('host_roles', help=( 'semicomma separated host roles ' '=' ), default=''), cfg.StrOpt('default_roles', help=( 'comma seperated default roles ' '' ), default=''), cfg.IntOpt('action_timeout', help='action timeout in seconds', default=60), cfg.IntOpt('deployment_timeout', help='deployment timeout in minutes', default=60), cfg.IntOpt('progress_update_check_interval', help='progress update status check interval in seconds', default=60), cfg.StrOpt('dashboard_url', help='dashboard url', default=''), cfg.StrOpt('dashboard_link_pattern', help='dashboard link pattern', default=r'(?m)(http://\d+\.\d+\.\d+\.\d+:5000/v2\.0)'), cfg.StrOpt('cluster_vip', help='cluster ip address', default=''), cfg.StrOpt('enable_secgroup', help='enable security group', default='true'), cfg.StrOpt('enable_vpnaas', help='enable vpn as service', default='true'), cfg.StrOpt('enable_fwaas', help='enable firewall as service', default='true'), cfg.StrOpt('network_cfg', help='netowrk config file',
##############################################################################
# Copyright (c) 2018 Mirantis Inc., Enea AB and others.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
---
classes:
  - system.glusterfs.client.cluster
  - system.nova.compute.cluster
  - system.nova.compute.nfv.hugepages
  - system.neutron.gateway.cluster
  - system.cinder.volume.single
  - system.cinder.volume.backend.lvm
  - system.ceilometer.agent.cluster
  - system.ceilometer.agent.polling.default
  - service.barbican.client.cluster
  - cluster.all-mcp-arch-common.backports
  - cluster.mcp-common-ha.glusterfs_repo
  - cluster.mcp-common-ha.openstack_compute_pdf
  - cluster.all-mcp-arch-common.opnfv.maas_proxy
  - cluster.all-mcp-arch-common.opnfv.lab_proxy_pdf
parameters:
  _param:
    cluster_vip_address: ${_param:openstack_control_address}
    cluster_local_address: ${_param:single_address}
    cluster_node01_hostname: ${_param:openstack_control_node01_hostname}
    cluster_node01_address: ${_param:openstack_control_node01_address}
    cluster_node02_hostname: ${_param:openstack_control_node02_hostname}
    cluster_node02_address: ${_param:openstack_control_node02_address}
    cluster_node03_hostname: ${_param:openstack_control_node03_hostname}
    cluster_node03_address: ${_param:openstack_control_node03_address}
    nova_vncproxy_url: https://${_param:cluster_public_host}:6080
    keepalived_vip_interface: br-ctl
    keepalived_vip_virtual_router_id: 69
    linux_system_codename: xenial
  glusterfs:
    client:
      volumes:
        nova_instances:
          path: /var/lib/nova/instances
          server: ${_param:glusterfs_service_host}
          # yamllint disable-line rule:line-length
          opts: "defaults,backup-volfile-servers=${_param:cluster_node01_address}:${_param:cluster_node02_address}:${_param:cluster_node03_address}"
  cinder:
    volume:
      my_ip: ${_param:single_address}
      backend:
        lvm-driver:
          # Align system.cinder.volume.backend.lvm and MaaS data
          volume_group: ${linux:storage:lvm:cinder-vg:name}
      database:
        connection_recycle_time: ${_param:db_connection_recycle_time}
      barbican:
        enabled: ${_param:barbican_integration_enabled}
  linux:
    storage:
      lvm:
        # Align with both system.cinder.volume.backend.lvm and MaaS data
        cinder-vg:
          name: vgroot
    system:
      kernel:
        sysctl:
          vm.dirty_ratio: 10
          vm.dirty_background_ratio: 5
        boot_options:
          - spectre_v2=off
          - nopti
          - nospec_store_bypass_disable
          - noibrs
          - noibpb
  neutron:
    gateway:
      vlan_aware_vms: true
      root_helper_daemon: false
      dhcp_lease_duration: 3600
      report_interval: 120
  nova:
    compute:
      disk_cachemodes: file=directsync,block=none
      preallocate_images: space
      heal_instance_info_cache_interval: 300
      barbican:
        enabled: ${_param:barbican_integration_enabled}
      image:
        verify_glance_signatures: false
host_ids = self.host_mapping.values() status, response = self.client.review_cluster( cluster_id, review={'hosts': host_ids} ) LOG.info( 'review cluster %s hosts %s, status %s: %s', cluster_id, host_ids, status, response ) #TODO, what this doning? if not self.is_ok(status): raise RuntimeError("review cluster host failed") status, response = self.client.deploy_cluster( cluster_id, deploy={'hosts': host_ids} ) LOG.info( 'deploy cluster %s hosts %s status %s: %s', cluster_id, host_ids, status, response ) if not self.is_ok(status): raise RuntimeError("deploy cluster failed") def redeploy_clusters(self, cluster_id): status, response = self.client.redeploy_cluster( cluster_id ) if not self.is_ok(status): LOG.info( 'deploy cluster %s status %s: %s', cluster_id, status, response ) raise RuntimeError("redeploy cluster failed") def get_installing_progress(self, cluster_id): """get intalling progress.""" action_timeout = time.time() + 60 * float(CONF.action_timeout) deployment_timeout = time.time() + 60 * float( CONF.deployment_timeout) current_time = time.time() deployment_failed = True while current_time < deployment_timeout: status, cluster_state = self.client.get_cluster_state(cluster_id) LOG.info( 'get cluster %s state status %s: %s', cluster_id, status, cluster_state ) if not self.is_ok(status): raise RuntimeError("can not get cluster state") if cluster_state['state'] in ['UNINITIALIZED', 'INITIALIZED']: if current_time >= action_timeout: deployment_failed = True break else: continue elif cluster_state['state'] == 'SUCCESSFUL': deployment_failed = False break elif cluster_state['state'] == 'ERROR': deployment_failed = True break if deployment_failed: raise RuntimeError("deploy cluster failed") def check_dashboard_links(self, cluster_id): dashboard_url = CONF.dashboard_url if not dashboard_url: LOG.info('no dashboarde url set') return dashboard_link_pattern = re.compile( CONF.dashboard_link_pattern) r = requests.get(dashboard_url, verify=False) r.raise_for_status() match = dashboard_link_pattern.search(r.text) if match: LOG.info( 'dashboard login page for cluster %s can be downloaded', cluster_id) else: msg = ( '%s failed to be downloaded\n' 'the context is:\n%s\n' ) % (dashboard_url, r.text) raise Exception(msg) def deploy(): client = CompassClient() machines = client.get_machines() LOG.info('machines are %s', machines) client.add_subnets() adapter_id, os_id, flavor_id = client.get_adapter() cluster_id = client.add_cluster(adapter_id, os_id, flavor_id) client.add_cluster_hosts(cluster_id, machines) client.set_host_networking() client.set_cluster_os_config(cluster_id) if flavor_id: client.set_cluster_package_config(cluster_id) client.set_all_hosts_roles(cluster_id) client.deploy_clusters(cluster_id) client.get_installing_progress(cluster_id) client.check_dashboard_links(cluster_id) def redeploy(): client = CompassClient() cluster_id = client.list_clusters() client.redeploy_clusters(cluster_id) client.get_installing_progress(cluster_id) client.check_dashboard_links(cluster_id) def main(): if CONF.deploy_flag == "redeploy": redeploy() else: deploy() if __name__ == "__main__": CONF(args=sys.argv[1:]) main()