dlcClient.ts 6.44 KB
Newer Older
1
2
3
4
5
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { Deferred } from 'ts-deferred';
import { PythonShell } from 'python-shell';
6
import { getLogger, Logger } from 'common/log';
7
8
9
10
11
12
13
14
15

export class DlcClient {
    private log: Logger;
    public type: string;
    public image: string;
    public jobType: string;
    public podCount: number;
    public ecsSpec: string;
    public region: string;
16
    public workspaceId: string;
17
18
19
    // e.g., data1e6vg1tu0zi7, to generate it, go to 'Dataset Config' page of DLC
    //       create a NAS data and copy the 'DataSet ConfigurationID'
    public nasDataSourceId: string;
20
    public ossDataSourceId: string;
21
22
23
24
25
    public accessKeyId: string;
    public accessKeySecret: string;
    public experimentId: string;
    public environmentId: string;
    public userCommand: string;
26
27
    // dlcUtil exception log dir
    public logDir: string;
28
    public pythonShellClient: undefined | PythonShell;
29
    public status: string;
30
31
32
33
34
35
36
37
38
39

    constructor(
        type: string,
        image: string,
        jobType: string,
        podCount: number,
        experimentId: string,
        environmentId: string,
        ecsSpec: string,
        region: string,
40
        workspaceId: string,
41
42
43
44
        nasDataSourceId: string,
        accessKeyId: string,
        accessKeySecret: string,
        userCommand: string,
45
46
        logDir: string,
        ossDataSourceId?: string,
47
48
49
50
51
52
53
54
55
        ) {
        this.log = getLogger('DlcClient');
        this.type = type;
        this.image = image;
        this.jobType = jobType;
        this.podCount = podCount;
        this.ecsSpec = ecsSpec;
        this.image = image;
        this.region = region;
56
        this.workspaceId = workspaceId;
57
        this.nasDataSourceId = nasDataSourceId;
58
59
60
61
62
        if (ossDataSourceId !== undefined) {
            this.ossDataSourceId = ossDataSourceId;
        } else {
            this.ossDataSourceId = '';
        }
63
64
65
66
67
        this.accessKeyId = accessKeyId;
        this.accessKeySecret = accessKeySecret
        this.experimentId = experimentId;
        this.environmentId = environmentId;
        this.userCommand = userCommand;
68
        this.logDir = logDir;
69
        this.status = '';
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
    }

    public submit(): Promise<string> {
        const deferred: Deferred<string> = new Deferred<string>();
        this.pythonShellClient = new PythonShell('dlcUtil.py', {
            scriptPath: './config/dlc',
            pythonPath: 'python3',
            pythonOptions: ['-u'], // get print results in real-time
            args: [
                '--type', this.type,
                '--image', this.image,
                '--job_type', this.jobType,
                '--pod_count', String(this.podCount),
                '--ecs_spec', this.ecsSpec,
                '--region', this.region,
85
                '--workspace_id', this.workspaceId,
86
                '--nas_data_source_id', this.nasDataSourceId,
87
                '--oss_data_source_id', this.ossDataSourceId,
88
89
90
91
                '--access_key_id', this.accessKeyId,
                '--access_key_secret', this.accessKeySecret,
                '--experiment_name', `nni_exp_${this.experimentId}_env_${this.environmentId}`,
                '--user_command', this.userCommand,
92
                '--log_dir', this.logDir,
93
94
95
              ]
        });
        this.log.debug(this.pythonShellClient.command);
96
97
        this.onMessage();
        this.log.debug(`on message`);
98
        this.monitorError(this.pythonShellClient, deferred);
99
100
101
102
103
104
105
106
107
        this.log.debug(`monitor submit`);
        const log = this.log;
        this.pythonShellClient.on('message', (message: any) => {
            const jobid = this.parseContent('job_id', message);
            if (jobid !== '') {
                log.debug(`reslove job_id ${jobid}`);
                deferred.resolve(jobid);
            }
        });
108
109
        return deferred.promise;
    }
110
111
112
113
114
115
116
117
118
119
120
121
122
123
    private onMessage(): void {
        if (this.pythonShellClient === undefined) {
            throw Error('python shell client not initialized!');
        }
        const log = this.log;
        this.pythonShellClient.on('message', (message: any) => {
            const status: string= this.parseContent('status', message);
            if (status.length > 0) {
                log.debug(`on message status: ${status}`)
                this.status = status;
                return;
            }
        });
    }
124
125
    public stop(): void {
        if (this.pythonShellClient === undefined) {
126
            this.log.debug(`python shell client not initialized!`);
127
128
            throw Error('python shell client not initialized!');
        }
129
        this.log.debug(`send stop`);
130
131
132
133
134
135
136
137
        this.pythonShellClient.send('stop');
    }

    public getTrackingUrl(): Promise<string> {
        const deferred: Deferred<string> = new Deferred<string>();
        if (this.pythonShellClient === undefined) {
            throw Error('python shell client not initialized!');
        }
138
        this.log.debug(`send tracking_url`);
139
        this.pythonShellClient.send('tracking_url');
140
141

        const log = this.log;
142
143
144
        this.pythonShellClient.on('message', (status: any) => {
            const trackingUrl = this.parseContent('tracking_url', status);
            if (trackingUrl !== '') {
145
                log.debug(`trackingUrl:${trackingUrl}`);
146
147
148
149
150
151
152
153
154
155
156
157
                deferred.resolve(trackingUrl);
            }
        });
        return deferred.promise;
    }

    public updateStatus(oldStatus: string): Promise<string> {
        const deferred: Deferred<string> = new Deferred<string>();
        if (this.pythonShellClient === undefined) {
            throw Error('python shell client not initialized!');
        }
        this.pythonShellClient.send('update_status');
158
159
        if (this.status === '') {
            this.status = oldStatus;
160
        }
161
162
        this.log.debug(`update_status:${this.status}`);
        deferred.resolve(this.status);
163
164
        return deferred.promise;
    }
165

166
167
    // Monitor error information in dlc python shell client
    private monitorError(pythonShellClient: PythonShell, deferred: Deferred<any>): void {
168
        const log = this.log;
169
        pythonShellClient.on('error', function (error: any) {
170
            log.info(`error:${error}`);
171
172
173
174
175
176
177
178
179
180
181
182
183
            deferred.reject(error);
        });
    }
    
    // Parse command content, command format is {head}:{content}
    public parseContent(head: string, command: string): string {
        const items = command.split(':');
        if (items[0] === head) {
            return command.slice(head.length + 1);
        }
        return '';
    }
}